-package imgui.gl
+package imgui.gl_utils
use package core
#private_file gl :: package gl
#load "lib/gl/gl_utils"
use package core
-use package imgui.gl
+use package imgui.gl_utils
#private_file gl :: package gl
Vector2 :: 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;
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);
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) {
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}) {
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);
+ }
}
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);
+}
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;
#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();
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 {
}
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);
}
}
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" () {
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 };
Empty;
Sand;
Water;
+ Wall;
}
+
+ types :: Type.[ .Sand, .Water, .Wall ];
type: Type = .Empty;
life: f32 = 0;
switch t {
case .Sand do return .Sand;
case .Water do return .Water;
+ case .Wall do return .Wall;
case #default do return .Empty;
}
}
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 ---
}
}
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) {
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);
}
}
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 });
}
}