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 sdl = @import("sdl");
|
||||
const Transform = @import("graphics/transform.zig");
|
||||
const Controller = @import("graph/controller.zig");
|
||||
const Graphics = @import("graphics.zig");
|
||||
const Game = @import("game.zig");
|
||||
@@ -70,35 +71,48 @@ pub fn init(controller: *Controller, graphics: *Graphics) !void {
|
||||
pub fn deinit() void {}
|
||||
|
||||
pub fn update(
|
||||
cube: *Cube,
|
||||
mouse: *Game.Mouse,
|
||||
keyboard: *Game.Keyboard,
|
||||
graphics: *Graphics,
|
||||
time: *Game.Time,
|
||||
) void {
|
||||
if (keyboard.is_pressed(sdl.SCANCODE_W)) {
|
||||
graphics.camera.transform.translateLocal(.{ 0.0, 0.0, 5.0 * time.delta });
|
||||
}
|
||||
if (keyboard.is_pressed(sdl.SCANCODE_S)) {
|
||||
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_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 });
|
||||
}
|
||||
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 });
|
||||
}
|
||||
|
||||
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 rot = Graphics.Transform.rotationByAxis(
|
||||
delta,
|
||||
length * std.math.pi / @as(f32, @floatFromInt(graphics.window_size[1])) * 2.0,
|
||||
);
|
||||
graphics.camera.transform.rotateLocal(rot);
|
||||
const ORIGIN_DIR = @Vector(3, f32){ 0.0, 0.0, -1.0 };
|
||||
const INIT_ROTATION = Transform.rotationByAxis(.{ 1.0, 0.0, 0.0 }, std.math.pi * 0.5);
|
||||
|
||||
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 }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
try graphics.drawMesh(cube.mesh, Graphics.Transform{
|
||||
.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();
|
||||
controller.addResource(graphics);
|
||||
controller.addResource(Mouse{
|
||||
.buttons = .{},
|
||||
.x = 0.0,
|
||||
.y = 0.0,
|
||||
.dx = 0.0,
|
||||
@@ -106,7 +107,7 @@ fn processEvents(
|
||||
) GameError!void {
|
||||
mouse.dx = 0.0;
|
||||
mouse.dy = 0.0;
|
||||
keyboard.reset();
|
||||
keyboard.keys.reset();
|
||||
|
||||
sdl.PumpEvents();
|
||||
while (true) {
|
||||
@@ -131,11 +132,19 @@ fn processEvents(
|
||||
},
|
||||
sdl.EVENT_KEY_DOWN => {
|
||||
if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue;
|
||||
keyboard.press(event.key.scancode);
|
||||
keyboard.keys.press(event.key.scancode);
|
||||
},
|
||||
sdl.EVENT_KEY_UP => {
|
||||
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 => {},
|
||||
}
|
||||
|
@@ -189,7 +189,7 @@ pub fn create() GameError!Self {
|
||||
|
||||
.camera = Camera{
|
||||
.transform = Transform{
|
||||
.position = .{ 0.0, 0.0, -4.0 },
|
||||
.position = .{ 0.0, 0.0, 4.0 },
|
||||
},
|
||||
.near = 1.0,
|
||||
.far = 1024.0,
|
||||
|
@@ -15,10 +15,10 @@ pub fn matrix(camera: Camera) @Vector(16, f32) {
|
||||
const zz = camera.far * fnmod;
|
||||
const wz = -camera.near * camera.far * fnmod;
|
||||
const projection = @Vector(16, f32){
|
||||
xx, 0, 0, 0,
|
||||
0, yy, 0, 0,
|
||||
0, 0, zz, wz,
|
||||
0, 0, 1, 0,
|
||||
xx, 0, 0, 0,
|
||||
0, yy, 0, 0,
|
||||
0, 0, -zz, wz,
|
||||
0, 0, -1, 0,
|
||||
};
|
||||
return Transform.multiplyMatrix(projection, camera.transform.inverseMatrix());
|
||||
}
|
||||
|
@@ -20,5 +20,10 @@ pub const DEPTH_ENABLED = sdl.GPUDepthStencilState{
|
||||
pub const RASTERIZER_CULL = sdl.GPURasterizerState{
|
||||
.cull_mode = sdl.GPU_CULLMODE_BACK,
|
||||
.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 {
|
||||
@setFloatMode(.optimized);
|
||||
|
||||
const a = transform.rotation[0];
|
||||
const b = transform.rotation[1];
|
||||
const c = transform.rotation[2];
|
||||
const d = transform.rotation[3];
|
||||
transform.position += rotateVector(translation, transform.rotation);
|
||||
}
|
||||
|
||||
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 bs = b * s;
|
||||
@@ -78,9 +84,11 @@ pub fn translateLocal(transform: *Transform, translation: Position) void {
|
||||
const cd = c * ds;
|
||||
const dd = d * ds;
|
||||
|
||||
transform.position[0] += translation[0] * (1 - cc - dd) + translation[1] * (bc - ad) + translation[2] * (bd + ac);
|
||||
transform.position[1] += translation[0] * (bc + ad) + translation[1] * (1 - bb - dd) + translation[2] * (cd - ab);
|
||||
transform.position[2] += translation[0] * (bd - ac) + translation[1] * (cd + ab) + translation[2] * (1 - bb - cc);
|
||||
return .{
|
||||
vector[0] * (1 - cc - dd) + vector[1] * (bc - ad) + vector[2] * (bd + ac),
|
||||
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 {
|
||||
@@ -95,11 +103,49 @@ pub fn rotateLocal(transform: *Transform, rotation: Rotation) void {
|
||||
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 {
|
||||
@setFloatMode(.optimized);
|
||||
|
||||
const length_inverse = 1.0 / @sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]);
|
||||
return r * @as(Rotation, @splat(length_inverse));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const cos = std.math.cos(rotation * 0.5);
|
||||
const sin = std.math.sin(rotation * 0.5);
|
||||
const cos = std.math.cos(angle * 0.5);
|
||||
const sin = std.math.sin(angle * 0.5);
|
||||
|
||||
return .{ cos, sin * axis[0], sin * axis[1], sin * axis[2] };
|
||||
}
|
||||
|
@@ -1,69 +1,4 @@
|
||||
const sdl = @import("sdl");
|
||||
const key_store = @import("data/keystore.zig");
|
||||
|
||||
const BUFFER_SIZE = 16;
|
||||
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;
|
||||
}
|
||||
keys: key_store.KeyStore(sdl.Scancode, 16, sdl.SCANCODE_UNKNOWN) = .{},
|
||||
|
@@ -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,
|
||||
y: f32,
|
||||
dx: f32,
|
||||
|
Reference in New Issue
Block a user