shader_type canvas_item; // Texture which stores all envelope values used in the map // Integrates time offsets and has static colors already multiplied on top // Updates each frame uniform sampler2D env_values; // Texture which stores the center of the quad around which it rotates, and uv texture coordinates uniform sampler2D center_uvs; // The COLOR vertex attribute is fake and does not store 4 floats // Each color channel is 1 divided by an integer between 0 and 255, as a float // Due to that, we can only store 1 byte per channel, which makes 4 bytes in total // We store the vertex index as a little endian 4 byte integer // Each channel stores a byte, combined by multiplying each byte with the respective factor of 2 // Each factor of 2 can be thought of as a bit-shift, moving each byte into the proper position int color_to_index(vec4 color) { const vec4 factors = vec4(256., 256.*256., 256.*256.*256., 256.*256.*256.*256.); ivec4 index_ = ivec4(color * factors); return index_.r + index_.g + index_.b + index_.a; } void vertex() { // Vertex index int index = color_to_index(COLOR); // Absolute position of quad corner vec2 corner = VERTEX; // X: Color env value index, Y: Position env value index ivec2 env_indices = ivec2(UV); vec4 center_uv = texelFetch(center_uvs, ivec2(index, 0), 0); vec4 pos_env = texelFetch(env_values, ivec2(env_indices.y, 0), 0); // Calculate rotation vec2 offset = corner - center_uv.xy; // Relative position of quad corner to quad center vec2 rotation_sin = vec2(sin(pos_env.z)) * vec2(-offset.y, offset.x); vec2 rotation_cos = vec2(cos(pos_env.z)) * offset; vec2 offset_transformed = rotation_sin + rotation_cos; VERTEX = corner + pos_env.xy + offset_transformed; UV = center_uv.zw; COLOR = texelFetch(env_values, ivec2(env_indices.x, 0), 0); }