Explosions: - trigger_explosion(x, y, radius, damage) in Game - Destroys non-static solids, ignites empty cells, pressure spike - Knockback to entities (velocity away from center + upward pop) - Particle burst on explosion (fire + smoke + debris) Particle system: - ParticleManager in src/physics/particle.rs (capacity 2000) - CPU-side: position, velocity, life, color, gravity, size - spawn_burst() for radial bursts, individual spawn() for ambient - Rendered as third draw call in graphics.rs (is_ui: 2 in shader) - ParticleInstance = ColorInstance compatible (12 bytes) - upload_particles() via GpuRenderer trait - Ambient: fire sparks, lava bubbles — scanned around player Electricity: - chunk.electricity: Vec<u8> (4KB/chunk) — current strength 0-255 - Material.conductive + conductivity fields (water = conductive) - electricity_step() in CA: propagates current through conductive neighbors, decays over time, skips non-conductive cells - cells_swap swaps electricity, serialization saves/loads it Structural integrity: - Material.structural: bool (stone, wood = true) - structural_step() in CA (every 30 ticks, infinite mode only): checks if structural cells have support (below, below-left, below-right) converts unsupported to Sand (falls) - Only active in infinite mode to avoid breaking test grids Graphics shader: is_ui==2 branch renders particles in world-space All 185 tests + 14 scenarios pass. 143 FPS benchmark.
100 lines
2.7 KiB
GLSL
100 lines
2.7 KiB
GLSL
#version 450
|
|
|
|
layout(push_constant) uniform PC {
|
|
vec2 screen_size;
|
|
vec2 cell_size;
|
|
ivec2 world_size;
|
|
ivec2 cam_pos;
|
|
vec3 ambient;
|
|
uint is_ui;
|
|
uint light_count;
|
|
} pc;
|
|
|
|
layout(location = 0) in vec2 in_pos;
|
|
layout(location = 1) in vec2 in_grid;
|
|
layout(location = 2) in vec4 in_color;
|
|
|
|
layout(location = 0) out vec4 out_color;
|
|
layout(location = 1) out vec3 out_light;
|
|
|
|
layout(std430, binding = 0) readonly buffer GridBuffer {
|
|
uint cells[];
|
|
} grid;
|
|
|
|
struct LightSrc {
|
|
vec2 pos;
|
|
float radius;
|
|
float pad0;
|
|
vec3 color;
|
|
float pad1;
|
|
};
|
|
|
|
layout(std430, binding = 1) readonly buffer LightBuffer {
|
|
LightSrc sources[];
|
|
} lights;
|
|
|
|
bool is_solid(uint m) {
|
|
return m == 3u || m == 5u || m == 6u || m == 7u || m == 12u || m == 13u || m == 14u;
|
|
}
|
|
|
|
bool line_of_sight(ivec2 a, ivec2 b) {
|
|
ivec2 p = a;
|
|
ivec2 d = abs(b - a);
|
|
ivec2 s = ivec2(a.x < b.x ? 1 : -1, a.y < b.y ? 1 : -1);
|
|
int err = d.x - d.y;
|
|
while (true) {
|
|
if (p == b) return true;
|
|
ivec2 vp = p - pc.cam_pos;
|
|
if (vp.x < 0 || vp.x >= pc.world_size.x || vp.y < 0 || vp.y >= pc.world_size.y) return false;
|
|
uint m = grid.cells[vp.y * pc.world_size.x + vp.x];
|
|
if (is_solid(m)) return false;
|
|
int e2 = 2 * err;
|
|
if (e2 > -d.y) { err -= d.y; p.x += s.x; }
|
|
if (e2 < d.x) { err += d.x; p.y += s.y; }
|
|
}
|
|
}
|
|
|
|
vec3 compute_light(ivec2 world_pos) {
|
|
vec3 light = pc.ambient;
|
|
for (uint i = 0u; i < pc.light_count; i++) {
|
|
LightSrc src = lights.sources[i];
|
|
ivec2 src_pos = ivec2(src.pos);
|
|
float dist = length(vec2(world_pos - src_pos));
|
|
float rad = src.radius;
|
|
if (dist >= rad) continue;
|
|
if (!line_of_sight(src_pos, world_pos)) continue;
|
|
float t = 1.0 - dist / rad;
|
|
float att = t * t;
|
|
light += src.color * att;
|
|
}
|
|
return min(light, vec3(1.0));
|
|
}
|
|
|
|
void main() {
|
|
if (pc.is_ui == 2u) {
|
|
vec2 world_pos = in_grid;
|
|
vec2 screen = (world_pos - vec2(pc.cam_pos)) * pc.cell_size + in_pos * pc.cell_size;
|
|
gl_Position = vec4(
|
|
2.0 * screen.x / pc.screen_size.x - 1.0,
|
|
2.0 * screen.y / pc.screen_size.y - 1.0,
|
|
0.0, 1.0
|
|
);
|
|
out_color = in_color;
|
|
out_light = vec3(1.0);
|
|
} else {
|
|
vec2 pixel = (in_grid + in_pos) * pc.cell_size;
|
|
gl_Position = vec4(
|
|
2.0 * pixel.x / pc.screen_size.x - 1.0,
|
|
2.0 * pixel.y / pc.screen_size.y - 1.0,
|
|
0.0, 1.0
|
|
);
|
|
out_color = in_color;
|
|
if (pc.is_ui != 0u) {
|
|
out_light = vec3(1.0);
|
|
} else {
|
|
ivec2 world_pos = ivec2(in_grid + vec2(pc.cam_pos));
|
|
out_light = compute_light(world_pos);
|
|
}
|
|
}
|
|
}
|