rotateTowards, key store, camera faces Z-, CCW frontface
This commit is contained in:
68
src/data/keystore.zig
Normal file
68
src/data/keystore.zig
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
pub fn KeyStore(comptime key_type: type, comptime buf_size: usize, comptime zero: key_type) type {
|
||||||
|
return struct {
|
||||||
|
just_pressed: [buf_size]key_type = .{zero} ** buf_size,
|
||||||
|
pressed: [buf_size]key_type = .{zero} ** buf_size,
|
||||||
|
just_released: [buf_size]key_type = .{zero} ** buf_size,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
pub fn reset(self: *Self) void {
|
||||||
|
self.just_pressed = .{zero} ** buf_size;
|
||||||
|
self.just_released = .{zero} ** buf_size;
|
||||||
|
}
|
||||||
|
pub fn press(self: *Self, code: key_type) void {
|
||||||
|
for (self.pressed) |pressed| {
|
||||||
|
if (pressed == code) return;
|
||||||
|
}
|
||||||
|
for (&self.pressed) |*pressed| {
|
||||||
|
if (pressed.* == zero) {
|
||||||
|
pressed.* = code;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (self.just_pressed) |just_pressed| {
|
||||||
|
if (just_pressed == code) return;
|
||||||
|
}
|
||||||
|
for (&self.just_pressed) |*just_pressed| {
|
||||||
|
if (just_pressed.* == zero) {
|
||||||
|
just_pressed.* = code;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn release(self: *Self, code: key_type) void {
|
||||||
|
for (&self.pressed) |*pressed| {
|
||||||
|
if (pressed.* == code) {
|
||||||
|
pressed.* = zero;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (self.just_released) |just_released| {
|
||||||
|
if (just_released == code) return;
|
||||||
|
}
|
||||||
|
for (&self.just_released) |*just_released| {
|
||||||
|
if (just_released.* == zero) {
|
||||||
|
just_released.* = code;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_just_pressed(self: *Self, code: key_type) bool {
|
||||||
|
for (self.just_pressed) |just_pressed| {
|
||||||
|
if (just_pressed == code) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pub fn is_pressed(self: *Self, code: key_type) bool {
|
||||||
|
for (self.pressed) |pressed| {
|
||||||
|
if (pressed == code) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pub fn is_just_released(self: *Self, code: key_type) bool {
|
||||||
|
for (self.just_released) |just_released| {
|
||||||
|
if (just_released == code) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const sdl = @import("sdl");
|
const sdl = @import("sdl");
|
||||||
|
const Transform = @import("graphics/transform.zig");
|
||||||
const Controller = @import("graph/controller.zig");
|
const Controller = @import("graph/controller.zig");
|
||||||
const Graphics = @import("graphics.zig");
|
const Graphics = @import("graphics.zig");
|
||||||
const Game = @import("game.zig");
|
const Game = @import("game.zig");
|
||||||
@@ -70,35 +71,48 @@ pub fn init(controller: *Controller, graphics: *Graphics) !void {
|
|||||||
pub fn deinit() void {}
|
pub fn deinit() void {}
|
||||||
|
|
||||||
pub fn update(
|
pub fn update(
|
||||||
|
cube: *Cube,
|
||||||
mouse: *Game.Mouse,
|
mouse: *Game.Mouse,
|
||||||
keyboard: *Game.Keyboard,
|
keyboard: *Game.Keyboard,
|
||||||
graphics: *Graphics,
|
graphics: *Graphics,
|
||||||
time: *Game.Time,
|
time: *Game.Time,
|
||||||
) void {
|
) void {
|
||||||
if (keyboard.is_pressed(sdl.SCANCODE_W)) {
|
if (keyboard.keys.is_pressed(sdl.SCANCODE_W)) {
|
||||||
graphics.camera.transform.translateLocal(.{ 0.0, 0.0, 5.0 * time.delta });
|
|
||||||
}
|
|
||||||
if (keyboard.is_pressed(sdl.SCANCODE_S)) {
|
|
||||||
graphics.camera.transform.translateLocal(.{ 0.0, 0.0, -5.0 * time.delta });
|
graphics.camera.transform.translateLocal(.{ 0.0, 0.0, -5.0 * time.delta });
|
||||||
}
|
}
|
||||||
if (keyboard.is_pressed(sdl.SCANCODE_D)) {
|
if (keyboard.keys.is_pressed(sdl.SCANCODE_S)) {
|
||||||
|
graphics.camera.transform.translateLocal(.{ 0.0, 0.0, 5.0 * time.delta });
|
||||||
|
}
|
||||||
|
if (keyboard.keys.is_pressed(sdl.SCANCODE_D)) {
|
||||||
graphics.camera.transform.translateLocal(.{ 5.0 * time.delta, 0.0, 0.0 });
|
graphics.camera.transform.translateLocal(.{ 5.0 * time.delta, 0.0, 0.0 });
|
||||||
}
|
}
|
||||||
if (keyboard.is_pressed(sdl.SCANCODE_A)) {
|
if (keyboard.keys.is_pressed(sdl.SCANCODE_A)) {
|
||||||
graphics.camera.transform.translateLocal(.{ -5.0 * time.delta, 0.0, 0.0 });
|
graphics.camera.transform.translateLocal(.{ -5.0 * time.delta, 0.0, 0.0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@abs(mouse.dx) < 0.01 and @abs(mouse.dy) < 0.01) return;
|
if (mouse.buttons.is_pressed(sdl.BUTTON_LEFT)) {
|
||||||
|
const scale = 1.0 / @as(f32, @floatFromInt(graphics.window_size[1]));
|
||||||
|
cube.transform.position[0] += mouse.dx * scale * 4.0;
|
||||||
|
cube.transform.position[1] -= mouse.dy * scale * 4.0;
|
||||||
|
|
||||||
const delta, const length = Graphics.Transform.extractNormal(.{ mouse.dy, mouse.dx, 0.0 });
|
const ORIGIN_DIR = @Vector(3, f32){ 0.0, 0.0, -1.0 };
|
||||||
const rot = Graphics.Transform.rotationByAxis(
|
const INIT_ROTATION = Transform.rotationByAxis(.{ 1.0, 0.0, 0.0 }, std.math.pi * 0.5);
|
||||||
delta,
|
|
||||||
length * std.math.pi / @as(f32, @floatFromInt(graphics.window_size[1])) * 2.0,
|
const ROTATED_DIR = Transform.rotateVector(ORIGIN_DIR, INIT_ROTATION);
|
||||||
|
|
||||||
|
graphics.camera.transform.rotation = Transform.combineRotations(
|
||||||
|
INIT_ROTATION,
|
||||||
|
Transform.rotationToward(ROTATED_DIR, cube.transform.position - graphics.camera.transform.position, .{ .normalize_to = true }),
|
||||||
);
|
);
|
||||||
graphics.camera.transform.rotateLocal(rot);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(cube: *Cube, graphics: *Graphics) !void {
|
pub fn draw(cube: *Cube, graphics: *Graphics) !void {
|
||||||
|
try graphics.drawMesh(cube.mesh, Graphics.Transform{
|
||||||
|
.position = .{ 0.0, 0.0, 0.0 },
|
||||||
|
.rotation = cube.transform.rotation,
|
||||||
|
.scale = cube.transform.scale,
|
||||||
|
});
|
||||||
for (OFFSETS) |offset| {
|
for (OFFSETS) |offset| {
|
||||||
try graphics.drawMesh(cube.mesh, Graphics.Transform{
|
try graphics.drawMesh(cube.mesh, Graphics.Transform{
|
||||||
.position = cube.transform.position + offset,
|
.position = cube.transform.position + offset,
|
||||||
|
15
src/game.zig
15
src/game.zig
@@ -27,6 +27,7 @@ pub fn init(alloc: std.mem.Allocator) GameError!Self {
|
|||||||
var controller = try graph.getController();
|
var controller = try graph.getController();
|
||||||
controller.addResource(graphics);
|
controller.addResource(graphics);
|
||||||
controller.addResource(Mouse{
|
controller.addResource(Mouse{
|
||||||
|
.buttons = .{},
|
||||||
.x = 0.0,
|
.x = 0.0,
|
||||||
.y = 0.0,
|
.y = 0.0,
|
||||||
.dx = 0.0,
|
.dx = 0.0,
|
||||||
@@ -106,7 +107,7 @@ fn processEvents(
|
|||||||
) GameError!void {
|
) GameError!void {
|
||||||
mouse.dx = 0.0;
|
mouse.dx = 0.0;
|
||||||
mouse.dy = 0.0;
|
mouse.dy = 0.0;
|
||||||
keyboard.reset();
|
keyboard.keys.reset();
|
||||||
|
|
||||||
sdl.PumpEvents();
|
sdl.PumpEvents();
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -131,11 +132,19 @@ fn processEvents(
|
|||||||
},
|
},
|
||||||
sdl.EVENT_KEY_DOWN => {
|
sdl.EVENT_KEY_DOWN => {
|
||||||
if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue;
|
if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue;
|
||||||
keyboard.press(event.key.scancode);
|
keyboard.keys.press(event.key.scancode);
|
||||||
},
|
},
|
||||||
sdl.EVENT_KEY_UP => {
|
sdl.EVENT_KEY_UP => {
|
||||||
if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue;
|
if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue;
|
||||||
keyboard.release(event.key.scancode);
|
keyboard.keys.release(event.key.scancode);
|
||||||
|
},
|
||||||
|
sdl.EVENT_MOUSE_BUTTON_DOWN => {
|
||||||
|
if (event.button.windowID != sdl.GetWindowID(graphics.window)) continue;
|
||||||
|
mouse.buttons.press(event.button.button);
|
||||||
|
},
|
||||||
|
sdl.EVENT_MOUSE_BUTTON_UP => {
|
||||||
|
if (event.button.windowID != sdl.GetWindowID(graphics.window)) continue;
|
||||||
|
mouse.buttons.release(event.button.button);
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
@@ -189,7 +189,7 @@ pub fn create() GameError!Self {
|
|||||||
|
|
||||||
.camera = Camera{
|
.camera = Camera{
|
||||||
.transform = Transform{
|
.transform = Transform{
|
||||||
.position = .{ 0.0, 0.0, -4.0 },
|
.position = .{ 0.0, 0.0, 4.0 },
|
||||||
},
|
},
|
||||||
.near = 1.0,
|
.near = 1.0,
|
||||||
.far = 1024.0,
|
.far = 1024.0,
|
||||||
|
@@ -17,8 +17,8 @@ pub fn matrix(camera: Camera) @Vector(16, f32) {
|
|||||||
const projection = @Vector(16, f32){
|
const projection = @Vector(16, f32){
|
||||||
xx, 0, 0, 0,
|
xx, 0, 0, 0,
|
||||||
0, yy, 0, 0,
|
0, yy, 0, 0,
|
||||||
0, 0, zz, wz,
|
0, 0, -zz, wz,
|
||||||
0, 0, 1, 0,
|
0, 0, -1, 0,
|
||||||
};
|
};
|
||||||
return Transform.multiplyMatrix(projection, camera.transform.inverseMatrix());
|
return Transform.multiplyMatrix(projection, camera.transform.inverseMatrix());
|
||||||
}
|
}
|
||||||
|
@@ -20,5 +20,10 @@ pub const DEPTH_ENABLED = sdl.GPUDepthStencilState{
|
|||||||
pub const RASTERIZER_CULL = sdl.GPURasterizerState{
|
pub const RASTERIZER_CULL = sdl.GPURasterizerState{
|
||||||
.cull_mode = sdl.GPU_CULLMODE_BACK,
|
.cull_mode = sdl.GPU_CULLMODE_BACK,
|
||||||
.fill_mode = sdl.GPU_FILLMODE_FILL,
|
.fill_mode = sdl.GPU_FILLMODE_FILL,
|
||||||
.front_face = sdl.GPU_FRONTFACE_CLOCKWISE,
|
.front_face = sdl.GPU_FRONTFACE_COUNTER_CLOCKWISE,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RASTERIZER_NO_CULL = sdl.GPURasterizerState{
|
||||||
|
.cull_mode = sdl.GPU_CULLMODE_NONE,
|
||||||
|
.fill_mode = sdl.GPU_FILLMODE_FILL,
|
||||||
};
|
};
|
||||||
|
@@ -58,10 +58,16 @@ pub fn translate(transform: *Transform, translation: Position) void {
|
|||||||
pub fn translateLocal(transform: *Transform, translation: Position) void {
|
pub fn translateLocal(transform: *Transform, translation: Position) void {
|
||||||
@setFloatMode(.optimized);
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
const a = transform.rotation[0];
|
transform.position += rotateVector(translation, transform.rotation);
|
||||||
const b = transform.rotation[1];
|
}
|
||||||
const c = transform.rotation[2];
|
|
||||||
const d = transform.rotation[3];
|
pub fn rotateVector(vector: Position, rotation: Rotation) Position {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
|
const a = rotation[0];
|
||||||
|
const b = rotation[1];
|
||||||
|
const c = rotation[2];
|
||||||
|
const d = rotation[3];
|
||||||
|
|
||||||
const s = 2.0 / (a * a + b * b + c * c + d * d);
|
const s = 2.0 / (a * a + b * b + c * c + d * d);
|
||||||
const bs = b * s;
|
const bs = b * s;
|
||||||
@@ -78,9 +84,11 @@ pub fn translateLocal(transform: *Transform, translation: Position) void {
|
|||||||
const cd = c * ds;
|
const cd = c * ds;
|
||||||
const dd = d * ds;
|
const dd = d * ds;
|
||||||
|
|
||||||
transform.position[0] += translation[0] * (1 - cc - dd) + translation[1] * (bc - ad) + translation[2] * (bd + ac);
|
return .{
|
||||||
transform.position[1] += translation[0] * (bc + ad) + translation[1] * (1 - bb - dd) + translation[2] * (cd - ab);
|
vector[0] * (1 - cc - dd) + vector[1] * (bc - ad) + vector[2] * (bd + ac),
|
||||||
transform.position[2] += translation[0] * (bd - ac) + translation[1] * (cd + ab) + translation[2] * (1 - bb - cc);
|
vector[0] * (bc + ad) + vector[1] * (1 - bb - dd) + vector[2] * (cd - ab),
|
||||||
|
vector[0] * (bd - ac) + vector[1] * (cd + ab) + vector[2] * (1 - bb - cc),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotate(transform: *Transform, rotation: Rotation) void {
|
pub fn rotate(transform: *Transform, rotation: Rotation) void {
|
||||||
@@ -95,11 +103,49 @@ pub fn rotateLocal(transform: *Transform, rotation: Rotation) void {
|
|||||||
transform.rotation = normalizeRotation(combineRotations(rotation, transform.rotation));
|
transform.rotation = normalizeRotation(combineRotations(rotation, transform.rotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rotateByAxis(transform: *Transform, axis: Position, angle: f32) void {
|
||||||
|
transform.rotate(rotationByAxis(axis, angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotateToward(transform: *Transform, target: Position, origin_norm: Position) void {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
|
transform.rotation = rotationToward(origin_norm, target - transform.position, .{ .normalize_to = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const RotationTowardOptions = struct {
|
||||||
|
normalize_from: bool = false,
|
||||||
|
normalize_to: bool = false,
|
||||||
|
};
|
||||||
|
pub fn rotationToward(from: Position, to: Position, comptime options: RotationTowardOptions) Rotation {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
|
const from_norm = if (options.normalize_from) extractNormal(from)[0] else from;
|
||||||
|
const to_norm = if (options.normalize_to) extractNormal(to)[0] else to;
|
||||||
|
|
||||||
|
const combined = combineRotations(.{
|
||||||
|
0.0, to_norm[0], to_norm[1], to_norm[2],
|
||||||
|
}, .{
|
||||||
|
0.0, from_norm[0], from_norm[1], from_norm[2],
|
||||||
|
});
|
||||||
|
return normalizeRotation(.{
|
||||||
|
1 - combined[0],
|
||||||
|
combined[1],
|
||||||
|
combined[2],
|
||||||
|
combined[3],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn normalizeRotation(r: Rotation) Rotation {
|
pub fn normalizeRotation(r: Rotation) Rotation {
|
||||||
@setFloatMode(.optimized);
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
const length_inverse = 1.0 / @sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]);
|
const length = @sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]);
|
||||||
|
if (length <= 1e-15) {
|
||||||
|
return .{ 1.0, 0.0, 0.0, 0.0 };
|
||||||
|
} else {
|
||||||
|
const length_inverse = 1.0 / length;
|
||||||
return r * @as(Rotation, @splat(length_inverse));
|
return r * @as(Rotation, @splat(length_inverse));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn combineRotations(a: Rotation, b: Rotation) Rotation {
|
pub fn combineRotations(a: Rotation, b: Rotation) Rotation {
|
||||||
@@ -113,11 +159,11 @@ pub fn combineRotations(a: Rotation, b: Rotation) Rotation {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotationByAxis(axis: Position, rotation: f32) Rotation {
|
pub fn rotationByAxis(axis: Position, angle: f32) Rotation {
|
||||||
@setFloatMode(.optimized);
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
const cos = std.math.cos(rotation * 0.5);
|
const cos = std.math.cos(angle * 0.5);
|
||||||
const sin = std.math.sin(rotation * 0.5);
|
const sin = std.math.sin(angle * 0.5);
|
||||||
|
|
||||||
return .{ cos, sin * axis[0], sin * axis[1], sin * axis[2] };
|
return .{ cos, sin * axis[0], sin * axis[1], sin * axis[2] };
|
||||||
}
|
}
|
||||||
|
@@ -1,69 +1,4 @@
|
|||||||
const sdl = @import("sdl");
|
const sdl = @import("sdl");
|
||||||
|
const key_store = @import("data/keystore.zig");
|
||||||
|
|
||||||
const BUFFER_SIZE = 16;
|
keys: key_store.KeyStore(sdl.Scancode, 16, sdl.SCANCODE_UNKNOWN) = .{},
|
||||||
const ZERO = sdl.SCANCODE_UNKNOWN;
|
|
||||||
|
|
||||||
just_pressed: [BUFFER_SIZE]sdl.Scancode = .{ZERO} ** BUFFER_SIZE,
|
|
||||||
pressed: [BUFFER_SIZE]sdl.Scancode = .{ZERO} ** BUFFER_SIZE,
|
|
||||||
just_released: [BUFFER_SIZE]sdl.Scancode = .{ZERO} ** BUFFER_SIZE,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
pub fn reset(self: *Self) void {
|
|
||||||
self.just_pressed = .{ZERO} ** BUFFER_SIZE;
|
|
||||||
self.just_released = .{ZERO} ** BUFFER_SIZE;
|
|
||||||
}
|
|
||||||
pub fn press(self: *Self, code: sdl.Scancode) void {
|
|
||||||
for (self.pressed) |pressed| {
|
|
||||||
if (pressed == code) return;
|
|
||||||
}
|
|
||||||
for (&self.pressed) |*pressed| {
|
|
||||||
if (pressed.* == ZERO) {
|
|
||||||
pressed.* = code;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (self.just_pressed) |just_pressed| {
|
|
||||||
if (just_pressed == code) return;
|
|
||||||
}
|
|
||||||
for (&self.just_pressed) |*just_pressed| {
|
|
||||||
if (just_pressed.* == ZERO) {
|
|
||||||
just_pressed.* = code;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn release(self: *Self, code: sdl.Scancode) void {
|
|
||||||
for (&self.pressed) |*pressed| {
|
|
||||||
if (pressed.* == code) {
|
|
||||||
pressed.* = ZERO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (self.just_released) |just_released| {
|
|
||||||
if (just_released == code) return;
|
|
||||||
}
|
|
||||||
for (&self.just_released) |*just_released| {
|
|
||||||
if (just_released.* == ZERO) {
|
|
||||||
just_released.* = code;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_just_pressed(self: *Self, code: sdl.Scancode) bool {
|
|
||||||
for (self.just_pressed) |just_pressed| {
|
|
||||||
if (just_pressed == code) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pub fn is_pressed(self: *Self, code: sdl.Scancode) bool {
|
|
||||||
for (self.pressed) |pressed| {
|
|
||||||
if (pressed == code) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pub fn is_just_released(self: *Self, code: sdl.Scancode) bool {
|
|
||||||
for (self.just_released) |just_released| {
|
|
||||||
if (just_released == code) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
@@ -1,3 +1,8 @@
|
|||||||
|
const sdl = @import("sdl");
|
||||||
|
const key_store = @import("data/keystore.zig");
|
||||||
|
|
||||||
|
buttons: key_store.KeyStore(@TypeOf(sdl.BUTTON_LEFT), 4, 0),
|
||||||
|
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
dx: f32,
|
dx: f32,
|
||||||
|
Reference in New Issue
Block a user