-
Notifications
You must be signed in to change notification settings - Fork 3
/
Root.gd
206 lines (163 loc) · 7.45 KB
/
Root.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
extends Node2D
const DIM = 255
@onready var map = %Map
@onready var label = %Label
#
var state_uvs : Dictionary;
#Store image so that we can query the color when we move the mouse over it
var look_up_image : Image
func _ready():
# we need an array of states. Where each index corresponds to the color of the state.
# And each value is its id
var states := [];
#Maximum amount of states with this method is ~16 million. Should be enough...
#If more is needed then the alpha channel could be used too.
states.resize(255*255*255)
#the image that will hold the color of the states
var owner_map : Image = Image.create(
DIM,
DIM,
false,
Image.FORMAT_RGBAF
)
if FileAccess.file_exists("res://Assets/Data/states_cache.json"):
var file = FileAccess.open("res://Assets/Data/states_cache.json", FileAccess.READ)
var file_contents = file.get_as_text();
var result : Dictionary = JSON.parse_string(file_contents)
#return false if we fail to parse JSON
if result == null: return;
for color_string in result:
var map_color : Color = get_color_from_8_string(color_string)
#We want to convert the color to its index representation. i.e. convert 3D value to 1D value
#And then set the value to the state ID
states[convert_to_index(map_color)] = result[color_string].id;
#Set the color at the uv coord that corresponds to the id
#little bit hacky for hackathon
var owner_color = get_color_from_8_string(result[color_string].owner_color)
var uv = Vector2(float(int(result[color_string].id) % DIM), floor(result[color_string].id / DIM))
state_uvs[Color(float(int(result[color_string].id) % DIM) / (DIM - 1), floor(result[color_string].id / DIM) / (DIM - 1), 0.0)] = result[color_string].id
owner_map.set_pixel(uv.x, uv.y, owner_color)
else:
#No file so return
return;
var image_texture = ImageTexture.create_from_image(owner_map)
map.material.set_shader_parameter('color_texture', image_texture)
#The image we are going to be working on
if !FileAccess.file_exists("res://Assets/states.bmp"): return false;
var image := preload("res://Assets/states.bmp").get_image();
var image_data := image.get_data()
var image_dimensions := image.get_size()
#We now have the array, so feed it into the compute shader
var result = compute_convert_states(states, image_data, image_dimensions)
look_up_image = create_lookup_image(result, image_dimensions)
map.material.set_shader_parameter('lookup_texture', ImageTexture.create_from_image(look_up_image))
func get_color_from_8_string(string : String) -> Color:
var result := Color();
var strings = string.split_floats(',')
if strings.size() != 3: return result;
result = Color8(int(strings[0]), int(strings[1]), int(strings[2]))
return result
func compute_convert_states(data : PackedInt32Array, image_data : PackedByteArray, dimensions : Vector2i) -> PackedByteArray:
# Create a local rendering device.
var rendering_device := RenderingServer.create_local_rendering_device()
# Load GLSL shader
var shader_file := load("res://ComputeShaders/state_conversion.glsl")
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
var shader := rendering_device.shader_create_from_spirv(shader_spirv)
##################################################################
# STATE ID INPUT
var input := data
var input_bytes := input.to_byte_array()
var buffer := rendering_device.storage_buffer_create(input_bytes.size(), input_bytes)
# Create a uniform to assign the buffer to the rendering device
var uniform := RDUniform.new()
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
uniform.binding = 0 # this needs to match the "binding" in our shader file
uniform.add_id(buffer)
##################################################################
# OUTPUT BUFFER
var output = PackedVector2Array()
output.resize(dimensions.x * dimensions.y)
var output_bytes := output.to_byte_array()
var output_buffer := rendering_device.storage_buffer_create(output_bytes.size(), output_bytes)
# Create a uniform to assign the buffer to the rendering device
var output_uniform := RDUniform.new()
output_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
output_uniform.binding = 1 # this needs to match the "binding" in our shader file
output_uniform.add_id(output_buffer)
##################################################################
# TEXTURE
var format = RDTextureFormat.new()
format.width = dimensions.x #128
format.height = dimensions.y #64
format.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT
format.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM
var v_tex = rendering_device.texture_create(format, RDTextureView.new(), [image_data])
var samp_state = RDSamplerState.new()
samp_state.unnormalized_uvw = true
var samp = rendering_device.sampler_create(samp_state)
var tex_uniform = RDUniform.new()
tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE
tex_uniform.binding = 2
tex_uniform.add_id(samp)
tex_uniform.add_id(v_tex)
#set uniforms
var uniform_set := rendering_device.uniform_set_create(
[
uniform,
output_uniform,
tex_uniform
],
shader,
0
) # the last parameter (the 0) needs to match the "set" in our shader file
var pipeline := rendering_device.compute_pipeline_create(shader)
var compute_list := rendering_device.compute_list_begin()
rendering_device.compute_list_bind_compute_pipeline(compute_list, pipeline)
rendering_device.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
#NOTE: the 2nd and 3rd parameters products with the invocation x and y should be the dimensions of the resulting image
#See note in the shader, this will probably be best to be set through defines.
rendering_device.compute_list_dispatch(compute_list, 320, 160, 1)
rendering_device.compute_list_end()
# Submit to GPU and wait for sync
rendering_device.submit()
rendering_device.sync()
# Read back the data from the buffer
var result := rendering_device.buffer_get_data(output_buffer);
#clean up
rendering_device.free_rid(uniform_set)
rendering_device.free_rid(pipeline)
rendering_device.free_rid(v_tex)
rendering_device.free_rid(buffer)
rendering_device.free_rid(output_buffer)
rendering_device.free_rid(samp)
rendering_device.free_rid(shader)
rendering_device.free()
return result;
func create_lookup_image(data : PackedByteArray, image_dimensions : Vector2i, save_as_png : String = '') -> Image:
var new_image : Image = Image.create_from_data(
image_dimensions.x,
image_dimensions.y,
false,
Image.FORMAT_RGF,
data
);
if save_as_png:
new_image.save_png(save_as_png);
return new_image;
func to_int(x):
return int(floor(x * 254.0))
func convert_to_index(color : Color):
return (to_int(color.b) * 255 * 255) + (to_int(color.g) * 255) + to_int(color.r)
func _on_control_gui_input(event):
if event is InputEventMouseMotion:
var mouse_motion_event = event as InputEventMouseMotion
#assume that the image is not centered
if mouse_motion_event.position.x < map.global_position.x or \
mouse_motion_event.position.y < map.global_position.x or \
mouse_motion_event.position.x / map.global_scale.x > (map.global_position.x + map.get_rect().size.x - 1) or \
mouse_motion_event.position.y / map.global_scale.y > (map.global_position.y + map.get_rect().size.y - 1):
return
var state_id = state_uvs.get(look_up_image.get_pixelv((mouse_motion_event.position - map.global_position) / map.global_scale))
if state_id:
label.set_text(str( state_id))