diff --git a/src/shaders/sandPhysics.frag b/src/shaders/sandPhysics.frag new file mode 100644 index 0000000..a6df7f9 --- /dev/null +++ b/src/shaders/sandPhysics.frag @@ -0,0 +1,116 @@ +precision highp float; +precision highp int; + +in vec2 vUv; + +uniform sampler2D uWorld; +uniform sampler2D uMaterialProps; +uniform vec2 uBlockOffset; +uniform int uFrame; + +out vec4 fragColor; + +uint hash(uint x) { + x ^= x >> 16u; + x *= 0x45d9f3bu; + x ^= x >> 16u; + return x; +} + +void main() { + ivec2 pixel = ivec2(gl_FragCoord.xy); + ivec2 blockBase = ((pixel - ivec2(uBlockOffset)) / 2) * 2 + ivec2(uBlockOffset); + ivec2 localPos = pixel - blockBase; + int localIndex = localPos.x + localPos.y * 2; + + // bounds check + int worldSize = int(WORLD_SIZE); + if (blockBase.x < 0 || blockBase.y < 0 || + blockBase.x + 1 >= worldSize || blockBase.y + 1 >= worldSize) { + fragColor = texelFetch(uWorld, pixel, 0); + return; + } + + // read 2x2 block + vec4 cells[4]; + float behaviors[4]; + float densities[4]; + for (int i = 0; i < 4; i++) { + ivec2 p = blockBase + ivec2(i % 2, i / 2); + cells[i] = texelFetch(uWorld, p, 0); + vec4 props = texelFetch(uMaterialProps, ivec2(int(cells[i].r), 0), 0); + behaviors[i] = props.r; + densities[i] = props.g; + } + + // capture blocked state before vertical swaps modify the arrays + bool col0Blocked = (behaviors[0] == BEHAVIOR_SOLID || behaviors[2] == BEHAVIOR_SOLID || densities[0] <= densities[2]); + bool col1Blocked = (behaviors[1] == BEHAVIOR_SOLID || behaviors[3] == BEHAVIOR_SOLID || densities[1] <= densities[3]); + + // random seed for this block this frame + uint seed = hash(uint(blockBase.x) * 7919u + uint(blockBase.y) * 6271u + uint(uFrame) * 3571u); + + // try vertical gravity: column 0 (indices 0, 2) and column 1 (indices 1, 3) + // top=0,1 bottom=2,3 + // block layout: + // [0][1] <- top row + // [2][3] <- bottom row + // if top is movable and top.density > bottom.density, swap + + for (int col = 0; col < 2; col++) { + int top = col; // 0 or 1 + int bot = col + 2; // 2 or 3 + + if (behaviors[top] != BEHAVIOR_SOLID && behaviors[bot] != BEHAVIOR_SOLID) { + if (densities[top] > densities[bot]) { + // swap entire vec4 + vec4 tmp = cells[top]; + cells[top] = cells[bot]; + cells[bot] = tmp; + // update cached props + float tmpB = behaviors[top]; behaviors[top] = behaviors[bot]; behaviors[bot] = tmpB; + float tmpD = densities[top]; densities[top] = densities[bot]; densities[bot] = tmpD; + } + } + } + + // try diagonal: if top cell still has high density and couldn't fall straight + // for top-left (0): can it go diagonal to bottom-right (3)? + // for top-right (1): can it go diagonal to bottom-left (2)? + // only if the cell directly below is blocked (same or higher density) + bool tryLeftFirst = (seed & 1u) == 0u; + + if (tryLeftFirst) { + // try 0->3 diagonal (top-left to bottom-right) + if (col0Blocked && behaviors[0] != BEHAVIOR_SOLID && behaviors[3] != BEHAVIOR_SOLID && + densities[0] > densities[3]) { + vec4 tmp = cells[0]; cells[0] = cells[3]; cells[3] = tmp; + float tmpB = behaviors[0]; behaviors[0] = behaviors[3]; behaviors[3] = tmpB; + float tmpD = densities[0]; densities[0] = densities[3]; densities[3] = tmpD; + } + // try 1->2 diagonal (top-right to bottom-left) + if (col1Blocked && behaviors[1] != BEHAVIOR_SOLID && behaviors[2] != BEHAVIOR_SOLID && + densities[1] > densities[2]) { + vec4 tmp = cells[1]; cells[1] = cells[2]; cells[2] = tmp; + float tmpB = behaviors[1]; behaviors[1] = behaviors[2]; behaviors[2] = tmpB; + float tmpD = densities[1]; densities[1] = densities[2]; densities[2] = tmpD; + } + } else { + // try 1->2 first + if (col1Blocked && behaviors[1] != BEHAVIOR_SOLID && behaviors[2] != BEHAVIOR_SOLID && + densities[1] > densities[2]) { + vec4 tmp = cells[1]; cells[1] = cells[2]; cells[2] = tmp; + float tmpB = behaviors[1]; behaviors[1] = behaviors[2]; behaviors[2] = tmpB; + float tmpD = densities[1]; densities[1] = densities[2]; densities[2] = tmpD; + } + // try 0->3 + if (col0Blocked && behaviors[0] != BEHAVIOR_SOLID && behaviors[3] != BEHAVIOR_SOLID && + densities[0] > densities[3]) { + vec4 tmp = cells[0]; cells[0] = cells[3]; cells[3] = tmp; + float tmpB = behaviors[0]; behaviors[0] = behaviors[3]; behaviors[3] = tmpB; + float tmpD = densities[0]; densities[0] = densities[3]; densities[3] = tmpD; + } + } + + fragColor = cells[localIndex]; +} diff --git a/src/shaders/sandPhysics.vert b/src/shaders/sandPhysics.vert new file mode 100644 index 0000000..901d04b --- /dev/null +++ b/src/shaders/sandPhysics.vert @@ -0,0 +1,12 @@ +precision highp float; +precision highp int; + +in vec3 position; +in vec2 uv; + +out vec2 vUv; + +void main() { + vUv = uv; + gl_Position = vec4(position, 1.0); +}