From: Brendan Hansen Date: Thu, 29 Apr 2021 04:14:22 +0000 (-0500) Subject: proper ui; wall particle X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=c4c096c6473ca6825749307f294a778ade185931;p=sand-toy.git proper ui; wall particle --- diff --git a/lib/gl/gl_utils.onyx b/lib/gl/gl_utils.onyx index b306d5a..29b749f 100644 --- a/lib/gl/gl_utils.onyx +++ b/lib/gl/gl_utils.onyx @@ -1,4 +1,4 @@ -package imgui.gl +package imgui.gl_utils use package core #private_file gl :: package gl diff --git a/lib/immediate_renderer.onyx b/lib/immediate_renderer.onyx index 5a7c315..98343f8 100644 --- a/lib/immediate_renderer.onyx +++ b/lib/immediate_renderer.onyx @@ -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); +} diff --git a/lib/shaders/immediate_vertex.glsl b/lib/shaders/immediate_vertex.glsl index cf95128..d3fdcab 100644 --- a/lib/shaders/immediate_vertex.glsl +++ b/lib/shaders/immediate_vertex.glsl @@ -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; diff --git a/site/sand_toy.wasm b/site/sand_toy.wasm index 98b2ec3..49bc6f4 100644 Binary files a/site/sand_toy.wasm and b/site/sand_toy.wasm differ diff --git a/src/sand_toy.onyx b/src/sand_toy.onyx index 3455a50..7d717de 100644 --- a/src/sand_toy.onyx +++ b/src/sand_toy.onyx @@ -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" () { diff --git a/src/simulation.onyx b/src/simulation.onyx index d5c68fe..93a12fa 100644 --- a/src/simulation.onyx +++ b/src/simulation.onyx @@ -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 }); } }