Snap brick to mouse on rotate
And fix crashing on moving outside of window
This commit is contained in:
+16
-3
@@ -656,14 +656,25 @@ fn wlKeyboardHandleKey(data: ?*anyopaque, keyboard: ?*c.wl_keyboard, serial: u32
|
||||
c.XKB_KEY_Escape => {
|
||||
wl_state.running = false;
|
||||
},
|
||||
c.XKB_KEY_space => {
|
||||
wl_state.app_input.key.space.down = pressed;
|
||||
c.XKB_KEY_f => {
|
||||
const already_pressed: bool = wl_state.app_input.key.f.down;
|
||||
wl_state.app_input.key.f.down = pressed;
|
||||
wl_state.app_input.key.f.pressing = pressed and already_pressed;
|
||||
},
|
||||
c.XKB_KEY_r => {
|
||||
const already_pressed: bool = wl_state.app_input.key.r.down;
|
||||
wl_state.app_input.key.r.down = pressed;
|
||||
wl_state.app_input.key.r.pressing = pressed and already_pressed;
|
||||
},
|
||||
c.XKB_KEY_q => {
|
||||
const already_pressed: bool = wl_state.app_input.key.q.down;
|
||||
wl_state.app_input.key.q.down = pressed;
|
||||
wl_state.app_input.key.q.pressing = pressed and already_pressed;
|
||||
},
|
||||
c.XKB_KEY_space => {
|
||||
const already_pressed: bool = wl_state.app_input.key.space.down;
|
||||
wl_state.app_input.key.space.down = pressed;
|
||||
wl_state.app_input.key.space.pressing = pressed and already_pressed;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -929,13 +940,15 @@ pub fn main() u8 {
|
||||
|
||||
const render_start_time: u64 = timer.read();
|
||||
|
||||
wl_state.app_input.delta_time = frame_target_time_s;
|
||||
|
||||
const app_offscreen_buffer: puzzle.platform.OffscreenBuffer = .{
|
||||
.width = wl_state.window_width,
|
||||
.height = wl_state.window_height,
|
||||
.stride = wl_state.window_width * 4,
|
||||
.data = shared_memory_pool_data,
|
||||
};
|
||||
puzzle.updateAndRender(&app_memory, app_offscreen_buffer, wl_state.app_input);
|
||||
puzzle.updateAndRender(&app_memory, app_offscreen_buffer, &wl_state.app_input);
|
||||
|
||||
const render_end_time: u64 = timer.read();
|
||||
const render_time = render_end_time - render_start_time;
|
||||
|
||||
+7
-1
@@ -8,17 +8,23 @@ pub fn megaBytes(bytes: usize) usize {
|
||||
return kiloBytes(bytes) * 1024;
|
||||
}
|
||||
|
||||
const Key = struct {
|
||||
pub const Key = struct {
|
||||
down: bool,
|
||||
pressing: bool,
|
||||
frame_count: u32,
|
||||
};
|
||||
|
||||
const Keyboard = struct {
|
||||
esc: Key,
|
||||
f: Key,
|
||||
r: Key,
|
||||
q: Key,
|
||||
space: Key,
|
||||
};
|
||||
|
||||
pub const AppInput = struct {
|
||||
delta_time: f64,
|
||||
|
||||
mouse_present: bool = false,
|
||||
mouse_left_down: bool = false,
|
||||
mouse_x: f64,
|
||||
|
||||
+185
-20
@@ -1,5 +1,6 @@
|
||||
//! By convention, root.zig is the root source file when making a library.
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const platform = @import("platform.zig");
|
||||
|
||||
@@ -9,6 +10,9 @@ const V2 = struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
|
||||
const max = V2{ .x = std.math.floatMax(f32), .y = std.math.floatMax(f32) };
|
||||
const zero = V2{ .x = 0.0, .y = 0.0 };
|
||||
|
||||
fn add(self: V2, other: V2) V2 {
|
||||
return .{
|
||||
.x = self.x + other.x,
|
||||
@@ -24,6 +28,12 @@ const V2 = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const BBox = struct {
|
||||
min: V2,
|
||||
max: V2,
|
||||
center: V2,
|
||||
};
|
||||
|
||||
const Camera = struct {
|
||||
pos: V2,
|
||||
offset: V2,
|
||||
@@ -33,8 +43,27 @@ const Brick = struct {
|
||||
rotating: bool,
|
||||
pos: V2,
|
||||
color: u32,
|
||||
rotating_time: f64,
|
||||
tiles: [4][4]u32,
|
||||
|
||||
fn transpose(self: Brick) [4][4]u32 {
|
||||
var result = [4][4]u32{
|
||||
.{ 0, 0, 0, 0 },
|
||||
.{ 0, 0, 0, 0 },
|
||||
.{ 0, 0, 0, 0 },
|
||||
.{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
for (0..self.tiles.len) |i| {
|
||||
const row = self.tiles[i];
|
||||
for (0..row.len) |j| {
|
||||
result[i][j] = self.tiles[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn rotate(self: Brick) [4][4]u32 {
|
||||
var result = [4][4]u32{
|
||||
.{ 0, 0, 0, 0 },
|
||||
@@ -61,6 +90,11 @@ const State = struct {
|
||||
bricks: [8]Brick,
|
||||
};
|
||||
|
||||
const tile_size = V2{
|
||||
.x = TILE_SIZE,
|
||||
.y = TILE_SIZE,
|
||||
};
|
||||
|
||||
fn pointInsideRect(p: V2, rect_start: V2, rect_size: V2) bool {
|
||||
var result: bool = false;
|
||||
|
||||
@@ -74,6 +108,67 @@ fn pointInsideRect(p: V2, rect_start: V2, rect_size: V2) bool {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn findBrickBoundingBox(brick: *const Brick) BBox {
|
||||
var result = BBox{ .min = V2.max, .max = V2.zero, .center = V2.zero };
|
||||
|
||||
for (brick.tiles, 0..) |row, i| {
|
||||
for (row, 0..) |tile, j| {
|
||||
if (tile > 0) {
|
||||
const tile_start = V2{
|
||||
.x = @floatFromInt(j * TILE_SIZE),
|
||||
.y = @floatFromInt(i * TILE_SIZE),
|
||||
};
|
||||
const tile_end = tile_start.add(tile_size);
|
||||
|
||||
const start = brick.pos.add(tile_start);
|
||||
const end = brick.pos.add(tile_end);
|
||||
|
||||
if (start.x < result.min.x) result.min.x = start.x;
|
||||
if (start.y < result.min.y) result.min.y = start.y;
|
||||
if (result.max.x < end.x) result.max.x = end.x;
|
||||
if (result.max.y < end.y) result.max.y = end.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.center = .{
|
||||
.x = (result.min.x + result.max.x) / 2.0,
|
||||
.y = (result.min.y + result.max.y) / 2.0,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn drawRect(buffer: platform.OffscreenBuffer, pos: V2, size: V2, color: u32, line_width: i32) void {
|
||||
var pixels: []u32 = @ptrCast(@alignCast(buffer.data));
|
||||
|
||||
const start_x: i32 = if (pos.x < 0) 0 else @intFromFloat(pos.x);
|
||||
const start_y: i32 = if (pos.y < 0) 0 else @intFromFloat(pos.y);
|
||||
|
||||
const end = pos.add(size);
|
||||
const end_x: i32 = if (end.x < 0) 0 else @intFromFloat(end.x);
|
||||
const end_y: i32 = if (end.y < 0) 0 else @intFromFloat(end.y);
|
||||
|
||||
const min_x: usize = @intCast(if (buffer.width < start_x) buffer.width else start_x);
|
||||
const min_y: usize = @intCast(if (buffer.height < start_y) buffer.height else start_y);
|
||||
const max_x: usize = @intCast(if (buffer.width < end_x) buffer.width else end_x);
|
||||
const max_y: usize = @intCast(if (buffer.height < end_y) buffer.height else end_y);
|
||||
|
||||
for (min_y..max_y) |y| {
|
||||
for (min_x..max_x) |x| {
|
||||
const left = x < start_x + line_width;
|
||||
const top = y < start_y + line_width;
|
||||
const right = end_x - line_width - 1 < x;
|
||||
const bottom = end_y - line_width - 1 < y;
|
||||
if (left or top or right or bottom) {
|
||||
const idx: usize = y * @as(usize, @intCast(buffer.width)) + x;
|
||||
|
||||
pixels[idx] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fillRect(
|
||||
buffer: platform.OffscreenBuffer,
|
||||
pos_x: i32,
|
||||
@@ -91,6 +186,11 @@ fn fillRect(
|
||||
|
||||
if (start_x < 0) start_x = 0;
|
||||
if (start_y < 0) start_y = 0;
|
||||
if (buffer.width < start_x) start_x = buffer.width;
|
||||
if (buffer.height < start_y) start_y = buffer.height;
|
||||
|
||||
if (end_x < 0) end_x = 0;
|
||||
if (end_y < 0) end_y = 0;
|
||||
if (buffer.width < end_x) end_x = buffer.width;
|
||||
if (buffer.height < end_y) end_y = buffer.height;
|
||||
|
||||
@@ -113,7 +213,22 @@ fn fillSquare(
|
||||
fillRect(buffer, x, y, size, size, color);
|
||||
}
|
||||
|
||||
pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: platform.AppInput) void {
|
||||
fn keyPressed(key: *platform.Key) bool {
|
||||
var result = false;
|
||||
|
||||
if (key.down) {
|
||||
if (key.frame_count == 0) {
|
||||
result = true;
|
||||
}
|
||||
key.frame_count += 1;
|
||||
} else {
|
||||
key.frame_count = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: *platform.AppInput) void {
|
||||
var state: *State = @ptrCast(@alignCast(memory.permanent_storage));
|
||||
|
||||
const mid = V2{
|
||||
@@ -125,6 +240,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[0] = Brick{
|
||||
.rotating = false,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 0, .y = 50 },
|
||||
.color = 0xFFFF0000,
|
||||
.tiles = [4][4]u32{
|
||||
@@ -137,8 +253,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[1] = Brick{
|
||||
.rotating = false,
|
||||
.pos = V2{ .x = 32, .y = 50 },
|
||||
.color = 0xFFEF0000,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 256, .y = 50 },
|
||||
.color = 0xFF665566,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 2, 2, 2, 0 },
|
||||
.{ 2, 2, 0, 0 },
|
||||
@@ -149,8 +266,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[2] = Brick{
|
||||
.rotating = false,
|
||||
.pos = V2{ .x = 64, .y = 50 },
|
||||
.color = 0xFFDF0000,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 384, .y = 50 },
|
||||
.color = 0xFF33A000,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 3, 3, 3, 0 },
|
||||
.{ 0, 0, 3, 3 },
|
||||
@@ -161,8 +279,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[3] = Brick{
|
||||
.rotating = false,
|
||||
.pos = V2{ .x = 96, .y = 50 },
|
||||
.color = 0xFFCF0000,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 512, .y = 50 },
|
||||
.color = 0xFFCF6666,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 4, 0, 0, 0 },
|
||||
.{ 4, 4, 4, 0 },
|
||||
@@ -173,8 +292,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[4] = Brick{
|
||||
.rotating = false,
|
||||
.pos = V2{ .x = 128, .y = 150 },
|
||||
.color = 0xFFBF0000,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 384, .y = 150 },
|
||||
.color = 0xFF00FFAA,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 5, 5, 5, 5 },
|
||||
.{ 0, 5, 0, 0 },
|
||||
@@ -185,8 +305,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[5] = Brick{
|
||||
.rotating = false,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 160, .y = 250 },
|
||||
.color = 0xFFAF0000,
|
||||
.color = 0xFFAF0F0F,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 6, 6, 6, 0 },
|
||||
.{ 6, 0, 0, 0 },
|
||||
@@ -197,8 +318,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[6] = Brick{
|
||||
.rotating = false,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 192, .y = 350 },
|
||||
.color = 0xFF9F0000,
|
||||
.color = 0xFF9FFF00,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 7, 7, 7, 0 },
|
||||
.{ 7, 7, 7, 0 },
|
||||
@@ -209,8 +331,9 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
|
||||
state.bricks[7] = Brick{
|
||||
.rotating = false,
|
||||
.rotating_time = 0,
|
||||
.pos = V2{ .x = 224, .y = 450 },
|
||||
.color = 0xFF8F0000,
|
||||
.color = 0xFF8F00FF,
|
||||
.tiles = [4][4]u32{
|
||||
.{ 8, 8, 8, 0 },
|
||||
.{ 8, 0, 8, 0 },
|
||||
@@ -225,14 +348,17 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
// Update
|
||||
const mouse_pos = V2{ .x = @floatCast(input.mouse_x), .y = @floatCast(input.mouse_y) };
|
||||
|
||||
const tile_size = V2{
|
||||
.x = TILE_SIZE,
|
||||
.y = TILE_SIZE,
|
||||
};
|
||||
const mouse_world_pos = mouse_pos.sub(state.camera.pos);
|
||||
|
||||
var mouse_on_brick = false;
|
||||
for (&state.bricks) |*brick| {
|
||||
brick.rotating = false;
|
||||
if (brick.rotating) {
|
||||
brick.rotating_time += input.delta_time;
|
||||
if (brick.rotating_time > 0.25) {
|
||||
brick.rotating = false;
|
||||
brick.rotating_time = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (brick.tiles, 0..) |row, i| {
|
||||
for (row, 0..) |tile, j| {
|
||||
@@ -249,11 +375,25 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
const diff = mouse_pos.sub(state.mouse_pos);
|
||||
brick.pos = brick.pos.add(diff);
|
||||
|
||||
if (input.key.space.down and !brick.rotating) {
|
||||
// TODO: Rotate
|
||||
if (keyPressed(&input.key.space)) {
|
||||
brick.rotating = true;
|
||||
const rotated = brick.rotate();
|
||||
brick.tiles = rotated;
|
||||
|
||||
const bbox = findBrickBoundingBox(brick);
|
||||
const center = brick.pos.sub(bbox.center);
|
||||
brick.pos = mouse_world_pos.add(center);
|
||||
}
|
||||
|
||||
// Flip
|
||||
if (keyPressed(&input.key.f)) {
|
||||
brick.rotating = true;
|
||||
const flipped = brick.transpose();
|
||||
brick.tiles = flipped;
|
||||
|
||||
const bbox = findBrickBoundingBox(brick);
|
||||
const center = brick.pos.sub(bbox.center);
|
||||
brick.pos = mouse_world_pos.add(center);
|
||||
}
|
||||
|
||||
mouse_on_brick = true;
|
||||
@@ -294,6 +434,8 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
}
|
||||
|
||||
for (state.bricks) |brick| {
|
||||
const bounding_box = findBrickBoundingBox(&brick);
|
||||
|
||||
for (brick.tiles, 0..) |row, i| {
|
||||
for (row, 0..) |tile, j| {
|
||||
if (tile > 0) {
|
||||
@@ -307,11 +449,34 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
||||
@intFromFloat(camera_pos.x),
|
||||
@intFromFloat(camera_pos.y),
|
||||
30,
|
||||
if (brick.rotating) 0xFF00FF00 else brick.color ,
|
||||
if (brick.rotating) 0xFF00FF00 else brick.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (comptime builtin.mode == std.builtin.OptimizeMode.Debug) {
|
||||
{
|
||||
const camera_pos = brick.pos.add(state.camera.pos);
|
||||
drawRect(buffer, camera_pos, .{ .x = 4 * TILE_SIZE, .y = 4 * TILE_SIZE }, 0xFFFF0000, 1);
|
||||
}
|
||||
|
||||
{
|
||||
const bbox_size = bounding_box.max.sub(bounding_box.min);
|
||||
const camera_pos = bounding_box.min.add(state.camera.pos);
|
||||
|
||||
drawRect(buffer, camera_pos, bbox_size, 0xFFFF0000, 1);
|
||||
}
|
||||
|
||||
{
|
||||
const size = 8;
|
||||
const half = @divTrunc(size, 2);
|
||||
const camera_pos = bounding_box.center.add(state.camera.pos);
|
||||
const x: i32 = @intFromFloat(camera_pos.x);
|
||||
const y: i32 = @intFromFloat(camera_pos.y);
|
||||
fillSquare(buffer, x - half, y - half, 8, 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.frame_count += 1;
|
||||
|
||||
Reference in New Issue
Block a user