#private_file events :: package js_events
#private_file gl :: package gl
+particle_board: ParticleBoard;
world_texture : gl.GLTexture
world_texture_data : [] u8
-world_width := 256
-world_height := 256
+world_width := 512
+world_height := 512
init :: () {
events.init();
gl.bindTexture(gl.TEXTURE_2D, -1);
imgui.immediate_renderer_init();
+
+ particle_board = create_board(world_width, world_height);
}
window_width := 0
case .MouseDown {
if event.mouse.button == .Left {
- println("Left button click!");
+ mx := (cast(f32) event.mouse.pos_x / cast(f32) window_width) * ~~world_width;
+ my := (cast(f32) event.mouse.pos_y / cast(f32) window_height) * ~~world_height;
+
+ radius := 10.0f;
+ for i: 100 {
+ rx := random.float(-1, 1);
+ ry := random.float(-1, 1);
+
+ particle_set(^particle_board,
+ get_index(^particle_board, ~~(mx + rx * radius), ~~(my + ry * radius)),
+ .{ .Water });
+ }
}
}
}
}
update :: () {
- world_texture_data[(100 * world_width + 50) * 3 + 0] = 255;
- world_texture_data[(100 * world_width + 50) * 3 + 2] = 255;
+ if random.between(0, 10) > 1 {
+ radius := 10.0f;
+ for i: 100 {
+ rx := random.float(-1, 1);
+ ry := random.float(-1, 1);
+
+ particle_set(^particle_board,
+ get_index(^particle_board, ~~(200 + rx * radius), ~~(100 + ry * radius)),
+ .{ .Sand });
+ }
+ }
+
+ update_particles(^particle_board);
+}
+
+prepare_world_texture :: () {
+ for y: particle_board.height {
+ for x: particle_board.width {
+ index := get_index(^particle_board, x, y);
+ color := particle_type_to_color(particle_board.particles[index].type);
+ world_texture_data[index * 3 + 0] = color.r;
+ world_texture_data[index * 3 + 1] = color.g;
+ world_texture_data[index * 3 + 2] = color.b;
+ }
+ }
+
+ gl.bindTexture(gl.TEXTURE_2D, world_texture);
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, world_width, world_height, gl.RGB, gl.UNSIGNED_BYTE, world_texture_data);
+ gl.bindTexture(gl.TEXTURE_2D, -1);
}
draw :: () {
+ prepare_world_texture();
+
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
use imgui { immediate_set_texture, immediate_textured_quad, immediate_flush };
gl.bindTexture(gl.TEXTURE_2D, world_texture);
- gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, world_width, world_height, gl.RGB, gl.UNSIGNED_BYTE, world_texture_data);
immediate_set_texture(0);
- immediate_textured_quad(.{ -1, -1 }, .{ 2, 2 }, .{ 0, 0 }, .{ 1, 1 }, color=.{ 1, 1, 1 });
+ immediate_textured_quad(.{ -1, 1 }, .{ 2, -2 }, .{ 0, 0 }, .{ 1, 1 }, color=.{ 1, 1, 1 });
immediate_flush();
+use package core
+
+Color :: struct {
+ Empty :: Color.{ 22, 22, 22 };
+ Sand :: Color.{ 226, 214, 173 };
+ Water :: Color.{ 100, 100, 255 };
+
+ r, g, b: u8;
+}
Vec2 :: struct {
- zero :: Vec2.{ 0, 0 };
+ zero :: Vec2.{ 0, 0 };
+ up :: Vec2.{ 0, -1 };
+ down :: Vec2.{ 0, 1 };
+ left :: Vec2.{ -1, 0 };
+ right :: Vec2.{ 1, 0 };
x, y: f32;
}
ParticleBoard :: struct {
particles: [] Particle;
+ particle_allocator: Allocator;
+
width, height: u32;
+
+ time: u32 = 0;
}
Particle :: struct {
Type :: enum {
Empty;
Sand;
+ Water;
}
type: Type = .Empty;
+ life: f32 = 0;
velocity: Vec2 = .zero;
}
-init_particles :: (particles: [] Particle) {
+particle_type_to_color :: (t: Particle.Type) -> Color {
+ switch t {
+ case .Sand do return .Sand;
+ case .Water do return .Water;
+ case #default do return .Empty;
+ }
+}
+
+create_board :: (width: u32, height: u32, allocator := context.allocator) -> ParticleBoard {
+ board := ParticleBoard.{
+ particles = memory.make_slice(Particle, width * height, allocator=allocator),
+ particle_allocator = allocator,
+ width = width,
+ height = height
+ };
+
+ init_particles(^board);
+
+ return board;
+}
+
+init_particles :: (use board: ^ParticleBoard) {
for ^particle: particles {
- *particle = .{ .Sand };
+ *particle = .{ .Empty };
+ }
+}
+
+update_particles :: (use board: ^ParticleBoard) {
+ time += 1;
+
+ // CLEANUP: Iterating backwards in a for loop should be easier
+ // This is iterating backwards because in general, particles that are lower on the screen
+ // should update first to beable to "fall", and then particles above them can "fall" where
+ // the first particle was. Obviously this is wrong for particles that "float", but it is
+ // less noticeable for those particles. Ideally, this would all work on a secondary buffer,
+ // then a buffer swap would take place between each update.
+ while y := cast(i32) (height - 1); y >= 0 {
+ defer y -= 1;
+
+ // This for loop should alternate from L2R to R2L
+ x := 0;
+ x_step := 1;
+ x_ne := width;
+ if time % 2 == 1 {
+ x = width - 1;
+ x_step = cast(i32) -1;
+ x_ne = cast(i32) -1;
+ }
+
+ while x != x_ne {
+ defer x += x_step;
+
+ particle_type := particles[get_index(board, x, y)].type;
+
+ switch particle_type {
+ case .Sand do update_sand(board, x, y);
+ case .Water do update_water(board, x, y);
+ case .Empty ---
+ }
+ }
+ }
+}
+
+particle_set :: (use board: ^ParticleBoard, index: i32, particle: Particle) {
+ if index < 0 do return;
+
+ particles[index] = particle;
+}
+
+// This should get inlined by Onyx, when inlining is a thing.
+get_index :: (use board: ^ParticleBoard, x: i32, y: i32) -> i32 {
+ if x < 0 || y < 0 || x >= width || y >= height do return -1;
+ return y * width + x;
+}
+
+
+#private_file
+update_sand :: (use board: ^ParticleBoard, x: i32, y: i32) {
+ index := get_index(board, x, y);
+
+ b_index := get_index(board, x, y + 1);
+ bl_index := get_index(board, x - 1, y + 1);
+ br_index := get_index(board, x + 1, y + 1);
+
+ // NOTE: These do NOT short circuit so we are accessing memory that is not
+ // technically in our control on the second part of the condition!! This
+ // does not affect too many things as the accessing does not modify or damage
+ // the data.
+ if b_index > 0 && particles[b_index].type == .Empty {
+ particle_set(board, b_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ else {
+ if time % 2 == 0 {
+ if bl_index > 0 && particles[bl_index].type == .Empty {
+ particle_set(board, bl_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif br_index > 0 && particles[br_index].type == .Empty {
+ particle_set(board, br_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ } else {
+ if br_index > 0 && particles[br_index].type == .Empty {
+ particle_set(board, br_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif bl_index > 0 && particles[bl_index].type == .Empty {
+ particle_set(board, bl_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ }
+ }
+}
+
+#private_file
+update_water :: (use board: ^ParticleBoard, x: i32, y: i32) {
+ index := get_index(board, x, y);
+
+ b_index := get_index(board, x, y + 1);
+ bl_index := get_index(board, x - 1, y + 1);
+ br_index := get_index(board, x + 1, y + 1);
+ l_index := get_index(board, x - 1, y);
+ r_index := get_index(board, x + 1, y);
+
+ // NOTE: These do NOT short circuit so we are accessing memory that is not
+ // technically in our control on the second part of the condition!! This
+ // does not affect too many things as the accessing does not modify or damage
+ // the data.
+ if b_index > 0 && particles[b_index].type == .Empty {
+ particle_set(board, b_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ else {
+ if time % 2 == 1 {
+ if bl_index > 0 && particles[bl_index].type == .Empty {
+ particle_set(board, bl_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif br_index > 0 && particles[br_index].type == .Empty {
+ particle_set(board, br_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif l_index > 0 && particles[l_index].type == .Empty {
+ particle_set(board, l_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif r_index > 0 && particles[r_index].type == .Empty {
+ particle_set(board, r_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ } else {
+ if br_index > 0 && particles[br_index].type == .Empty {
+ particle_set(board, br_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif bl_index > 0 && particles[bl_index].type == .Empty {
+ particle_set(board, bl_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif r_index > 0 && particles[r_index].type == .Empty {
+ particle_set(board, r_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ elseif l_index > 0 && particles[l_index].type == .Empty {
+ particle_set(board, l_index, particles[index]);
+ particle_set(board, index, .{ .Empty });
+ }
+ }
}
}