proper ui; wall particle
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 29 Apr 2021 04:14:22 +0000 (23:14 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 29 Apr 2021 04:14:22 +0000 (23:14 -0500)
lib/gl/gl_utils.onyx
lib/immediate_renderer.onyx
lib/shaders/immediate_vertex.glsl
site/sand_toy.wasm
src/sand_toy.onyx
src/simulation.onyx

index b306d5ab451b0839a82c729f43c5f9f838a0d13e..29b749fb5fe7c4631bdb4048d29b22fa8d978b9f 100644 (file)
@@ -1,4 +1,4 @@
-package imgui.gl
+package imgui.gl_utils
 
 use package core
 #private_file gl :: package gl
index 5a7c31568274f39b17101d18ad84a5fabb6a41f3..98343f879129d6b3355450447c1e0b913219864c 100644 (file)
@@ -3,7 +3,7 @@ package imgui
 #load "lib/gl/gl_utils"
 
 use package core
-use package imgui.gl
+use package imgui.gl_utils
 #private_file gl :: package gl
 
 Vector2 :: struct {
@@ -40,6 +40,9 @@ Immediate_Renderer :: struct {
     texture_uniform     : gl.GLUniformLocation;
     use_texture_uniform : gl.GLUniformLocation;
 
+    view_uniform        : gl.GLUniformLocation;
+    world_uniform       : gl.GLUniformLocation;
+
     // Needs to be a multiple of 3!!
     Default_Max_Verticies :: 1023;
 
@@ -58,6 +61,8 @@ Immediate_Renderer :: struct {
 
         texture_uniform     = gl.getUniformLocation(shader.program, "u_texture");
         use_texture_uniform = gl.getUniformLocation(shader.program, "u_use_texture");
+        view_uniform        = gl.getUniformLocation(shader.program, "u_view");
+        world_uniform       = gl.getUniformLocation(shader.program, "u_world");
 
         verticies = memory.make_slice(Immediate_Vertex, max_verticies);
         memory.set(verticies.data, 0, verticies.count * sizeof Immediate_Vertex);
@@ -83,6 +88,14 @@ Immediate_Renderer :: struct {
         gl.vertexAttribPointer(2, 2, gl.FLOAT, false, sizeof Immediate_Vertex, 6 * sizeof f32);
 
         gl.bindBuffer(gl.ARRAY_BUFFER, -1);
+
+
+
+        // TEMPORARY
+        gl.uniformMatrix4(world_uniform, false, f32.[ 1, 0, 0, 0,
+                                                      0, 1, 0, 0,
+                                                      0, 0, 1, 0,
+                                                      0, 0, 0, 1 ]);
     }
 
     free :: (use ir: ^Immediate_Renderer) {
@@ -112,7 +125,7 @@ Immediate_Renderer :: struct {
     push_vertex :: proc {
         // If a color is not provided, the previous color is used.
         (use ir: ^Immediate_Renderer, position: Vector2) {
-            push_vertex(ir, position, texture = .{ 0, 0 }, color = verticies[vertex_count - 1].color);
+            push_vertex(ir, position, color = verticies[vertex_count - 1].color);
         },
 
         (use ir: ^Immediate_Renderer, position: Vector2, color: Color4, texture: Vector2 = .{0,0}) {
@@ -154,6 +167,17 @@ Immediate_Renderer :: struct {
         gl.uniform1i(texture_uniform, math.max(texture_id, 0));
         gl.uniform1i(use_texture_uniform, cast(i32) (texture_id >= 0));
     }
+
+    use_ortho_projection :: (use ir: ^Immediate_Renderer, left: f32, right: f32, top: f32, bottom: f32) {
+        projection_matrix := f32.[
+            2 / (right - left), 0, 0, -(right + left) / (right - left),
+            0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom),
+            0, 0, -2, -1,
+            0, 0, 0, 1
+        ];
+
+        gl.uniformMatrix4(view_uniform, true, projection_matrix);
+    }
 }
 
 
@@ -192,3 +216,7 @@ immediate_textured_quad :: (position: Vector2, size: Vector2, texture_position:
 immediate_flush :: () do immediate_renderer->flush();
 
 immediate_set_texture :: (texture_id: i32 = -1) do immediate_renderer->set_texture(texture_id);
+
+use_ortho_projection :: (left: f32, right: f32, top: f32, bottom: f32) {
+    immediate_renderer->use_ortho_projection(left, right, top, bottom);
+}
index cf9512833def17e6e4a91e583619278d59f21641..d3fdcab68d0e851c3ad971a5e06fca01b3abf9d0 100644 (file)
@@ -4,11 +4,14 @@ layout(location = 0) in vec2 a_vertex;
 layout(location = 1) in vec4 a_color;
 layout(location = 2) in vec2 a_texture;
 
+uniform mat4 u_view;
+uniform mat4 u_world;
+
 out vec4 v_color;
 out vec2 v_texture;
 
 void main() {
-    gl_Position = vec4(a_vertex, 0, 1);
+    gl_Position = u_view * u_world * vec4(a_vertex, 0, 1);
 
     v_color = a_color;
     v_texture = a_texture;
index 98b2ec3f07e8393aec3c0de6da2efe904818a548..49bc6f47d2957d2ef9e05b45fad247eaa12d20a2 100644 (file)
Binary files a/site/sand_toy.wasm and b/site/sand_toy.wasm differ
index 3455a50ff166b6c78175709cd913f0aeec5e3419..7d717de8e309b0b17d0914600f23580c41497b83 100644 (file)
@@ -3,12 +3,19 @@ use package core
 #private_file events :: package js_events
 #private_file gl     :: package gl
 
+BAR_HEIGHT :: 32.0f
+
 particle_board: ParticleBoard;
 
 world_texture : gl.GLTexture
 world_texture_data : [] u8
-world_width := 512
-world_height := 512
+world_width := 256
+world_height := 256
+
+window_width  := 0
+window_height := 0
+
+selected_particle := Particle.Type.Empty
 
 init :: () {
     events.init();
@@ -31,9 +38,6 @@ init :: () {
     particle_board = create_board(world_width, world_height);
 }
 
-window_width  := 0
-window_height := 0
-
 poll_events :: () {
     for event: events.consume() do switch event.kind {
         case .Resize {
@@ -47,37 +51,59 @@ poll_events :: () {
         }
 
         case .MouseDown {
+            if event.mouse.pos_y < ~~BAR_HEIGHT {
+                handle_ui_mouse(^event.mouse);
+                return;
+            }
+
             if event.mouse.button == .Left {
                 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;
+                my := ((~~event.mouse.pos_y - BAR_HEIGHT) / cast(f32) window_height) * ~~world_height;
 
-                radius := 10.0f;
-                for i: 100 {
-                    rx := random.float(-1, 1);
-                    ry := random.float(-1, 1);
+                spawn_particles(~~mx, ~~my, 20, selected_particle);
+            }
+
+            if event.mouse.button == .Right {
+                mx := (cast(f32) event.mouse.pos_x / cast(f32) window_width) * ~~world_width;
+                my := ((~~event.mouse.pos_y - BAR_HEIGHT) / cast(f32) window_height) * ~~world_height;
 
-                    particle_set(^particle_board,
-                        get_index(^particle_board, ~~(mx + rx * radius), ~~(my + ry * radius)),
-                        .{ .Water });
-                }
+                remove_particles(~~mx, ~~my, 10);
             }
         }
     }
 }
 
-update :: () {
-    if random.between(0, 10) > 1 {
-        radius := 10.0f;
-        for i: 100 {
-            rx := random.float(-1, 1);
-            ry := random.float(-1, 1);
+handle_ui_mouse :: (event: ^events.MouseEvent) {
+    if event.button == .Left {
+        selected_particle = Particle.types[cast(u32) math.floor(~~event.pos_x / BAR_HEIGHT)];
+    }
+}
+
+spawn_particles :: (x: i32, y: i32, radius: i32, type: Particle.Type, count := 100) {
+    for i: count {
+        r := random.float(0, ~~radius);
+        a := random.float(0, 2 * math.PI);
+
+        dx := r * math.cos(a);
+        dy := r * math.sin(a);
+
+        particle_set(^particle_board,
+            get_index(^particle_board, ~~(~~x + dx), ~~(~~y + dy)),
+            .{ type });
+    }
+}
 
+remove_particles :: (x: i32, y: i32, radius: i32) {
+    for yy: y - radius .. y + radius {
+        for xx: x - radius .. x + radius {
             particle_set(^particle_board,
-                get_index(^particle_board, ~~(200 + rx * radius), ~~(100 + ry * radius)),
-                .{ .Sand });
+                get_index(^particle_board, ~~xx, ~~yy),
+                .{ .Empty });
         }
     }
+}
 
+update :: () {
     update_particles(^particle_board);
 }
 
@@ -98,21 +124,53 @@ prepare_world_texture :: () {
 }
 
 draw :: () {
+    use imgui;
+
     prepare_world_texture();
+    use_ortho_projection(0, ~~window_width, 0, ~~window_height);
 
     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);
+    draw_selection_bar();
     
+    gl.bindTexture(gl.TEXTURE_2D, world_texture);
+
     immediate_set_texture(0);
-    immediate_textured_quad(.{ -1, 1 }, .{ 2, -2 }, .{ 0, 0 }, .{ 1, 1 }, color=.{ 1, 1, 1 });
+    immediate_textured_quad(.{ 0, BAR_HEIGHT }, .{ ~~window_width, ~~window_height - BAR_HEIGHT }, .{ 0, 0 }, .{ 1, 1 }, color=.{ 1, 1, 1 });
+    immediate_set_texture();
 
     immediate_flush();
 
     gl.bindTexture(gl.TEXTURE_2D, -1);
+
+
+    draw_selection_bar :: () {
+        use imgui;
+
+        immediate_quad(.{ 0, 0 }, .{ ~~window_width, BAR_HEIGHT }, color=.{.2,.2,.2});
+
+        x_off := 4.0f;
+        for type: Particle.types {
+            if type == selected_particle {
+                immediate_quad(.{ x_off - 4, 0 }, .{ BAR_HEIGHT, BAR_HEIGHT }, color=.{1,1,0});
+            }
+
+            color := particle_type_to_color(type) |> convert_color();
+            immediate_quad(.{ x_off, 4 }, .{ BAR_HEIGHT - 8,  BAR_HEIGHT - 8 }, color=color);
+
+            x_off += BAR_HEIGHT;
+        }
+    }
+
+
+    convert_color :: (use c: Color) -> imgui.Color4 {
+        return .{
+            ~~cast(i32) r / 255.0f,
+            ~~cast(i32) g / 255.0f,
+            ~~cast(i32) b / 255.0f,
+        };
+    }
 }
 
 #export "loop" () {
index d5c68fe681bb367765a6a1d94ad80fd6c6be3816..93a12faf1d6e00fb19d5123c020f3111febbb5e1 100644 (file)
@@ -4,12 +4,13 @@ Color :: struct {
     Empty :: Color.{  22,  22,  22 };
     Sand  :: Color.{ 226, 214, 173 };
     Water :: Color.{ 100, 100, 255 };
+    Wall  :: Color.{  60,  60,  30 };
 
     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 };
@@ -32,7 +33,10 @@ Particle :: struct {
         Empty;
         Sand;
         Water;
+        Wall;
     }
+    
+    types :: Type.[ .Sand, .Water, .Wall ];
 
     type: Type = .Empty;
     life: f32 = 0;
@@ -43,6 +47,7 @@ particle_type_to_color :: (t: Particle.Type) -> Color {
     switch t {
         case .Sand    do return .Sand;
         case .Water   do return .Water;
+        case .Wall    do return .Wall;
         case #default do return .Empty;
     }
 }
@@ -96,6 +101,7 @@ update_particles :: (use board: ^ParticleBoard) {
             switch particle_type {
                 case .Sand  do update_sand(board, x, y);
                 case .Water do update_water(board, x, y);
+                case .Wall  --- // Walls do not need updating
                 case .Empty ---
             }
         }
@@ -108,12 +114,43 @@ particle_set :: (use board: ^ParticleBoard, index: i32, particle: Particle) {
     particles[index] = particle;
 }
 
+particle_switch :: (use board: ^ParticleBoard, a_index: i32, b_index: i32) {
+    if a_index < 0 || b_index < 0 do return;
+
+    particles[a_index], particles[b_index] = particles[b_index], particles[a_index];
+}
+
 // 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
+particle_is_liquid :: (type: Particle.Type) -> bool {
+    switch type {
+        case .Water   do return true;
+        case #default do return false;
+    }
+}
+
+#private_file
+first_empty :: (use board: ^ParticleBoard, indicies: ..i32) -> i32 {
+    for index: indicies {
+        if index < 0 do continue;
+
+        if particles[index].type == .Empty {
+            return index;
+        }
+    }
+
+    return -1;
+}
+
+#private_file
+can_sink :: (use board: ^ParticleBoard, below_index: i32) -> bool {
+    return particle_is_liquid(particles[below_index].type);
+}
 
 #private_file
 update_sand :: (use board: ^ParticleBoard, x: i32, y: i32) {
@@ -123,34 +160,21 @@ update_sand :: (use board: ^ParticleBoard, x: i32, y: i32) {
     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]);
+    move_to_index := -1;
+    if time % 2 == 0 {
+        move_to_index = first_empty(board, b_index, bl_index, br_index); 
+    } else {
+        move_to_index = first_empty(board, b_index, br_index, bl_index); 
+    }
+
+    if move_to_index != -1 {
+        particle_set(board, move_to_index, particles[index]);
         particle_set(board, index, .{ .Empty });
+        return;
     }
-    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 });
-            }
-        }
+
+    if can_sink(board, b_index) {
+        particle_switch(board, index, b_index);
     }
 }
 
@@ -164,49 +188,15 @@ update_water :: (use board: ^ParticleBoard, x: i32, y: i32) {
     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 });
+    move_to_index := -1;
+    if time % 2 == 1 {
+        move_to_index = first_empty(board, b_index, bl_index, br_index, l_index, r_index);
+    } else {
+        move_to_index = first_empty(board, b_index, br_index, bl_index, r_index, l_index);
     }
-    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 });
-            }
-        }
+
+    if move_to_index != -1 {
+        particle_set(board, move_to_index, particles[index]);
+        particle_set(board, index, .{ .Empty });
     }
 }