Initial commit for tabletop
We'll branch off of main game for a cool quick side project idea for a moment
This commit is contained in:
83
build.zig
83
build.zig
@@ -11,42 +11,93 @@ pub fn build(b: *Build) void {
|
|||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const exe = stepBuildMain(b, target, optimize);
|
const sdl_module, const sdl_step = stepSdlModule(b, target, optimize);
|
||||||
b.installArtifact(exe);
|
|
||||||
|
const client_exe = stepBuildClient(b, target, optimize, sdl_module, sdl_step);
|
||||||
|
const client_install = b.addInstallArtifact(client_exe, .{});
|
||||||
|
|
||||||
|
const server_exe = stepBuildServer(b, target, optimize);
|
||||||
|
const server_install = b.addInstallArtifact(server_exe, .{});
|
||||||
|
|
||||||
|
const offline_exe = stepBuildOffline(b, target, optimize, sdl_module, sdl_step);
|
||||||
|
const offline_install = b.addInstallArtifact(offline_exe, .{});
|
||||||
|
|
||||||
const copy_data = stepCopyData(b, target, optimize);
|
const copy_data = stepCopyData(b, target, optimize);
|
||||||
|
|
||||||
|
b.getInstallStep().dependOn(&client_install.step);
|
||||||
|
b.getInstallStep().dependOn(&server_install.step);
|
||||||
|
b.getInstallStep().dependOn(&offline_install.step);
|
||||||
b.getInstallStep().dependOn(copy_data);
|
b.getInstallStep().dependOn(copy_data);
|
||||||
|
|
||||||
const run = b.addRunArtifact(exe);
|
const run = b.addRunArtifact(offline_exe);
|
||||||
run.step.dependOn(b.getInstallStep());
|
run.step.dependOn(&offline_install.step);
|
||||||
|
run.step.dependOn(copy_data);
|
||||||
|
|
||||||
// Why is this not the default behavoir?
|
// Why is this not the default behavior?
|
||||||
run.setCwd(b.path(std.fs.path.relative(b.allocator, b.build_root.path.?, b.exe_dir) catch unreachable));
|
run.setCwd(b.path(std.fs.path.relative(b.allocator, b.build_root.path.?, b.exe_dir) catch unreachable));
|
||||||
|
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
run.addArgs(args);
|
run.addArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
const run_step = b.step("run", "Run the app");
|
const run_step = b.step("run", "Build and Run tabletop in offline mode");
|
||||||
run_step.dependOn(&run.step);
|
run_step.dependOn(&run.step);
|
||||||
|
|
||||||
const check_step = b.step("check", "Check for build errors");
|
const check_step = b.step("check", "Check for build errors (offline build only)");
|
||||||
check_step.dependOn(&exe.step);
|
check_step.dependOn(&offline_exe.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stepBuildMain(
|
fn stepBuildClient(
|
||||||
|
b: *Build,
|
||||||
|
target: Build.ResolvedTarget,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
sdl_module: *Build.Module,
|
||||||
|
sdl_step: *Build.Step,
|
||||||
|
) *Build.Step.Compile {
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "tabletop_client",
|
||||||
|
.root_source_file = b.path("src/client.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.root_module.addImport("sdl", sdl_module);
|
||||||
|
exe.step.dependOn(sdl_step);
|
||||||
|
|
||||||
|
exe.addIncludePath(b.path("lib/clibs"));
|
||||||
|
|
||||||
|
return exe;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stepBuildServer(
|
||||||
b: *Build,
|
b: *Build,
|
||||||
target: Build.ResolvedTarget,
|
target: Build.ResolvedTarget,
|
||||||
optimize: std.builtin.OptimizeMode,
|
optimize: std.builtin.OptimizeMode,
|
||||||
) *Build.Step.Compile {
|
) *Build.Step.Compile {
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "spacefarer",
|
.name = "tabletop_server",
|
||||||
.root_source_file = b.path("src/main.zig"),
|
.root_source_file = b.path("src/server.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
return exe;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stepBuildOffline(
|
||||||
|
b: *Build,
|
||||||
|
target: Build.ResolvedTarget,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
sdl_module: *Build.Module,
|
||||||
|
sdl_step: *Build.Step,
|
||||||
|
) *Build.Step.Compile {
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "tabletop",
|
||||||
|
.root_source_file = b.path("src/offline.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const sdl_module, const sdl_step = stepSdlModule(b, target, optimize);
|
|
||||||
exe.root_module.addImport("sdl", sdl_module);
|
exe.root_module.addImport("sdl", sdl_module);
|
||||||
exe.step.dependOn(sdl_step);
|
exe.step.dependOn(sdl_step);
|
||||||
|
|
||||||
@@ -58,13 +109,12 @@ fn stepBuildMain(
|
|||||||
fn stepBuildSdlTranslator(
|
fn stepBuildSdlTranslator(
|
||||||
b: *Build,
|
b: *Build,
|
||||||
target: Build.ResolvedTarget,
|
target: Build.ResolvedTarget,
|
||||||
optimize: std.builtin.OptimizeMode,
|
|
||||||
) *Build.Step.Compile {
|
) *Build.Step.Compile {
|
||||||
const sdl_translator = b.addExecutable(.{
|
const sdl_translator = b.addExecutable(.{
|
||||||
.name = "sdl_header_translator",
|
.name = "sdl_header_translator",
|
||||||
.root_source_file = b.path("utils/sdl_translator.zig"),
|
.root_source_file = b.path("utils/sdl_translator.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = .Debug,
|
||||||
});
|
});
|
||||||
sdl_translator.linkSystemLibrary("SDL3");
|
sdl_translator.linkSystemLibrary("SDL3");
|
||||||
return sdl_translator;
|
return sdl_translator;
|
||||||
@@ -73,9 +123,8 @@ fn stepBuildSdlTranslator(
|
|||||||
fn stepTranslateSdl(
|
fn stepTranslateSdl(
|
||||||
b: *Build,
|
b: *Build,
|
||||||
target: Build.ResolvedTarget,
|
target: Build.ResolvedTarget,
|
||||||
optimize: std.builtin.OptimizeMode,
|
|
||||||
) struct { *Build.Step, Build.LazyPath } {
|
) struct { *Build.Step, Build.LazyPath } {
|
||||||
const sdl_translator = stepBuildSdlTranslator(b, target, optimize);
|
const sdl_translator = stepBuildSdlTranslator(b, target);
|
||||||
const translate = b.addRunArtifact(sdl_translator);
|
const translate = b.addRunArtifact(sdl_translator);
|
||||||
const sdl_rename = translate.addOutputFileArg("sdl_rename.h");
|
const sdl_rename = translate.addOutputFileArg("sdl_rename.h");
|
||||||
return .{
|
return .{
|
||||||
@@ -97,7 +146,7 @@ fn stepSdlModule(
|
|||||||
});
|
});
|
||||||
sdl_module.linkSystemLibrary("SDL3", .{});
|
sdl_module.linkSystemLibrary("SDL3", .{});
|
||||||
|
|
||||||
const translate_step, const sdl_rename = stepTranslateSdl(b, target, optimize);
|
const translate_step, const sdl_rename = stepTranslateSdl(b, target);
|
||||||
sdl_module.addIncludePath(sdl_rename.dirname());
|
sdl_module.addIncludePath(sdl_rename.dirname());
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
.{
|
.{
|
||||||
.name = .spacefarer,
|
.name = .tabletop,
|
||||||
.version = "0.0.0",
|
.version = "0.0.0",
|
||||||
.fingerprint = 0x946ddccb5911fb15,
|
.fingerprint = 0x9467f2a6727b4ca5,
|
||||||
.minimum_zig_version = "0.15.0",
|
.minimum_zig_version = "0.14.0",
|
||||||
.dependencies = .{},
|
.dependencies = .{},
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"build.zig",
|
"build.zig",
|
||||||
|
BIN
data/hand.png
Normal file
BIN
data/hand.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 353 B |
@@ -7,4 +7,7 @@ layout(set = 2, binding = 0) uniform sampler2D texture_sampler;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
fragColor = texture(texture_sampler, inUV);
|
fragColor = texture(texture_sampler, inUV);
|
||||||
|
if (fragColor.a < 0.5) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,5 +13,6 @@ layout(set = 1, binding = 1) uniform Object{
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(inCoord, 1.0) * object.transform * camera.transform;
|
gl_Position = vec4(inCoord, 1.0) * object.transform * camera.transform;
|
||||||
|
gl_ClipDistance[0] = gl_Position.z;
|
||||||
outUV = inUV;
|
outUV = inUV;
|
||||||
}
|
}
|
||||||
|
BIN
data/yakuza.png
Normal file
BIN
data/yakuza.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
123
src/entity.zig
123
src/entity.zig
@@ -1,123 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const sdl = @import("sdl");
|
|
||||||
const Game = @import("game.zig");
|
|
||||||
const Graphics = @import("graphics.zig");
|
|
||||||
const Time = @import("time.zig");
|
|
||||||
const World = @import("world.zig");
|
|
||||||
const math = @import("math.zig");
|
|
||||||
|
|
||||||
position: @Vector(2, i32),
|
|
||||||
player: bool = false,
|
|
||||||
enemy: bool = false,
|
|
||||||
controller: Controller = .{},
|
|
||||||
next_update: Time = Time.ZERO,
|
|
||||||
sway_x: math.Sway(f32) = .{ .amplitude = 1, .frequency = 0.11 },
|
|
||||||
sway_y: math.Sway(f32) = .{ .amplitude = 1, .frequency = 0.13 },
|
|
||||||
|
|
||||||
const Controller = struct {
|
|
||||||
const Action = union(enum) {
|
|
||||||
move: @Vector(2, i32),
|
|
||||||
};
|
|
||||||
wanted_action: ?Action = null,
|
|
||||||
move_units: f32 = 0.125,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
pub fn update(self: *Self) void {
|
|
||||||
if (!World.time.past(self.next_update)) return;
|
|
||||||
|
|
||||||
if (self.player) self.updatePlayer();
|
|
||||||
if (self.enemy) self.updateEnemy();
|
|
||||||
self.updateController();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn updatePlayer(self: *Self) void {
|
|
||||||
var delta: @Vector(2, i32) = .{ 0, 0 };
|
|
||||||
if (Game.keyboard.keys.is_pressed(sdl.SCANCODE_UP)) {
|
|
||||||
delta[1] += 1;
|
|
||||||
}
|
|
||||||
if (Game.keyboard.keys.is_pressed(sdl.SCANCODE_DOWN)) {
|
|
||||||
delta[1] -= 1;
|
|
||||||
}
|
|
||||||
if (Game.keyboard.keys.is_pressed(sdl.SCANCODE_RIGHT)) {
|
|
||||||
delta[0] += 1;
|
|
||||||
}
|
|
||||||
if (Game.keyboard.keys.is_pressed(sdl.SCANCODE_LEFT)) {
|
|
||||||
delta[0] -= 1;
|
|
||||||
}
|
|
||||||
if (@reduce(.Or, delta != @Vector(2, i32){ 0, 0 }))
|
|
||||||
self.controller.wanted_action = .{ .move = delta }
|
|
||||||
else
|
|
||||||
self.controller.wanted_action = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn updateEnemy(self: *Self) void {
|
|
||||||
if (World.getPlayer()) |player| {
|
|
||||||
var delta = player.position - self.position;
|
|
||||||
if (@reduce(.And, @abs(delta) <= @Vector(2, i64){ 1, 1 })) {
|
|
||||||
self.controller.wanted_action = null;
|
|
||||||
} else {
|
|
||||||
delta[0] = @max(-1, @min(1, delta[0]));
|
|
||||||
delta[1] = @max(-1, @min(1, delta[1]));
|
|
||||||
self.controller.wanted_action = .{ .move = delta };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn updateController(self: *Self) void {
|
|
||||||
if (self.controller.wanted_action) |action| {
|
|
||||||
switch (action) {
|
|
||||||
.move => |delta| {
|
|
||||||
const target = self.position + delta;
|
|
||||||
if (World.isFree(target)) {
|
|
||||||
self.next_update = World.time.offset(self.controller.move_units * math.lengthInt(delta));
|
|
||||||
self.position[0] += delta[0];
|
|
||||||
self.position[1] += delta[1];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(self: *Self, delta: f32) void {
|
|
||||||
self.sway_x.update(delta);
|
|
||||||
self.sway_y.update(delta);
|
|
||||||
const transform = Graphics.Transform{
|
|
||||||
.rotation = Graphics.Transform.rotationByAxis(.{ self.sway_x.value, self.sway_y.value, 0 }, 0.05),
|
|
||||||
.position = .{
|
|
||||||
@floatFromInt(self.position[0]),
|
|
||||||
@floatFromInt(self.position[1]),
|
|
||||||
0.5,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Graphics.drawMesh(World.plane_mesh, World.texture, transform.matrix());
|
|
||||||
|
|
||||||
if (!self.player) return;
|
|
||||||
|
|
||||||
Graphics.camera.transform.position = math.lerpTimeLn(
|
|
||||||
Graphics.camera.transform.position,
|
|
||||||
transform.position + @Vector(3, f32){ 0.0, -1.0, 4.0 },
|
|
||||||
delta,
|
|
||||||
-25,
|
|
||||||
);
|
|
||||||
|
|
||||||
const ORIGIN_DIR = @Vector(3, f32){ 0.0, 0.0, -1.0 };
|
|
||||||
const INIT_ROTATION = Graphics.Transform.rotationByAxis(.{ 1.0, 0.0, 0.0 }, std.math.pi * 0.5);
|
|
||||||
|
|
||||||
const ROTATED_DIR = Graphics.Transform.rotateVector(ORIGIN_DIR, INIT_ROTATION);
|
|
||||||
|
|
||||||
const target_rotation = Graphics.Transform.combineRotations(
|
|
||||||
INIT_ROTATION,
|
|
||||||
Graphics.Transform.rotationToward(
|
|
||||||
ROTATED_DIR,
|
|
||||||
transform.position - Graphics.camera.transform.position,
|
|
||||||
.{ .normalize_to = true },
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Graphics.camera.transform.rotation = Graphics.Transform.normalizeRotation(math.slerpTimeLn(
|
|
||||||
Graphics.camera.transform.rotation,
|
|
||||||
target_rotation,
|
|
||||||
delta,
|
|
||||||
-2,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
25
src/game.zig
25
src/game.zig
@@ -27,7 +27,16 @@ pub fn init(game_alloc: std.mem.Allocator) void {
|
|||||||
Game.running = false;
|
Game.running = false;
|
||||||
Game.time = Time{ .now = 0, .delta = 0 };
|
Game.time = Time{ .now = 0, .delta = 0 };
|
||||||
Game.keyboard = .{};
|
Game.keyboard = .{};
|
||||||
Game.mouse = .{ .x = 0, .y = 0, .dx = 0, .dy = 0 };
|
Game.mouse = .{
|
||||||
|
.buttons = .{},
|
||||||
|
.x_screen = 0,
|
||||||
|
.y_screen = 0,
|
||||||
|
.x_norm = 0,
|
||||||
|
.y_norm = 0,
|
||||||
|
.dx = 0,
|
||||||
|
.dy = 0,
|
||||||
|
.wheel = 0,
|
||||||
|
};
|
||||||
Graphics.create();
|
Graphics.create();
|
||||||
Assets.init();
|
Assets.init();
|
||||||
World.initDebug();
|
World.initDebug();
|
||||||
@@ -46,9 +55,11 @@ pub fn run() void {
|
|||||||
} else err.sdl();
|
} else err.sdl();
|
||||||
|
|
||||||
Game.processEvents();
|
Game.processEvents();
|
||||||
World.updateReal(Game.time.delta);
|
Game.mouse.x_norm = (Game.mouse.x_screen / @as(f32, @floatFromInt(Graphics.getWidth()))) * 2 - 1;
|
||||||
|
Game.mouse.y_norm = (Game.mouse.y_screen / @as(f32, @floatFromInt(Graphics.getHeight()))) * -2 + 1;
|
||||||
|
World.update(Game.time.delta);
|
||||||
if (Game.beginDraw()) {
|
if (Game.beginDraw()) {
|
||||||
World.draw(Game.time.delta);
|
World.draw();
|
||||||
Game.endDraw();
|
Game.endDraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,6 +77,7 @@ fn processEvents() void {
|
|||||||
Game.mouse.dx = 0.0;
|
Game.mouse.dx = 0.0;
|
||||||
Game.mouse.dy = 0.0;
|
Game.mouse.dy = 0.0;
|
||||||
Game.keyboard.keys.reset();
|
Game.keyboard.keys.reset();
|
||||||
|
Game.mouse.reset();
|
||||||
|
|
||||||
sdl.PumpEvents();
|
sdl.PumpEvents();
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -83,8 +95,8 @@ fn processEvents() void {
|
|||||||
},
|
},
|
||||||
sdl.EVENT_MOUSE_MOTION => {
|
sdl.EVENT_MOUSE_MOTION => {
|
||||||
if (event.motion.windowID != Graphics.windowId()) continue;
|
if (event.motion.windowID != Graphics.windowId()) continue;
|
||||||
Game.mouse.x = event.motion.x;
|
Game.mouse.x_screen = event.motion.x;
|
||||||
Game.mouse.y = event.motion.y;
|
Game.mouse.y_screen = event.motion.y;
|
||||||
Game.mouse.dx += event.motion.xrel;
|
Game.mouse.dx += event.motion.xrel;
|
||||||
Game.mouse.dy += event.motion.yrel;
|
Game.mouse.dy += event.motion.yrel;
|
||||||
},
|
},
|
||||||
@@ -104,6 +116,9 @@ fn processEvents() void {
|
|||||||
if (event.button.windowID != Graphics.windowId()) continue;
|
if (event.button.windowID != Graphics.windowId()) continue;
|
||||||
Game.mouse.buttons.release(event.button.button);
|
Game.mouse.buttons.release(event.button.button);
|
||||||
},
|
},
|
||||||
|
sdl.EVENT_MOUSE_WHEEL => {
|
||||||
|
Game.mouse.wheel += event.wheel.integer_y;
|
||||||
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
166
src/graphics.zig
166
src/graphics.zig
@@ -13,11 +13,11 @@ pub const Mesh = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var window: *sdl.Window = undefined;
|
var window: *sdl.Window = undefined;
|
||||||
var renderer: *sdl.Renderer = undefined;
|
|
||||||
var device: *sdl.GPUDevice = undefined;
|
var device: *sdl.GPUDevice = undefined;
|
||||||
/// Only available while drawing
|
/// Only available while drawing
|
||||||
var command_buffer: ?*sdl.GPUCommandBuffer = null;
|
var command_buffer: ?*sdl.GPUCommandBuffer = null;
|
||||||
var render_pass: ?*sdl.GPURenderPass = null;
|
var render_pass: ?*sdl.GPURenderPass = null;
|
||||||
|
var render_target: ?*sdl.GPUTexture = null;
|
||||||
|
|
||||||
var shader_vert: *sdl.GPUShader = undefined;
|
var shader_vert: *sdl.GPUShader = undefined;
|
||||||
var shader_frag: *sdl.GPUShader = undefined;
|
var shader_frag: *sdl.GPUShader = undefined;
|
||||||
@@ -30,10 +30,12 @@ var transfer_buffer: *sdl.GPUTransferBuffer = undefined;
|
|||||||
var transfer_buffer_capacity: usize = undefined;
|
var transfer_buffer_capacity: usize = undefined;
|
||||||
|
|
||||||
var depth_texture: *sdl.GPUTexture = undefined;
|
var depth_texture: *sdl.GPUTexture = undefined;
|
||||||
var msaa_resolve: *sdl.GPUTexture = undefined;
|
var fsaa_target: *sdl.GPUTexture = undefined;
|
||||||
var pipeline: *sdl.GPUGraphicsPipeline = undefined;
|
var pipeline: *sdl.GPUGraphicsPipeline = undefined;
|
||||||
|
|
||||||
var window_size: [2]u32 = undefined;
|
var window_size: [2]u32 = undefined;
|
||||||
|
var fsaa_scale: u32 = 4;
|
||||||
|
var fsaa_level: u32 = 3;
|
||||||
|
|
||||||
pub var camera: Camera = undefined;
|
pub var camera: Camera = undefined;
|
||||||
|
|
||||||
@@ -41,27 +43,27 @@ var to_resize: ?[2]u32 = null;
|
|||||||
|
|
||||||
const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024;
|
const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024;
|
||||||
const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2;
|
const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2;
|
||||||
const TRANSFER_BUFFER_DEFAULT_CAPACITY = 4096;
|
const TRANSFER_BUFFER_DEFAULT_CAPACITY = 4096 * 1024;
|
||||||
const BYTES_PER_VERTEX = 5 * 4;
|
const BYTES_PER_VERTEX = 5 * 4;
|
||||||
|
const DEPTH_FORMAT = sdl.GPU_TEXTUREFORMAT_D32_FLOAT;
|
||||||
|
const MIP_LEVEL = 4;
|
||||||
|
|
||||||
const Graphics = @This();
|
const Graphics = @This();
|
||||||
pub fn create() void {
|
pub fn create() void {
|
||||||
// Init
|
// Init
|
||||||
if (!sdl.Init(sdl.INIT_VIDEO | sdl.INIT_EVENTS)) err.sdl();
|
if (!sdl.Init(sdl.INIT_VIDEO | sdl.INIT_EVENTS)) err.sdl();
|
||||||
|
if (!sdl.SetHint(sdl.HINT_LOGGING, "*=info")) err.sdl();
|
||||||
|
if (!sdl.SetHint(sdl.HINT_GPU_DRIVER, "vulkan")) err.sdl();
|
||||||
|
|
||||||
// Window and Renderer
|
// Window and Renderer
|
||||||
if (!sdl.CreateWindowAndRenderer(
|
Graphics.window = sdl.CreateWindow(
|
||||||
"",
|
"",
|
||||||
1600,
|
1600,
|
||||||
900,
|
900,
|
||||||
sdl.WINDOW_VULKAN | sdl.WINDOW_RESIZABLE,
|
sdl.WINDOW_VULKAN | sdl.WINDOW_RESIZABLE,
|
||||||
@ptrCast(&Graphics.window),
|
) orelse err.sdl();
|
||||||
@ptrCast(&Graphics.renderer),
|
|
||||||
)) err.sdl();
|
|
||||||
Graphics.window_size = .{ 1600, 900 };
|
Graphics.window_size = .{ 1600, 900 };
|
||||||
|
|
||||||
if (!sdl.SetRenderVSync(renderer, sdl.RENDERER_VSYNC_ADAPTIVE)) err.sdl();
|
|
||||||
|
|
||||||
// Device
|
// Device
|
||||||
Graphics.device = sdl.CreateGPUDevice(
|
Graphics.device = sdl.CreateGPUDevice(
|
||||||
sdl.GPU_SHADERFORMAT_SPIRV,
|
sdl.GPU_SHADERFORMAT_SPIRV,
|
||||||
@@ -113,8 +115,20 @@ pub fn create() void {
|
|||||||
var window_height: c_int = 1;
|
var window_height: c_int = 1;
|
||||||
if (!sdl.GetWindowSizeInPixels(Graphics.window, &window_width, &window_height)) err.sdl();
|
if (!sdl.GetWindowSizeInPixels(Graphics.window, &window_width, &window_height)) err.sdl();
|
||||||
|
|
||||||
Graphics.depth_texture = createDepthTexture(@intCast(window_width), @intCast(window_height));
|
Graphics.depth_texture = createTexture(
|
||||||
Graphics.msaa_resolve = createTexture(@intCast(window_width), @intCast(window_height), target_format);
|
@as(u32, @intCast(window_width)) * Graphics.fsaa_scale,
|
||||||
|
@as(u32, @intCast(window_height)) * Graphics.fsaa_scale,
|
||||||
|
DEPTH_FORMAT,
|
||||||
|
sdl.GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
Graphics.fsaa_target = createTexture(
|
||||||
|
@as(u32, @intCast(window_width)) * Graphics.fsaa_scale,
|
||||||
|
@as(u32, @intCast(window_height)) * Graphics.fsaa_scale,
|
||||||
|
target_format,
|
||||||
|
sdl.GPU_TEXTUREUSAGE_COLOR_TARGET | sdl.GPU_TEXTUREUSAGE_SAMPLER,
|
||||||
|
fsaa_level,
|
||||||
|
);
|
||||||
|
|
||||||
Graphics.pipeline = sdl.CreateGPUGraphicsPipeline(Graphics.device, &.{
|
Graphics.pipeline = sdl.CreateGPUGraphicsPipeline(Graphics.device, &.{
|
||||||
.vertex_shader = Graphics.shader_vert,
|
.vertex_shader = Graphics.shader_vert,
|
||||||
@@ -144,12 +158,9 @@ pub fn create() void {
|
|||||||
},
|
},
|
||||||
.primitive_type = sdl.GPU_PRIMITIVETYPE_TRIANGLELIST,
|
.primitive_type = sdl.GPU_PRIMITIVETYPE_TRIANGLELIST,
|
||||||
.rasterizer_state = presets.RASTERIZER_CULL,
|
.rasterizer_state = presets.RASTERIZER_CULL,
|
||||||
.multisample_state = .{
|
|
||||||
.sample_count = sdl.GPU_SAMPLECOUNT_4,
|
|
||||||
},
|
|
||||||
.depth_stencil_state = presets.DEPTH_ENABLED,
|
.depth_stencil_state = presets.DEPTH_ENABLED,
|
||||||
.target_info = .{
|
.target_info = .{
|
||||||
.depth_stencil_format = sdl.GPU_TEXTUREFORMAT_D16_UNORM,
|
.depth_stencil_format = DEPTH_FORMAT,
|
||||||
.color_target_descriptions = &sdl.GPUColorTargetDescription{
|
.color_target_descriptions = &sdl.GPUColorTargetDescription{
|
||||||
.format = target_format,
|
.format = target_format,
|
||||||
.blend_state = presets.BLEND_NORMAL,
|
.blend_state = presets.BLEND_NORMAL,
|
||||||
@@ -161,20 +172,20 @@ pub fn create() void {
|
|||||||
|
|
||||||
Graphics.camera = Camera{
|
Graphics.camera = Camera{
|
||||||
.transform = .{},
|
.transform = .{},
|
||||||
.near = 1.0,
|
.near = 1.0 / 16.0,
|
||||||
.far = 1024.0,
|
.far = 128.0,
|
||||||
.lens = 1.5,
|
.lens = 1.5,
|
||||||
.aspect = 16.0 / 9.0,
|
.aspect = 16.0 / 9.0,
|
||||||
|
.matrix = undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy() void {
|
pub fn destroy() void {
|
||||||
sdl.ReleaseWindowFromGPUDevice(Graphics.device, Graphics.window);
|
sdl.ReleaseWindowFromGPUDevice(Graphics.device, Graphics.window);
|
||||||
sdl.DestroyRenderer(Graphics.renderer);
|
|
||||||
sdl.DestroyWindow(Graphics.window);
|
sdl.DestroyWindow(Graphics.window);
|
||||||
|
|
||||||
sdl.ReleaseGPUGraphicsPipeline(Graphics.device, Graphics.pipeline);
|
sdl.ReleaseGPUGraphicsPipeline(Graphics.device, Graphics.pipeline);
|
||||||
sdl.ReleaseGPUTexture(Graphics.device, Graphics.msaa_resolve);
|
sdl.ReleaseGPUTexture(Graphics.device, Graphics.fsaa_target);
|
||||||
sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture);
|
sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture);
|
||||||
sdl.ReleaseGPUBuffer(Graphics.device, Graphics.vertex_buffer);
|
sdl.ReleaseGPUBuffer(Graphics.device, Graphics.vertex_buffer);
|
||||||
sdl.ReleaseGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
sdl.ReleaseGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
||||||
@@ -190,25 +201,22 @@ pub fn destroy() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) struct { *sdl.GPUTexture, *sdl.GPUSampler } {
|
pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) struct { *sdl.GPUTexture, *sdl.GPUSampler } {
|
||||||
// const target_format = sdl.SDL_GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window);
|
|
||||||
const target_format = sdl.GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
const target_format = sdl.GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||||
|
|
||||||
const texture = sdl.CreateGPUTexture(Graphics.device, &sdl.GPUTextureCreateInfo{
|
const texture = Graphics.createTexture(
|
||||||
.format = target_format,
|
width,
|
||||||
.layer_count_or_depth = 1,
|
height,
|
||||||
.width = width,
|
target_format,
|
||||||
.height = height,
|
sdl.GPU_TEXTUREUSAGE_SAMPLER | sdl.GPU_TEXTUREUSAGE_COLOR_TARGET,
|
||||||
.num_levels = 1,
|
MIP_LEVEL,
|
||||||
.sample_count = sdl.GPU_SAMPLECOUNT_1,
|
);
|
||||||
.usage = sdl.GPU_TEXTUREUSAGE_SAMPLER,
|
|
||||||
}) orelse err.sdl();
|
|
||||||
|
|
||||||
const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl();
|
const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl();
|
||||||
{
|
{
|
||||||
const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer) orelse err.sdl();
|
const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer) orelse err.sdl();
|
||||||
defer sdl.EndGPUCopyPass(copy_pass);
|
defer sdl.EndGPUCopyPass(copy_pass);
|
||||||
|
|
||||||
const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, false) orelse err.sdl());
|
const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, true) orelse err.sdl());
|
||||||
@memcpy(map, texture_bytes);
|
@memcpy(map, texture_bytes);
|
||||||
sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
||||||
|
|
||||||
@@ -229,6 +237,7 @@ pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) struct {
|
|||||||
.d = 1,
|
.d = 1,
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
sdl.GenerateMipmapsForGPUTexture(temp_command_buffer, texture);
|
||||||
if (!sdl.SubmitGPUCommandBuffer(temp_command_buffer)) err.sdl();
|
if (!sdl.SubmitGPUCommandBuffer(temp_command_buffer)) err.sdl();
|
||||||
|
|
||||||
const sampler = sdl.CreateGPUSampler(Graphics.device, &sdl.GPUSamplerCreateInfo{
|
const sampler = sdl.CreateGPUSampler(Graphics.device, &sdl.GPUSamplerCreateInfo{
|
||||||
@@ -237,6 +246,10 @@ pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) struct {
|
|||||||
.address_mode_w = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
.address_mode_w = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
||||||
.mag_filter = sdl.GPU_FILTER_NEAREST,
|
.mag_filter = sdl.GPU_FILTER_NEAREST,
|
||||||
.min_filter = sdl.GPU_FILTER_LINEAR,
|
.min_filter = sdl.GPU_FILTER_LINEAR,
|
||||||
|
.mipmap_mode = sdl.GPU_SAMPLERMIPMAPMODE_LINEAR,
|
||||||
|
.min_lod = 0,
|
||||||
|
.max_lod = 16,
|
||||||
|
.mip_lod_bias = -2,
|
||||||
}) orelse err.sdl();
|
}) orelse err.sdl();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@@ -261,7 +274,7 @@ pub fn loadMesh(mesh_bytes: []const u8) Mesh {
|
|||||||
Graphics.growVertexBuffer(Graphics.vertex_buffer_capacity * size_mult);
|
Graphics.growVertexBuffer(Graphics.vertex_buffer_capacity * size_mult);
|
||||||
}
|
}
|
||||||
|
|
||||||
const map = sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, false) orelse err.sdl();
|
const map = sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, true) orelse err.sdl();
|
||||||
@memcpy(@as([*]u8, @ptrCast(map)), mesh_bytes);
|
@memcpy(@as([*]u8, @ptrCast(map)), mesh_bytes);
|
||||||
sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
||||||
|
|
||||||
@@ -353,22 +366,19 @@ pub fn beginDraw() bool {
|
|||||||
Graphics.to_resize = null;
|
Graphics.to_resize = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var render_target: ?*sdl.GPUTexture = null;
|
|
||||||
var width: u32 = 0;
|
var width: u32 = 0;
|
||||||
var height: u32 = 0;
|
var height: u32 = 0;
|
||||||
if (!sdl.WaitAndAcquireGPUSwapchainTexture(Graphics.command_buffer, Graphics.window, &render_target, &width, &height)) err.sdl();
|
if (!sdl.WaitAndAcquireGPUSwapchainTexture(Graphics.command_buffer, Graphics.window, &Graphics.render_target, &width, &height)) err.sdl();
|
||||||
// Hidden
|
// Window is probably hidden
|
||||||
if (render_target == null) return false;
|
if (Graphics.render_target == null) return false;
|
||||||
|
|
||||||
Graphics.render_pass = sdl.BeginGPURenderPass(Graphics.command_buffer, &.{
|
Graphics.render_pass = sdl.BeginGPURenderPass(Graphics.command_buffer, &.{
|
||||||
.clear_color = .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0 },
|
.clear_color = .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0 },
|
||||||
.cycle = false,
|
.cycle = false,
|
||||||
.load_op = sdl.GPU_LOADOP_CLEAR,
|
.load_op = sdl.GPU_LOADOP_CLEAR,
|
||||||
.store_op = sdl.GPU_STOREOP_RESOLVE,
|
.store_op = sdl.GPU_STOREOP_STORE,
|
||||||
// .store_op = sdl.GPU_STOREOP_STORE,
|
|
||||||
.resolve_texture = render_target,
|
|
||||||
.mip_level = 0,
|
.mip_level = 0,
|
||||||
.texture = Graphics.msaa_resolve,
|
.texture = Graphics.fsaa_target,
|
||||||
}, 1, &.{
|
}, 1, &.{
|
||||||
.clear_depth = 1.0,
|
.clear_depth = 1.0,
|
||||||
.load_op = sdl.GPU_LOADOP_CLEAR,
|
.load_op = sdl.GPU_LOADOP_CLEAR,
|
||||||
@@ -380,7 +390,8 @@ pub fn beginDraw() bool {
|
|||||||
|
|
||||||
sdl.BindGPUGraphicsPipeline(Graphics.render_pass, Graphics.pipeline);
|
sdl.BindGPUGraphicsPipeline(Graphics.render_pass, Graphics.pipeline);
|
||||||
sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = Graphics.vertex_buffer }, 1);
|
sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = Graphics.vertex_buffer }, 1);
|
||||||
sdl.PushGPUVertexUniformData(Graphics.command_buffer, 0, &Graphics.camera.matrix(), 16 * 4);
|
Graphics.camera.computeMatrix();
|
||||||
|
sdl.PushGPUVertexUniformData(Graphics.command_buffer, 0, &Graphics.camera.matrix, 16 * 4);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -402,6 +413,23 @@ pub fn endDraw() void {
|
|||||||
defer Graphics.render_pass = null;
|
defer Graphics.render_pass = null;
|
||||||
if (Graphics.render_pass) |pass| {
|
if (Graphics.render_pass) |pass| {
|
||||||
sdl.EndGPURenderPass(pass);
|
sdl.EndGPURenderPass(pass);
|
||||||
|
|
||||||
|
if (Graphics.fsaa_level > 1) sdl.GenerateMipmapsForGPUTexture(Graphics.command_buffer, Graphics.fsaa_target);
|
||||||
|
sdl.BlitGPUTexture(Graphics.command_buffer, &.{
|
||||||
|
.source = .{
|
||||||
|
.texture = Graphics.fsaa_target,
|
||||||
|
.w = Graphics.window_size[0],
|
||||||
|
.h = Graphics.window_size[1],
|
||||||
|
.mip_level = fsaa_level - 1,
|
||||||
|
},
|
||||||
|
.destination = .{
|
||||||
|
.texture = Graphics.render_target,
|
||||||
|
.w = Graphics.window_size[0],
|
||||||
|
.h = Graphics.window_size[1],
|
||||||
|
},
|
||||||
|
.load_op = sdl.GPU_LOADOP_DONT_CARE,
|
||||||
|
.filter = sdl.GPU_FILTER_NEAREST,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!sdl.SubmitGPUCommandBuffer(Graphics.command_buffer)) err.sdl();
|
if (!sdl.SubmitGPUCommandBuffer(Graphics.command_buffer)) err.sdl();
|
||||||
}
|
}
|
||||||
@@ -410,7 +438,7 @@ fn loadShader(path: []const u8, info: sdl.GPUShaderCreateInfo) *sdl.GPUShader {
|
|||||||
const file = std.fs.cwd().openFile(path, .{}) catch |e| err.file(e, path);
|
const file = std.fs.cwd().openFile(path, .{}) catch |e| err.file(e, path);
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const code = file.readToEndAllocOptions(std.heap.c_allocator, std.math.maxInt(usize), null, .@"1", 0) catch |e| err.file(e, path);
|
const code = file.readToEndAllocOptions(std.heap.c_allocator, std.math.maxInt(usize), null, 1, 0) catch |e| err.file(e, path);
|
||||||
defer std.heap.c_allocator.free(code);
|
defer std.heap.c_allocator.free(code);
|
||||||
|
|
||||||
var updated_info = info;
|
var updated_info = info;
|
||||||
@@ -419,38 +447,38 @@ fn loadShader(path: []const u8, info: sdl.GPUShaderCreateInfo) *sdl.GPUShader {
|
|||||||
return sdl.CreateGPUShader(device, &updated_info) orelse err.sdl();
|
return sdl.CreateGPUShader(device, &updated_info) orelse err.sdl();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createDepthTexture(width: u32, height: u32) *sdl.GPUTexture {
|
fn createTexture(width: u32, height: u32, format: c_uint, usage: c_uint, mip_level: u32) *sdl.GPUTexture {
|
||||||
return sdl.CreateGPUTexture(device, &.{
|
|
||||||
.format = sdl.GPU_TEXTUREFORMAT_D16_UNORM,
|
|
||||||
.layer_count_or_depth = 1,
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
.num_levels = 1,
|
|
||||||
.sample_count = sdl.GPU_SAMPLECOUNT_4,
|
|
||||||
.usage = sdl.GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
|
|
||||||
}) orelse err.sdl();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createTexture(width: u32, height: u32, format: c_uint) *sdl.GPUTexture {
|
|
||||||
return sdl.CreateGPUTexture(device, &.{
|
return sdl.CreateGPUTexture(device, &.{
|
||||||
.format = format,
|
.format = format,
|
||||||
.layer_count_or_depth = 1,
|
.layer_count_or_depth = 1,
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height,
|
.height = height,
|
||||||
.num_levels = 1,
|
.num_levels = mip_level,
|
||||||
.sample_count = sdl.GPU_SAMPLECOUNT_4,
|
.sample_count = sdl.GPU_SAMPLECOUNT_1,
|
||||||
.usage = sdl.GPU_TEXTUREUSAGE_COLOR_TARGET,
|
.usage = usage,
|
||||||
}) orelse err.sdl();
|
}) orelse err.sdl();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resetTextures(width: u32, height: u32) void {
|
fn resetTextures(width: u32, height: u32) void {
|
||||||
sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture);
|
sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture);
|
||||||
Graphics.depth_texture = createDepthTexture(width, height);
|
Graphics.depth_texture = createTexture(
|
||||||
|
width * Graphics.fsaa_scale,
|
||||||
|
height * Graphics.fsaa_scale,
|
||||||
|
DEPTH_FORMAT,
|
||||||
|
sdl.GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
const target_format = sdl.SDL_GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window);
|
const target_format = sdl.SDL_GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window);
|
||||||
|
|
||||||
sdl.ReleaseGPUTexture(Graphics.device, Graphics.msaa_resolve);
|
sdl.ReleaseGPUTexture(Graphics.device, Graphics.fsaa_target);
|
||||||
Graphics.msaa_resolve = createTexture(width, height, target_format);
|
Graphics.fsaa_target = createTexture(
|
||||||
|
width * Graphics.fsaa_scale,
|
||||||
|
height * Graphics.fsaa_scale,
|
||||||
|
target_format,
|
||||||
|
sdl.GPU_TEXTUREUSAGE_COLOR_TARGET | sdl.GPU_TEXTUREUSAGE_SAMPLER,
|
||||||
|
fsaa_level,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(width: u32, height: u32) void {
|
pub fn resize(width: u32, height: u32) void {
|
||||||
@@ -460,3 +488,21 @@ pub fn resize(width: u32, height: u32) void {
|
|||||||
pub fn windowId() sdl.WindowID {
|
pub fn windowId() sdl.WindowID {
|
||||||
return sdl.GetWindowID(Graphics.window);
|
return sdl.GetWindowID(Graphics.window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getWidth() u32 {
|
||||||
|
return @max(1, Graphics.window_size[0]);
|
||||||
|
}
|
||||||
|
pub fn getHeight() u32 {
|
||||||
|
return @max(1, Graphics.window_size[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generatePlane(x0: f32, y0: f32, x1: f32, y1: f32) [30]f32 {
|
||||||
|
return .{
|
||||||
|
-0.5, -0.5, 0, x0, y1,
|
||||||
|
0.5, 0.5, 0, x1, y0,
|
||||||
|
-0.5, 0.5, 0, x0, y0,
|
||||||
|
0.5, 0.5, 0, x1, y0,
|
||||||
|
-0.5, -0.5, 0, x0, y1,
|
||||||
|
0.5, -0.5, 0, x1, y1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const sdl = @import("sdl");
|
const sdl = @import("sdl");
|
||||||
|
const math = @import("../math.zig");
|
||||||
const Transform = @import("transform.zig");
|
const Transform = @import("transform.zig");
|
||||||
const Camera = @This();
|
const Camera = @This();
|
||||||
|
|
||||||
@@ -11,7 +12,11 @@ far: f32,
|
|||||||
/// width = height * aspect
|
/// width = height * aspect
|
||||||
aspect: f32,
|
aspect: f32,
|
||||||
|
|
||||||
pub fn matrix(camera: Camera) @Vector(16, f32) {
|
matrix: Transform.TMatrix,
|
||||||
|
|
||||||
|
pub fn computeMatrix(camera: *Camera) void {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
const xx = 1.0 / (camera.lens * camera.aspect);
|
const xx = 1.0 / (camera.lens * camera.aspect);
|
||||||
const yy = 1.0 / camera.lens;
|
const yy = 1.0 / camera.lens;
|
||||||
const fnmod = 1.0 / (camera.far - camera.near);
|
const fnmod = 1.0 / (camera.far - camera.near);
|
||||||
@@ -23,5 +28,79 @@ pub fn matrix(camera: Camera) @Vector(16, f32) {
|
|||||||
0, 0, -zz, wz,
|
0, 0, -zz, wz,
|
||||||
0, 0, -1, 0,
|
0, 0, -1, 0,
|
||||||
};
|
};
|
||||||
return Transform.multiplyMatrix(projection, camera.transform.inverseMatrix());
|
camera.matrix = Transform.multiplyMatrix(projection, camera.transform.inverseMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_screen(camera: Camera, position: Transform.Position) @Vector(2, f32) {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
|
var x: f32 = camera.matrix[3];
|
||||||
|
var y: f32 = camera.matrix[7];
|
||||||
|
var w: f32 = camera.matrix[15];
|
||||||
|
|
||||||
|
for (0..3) |i| {
|
||||||
|
x += camera.matrix[i] * position[i];
|
||||||
|
}
|
||||||
|
for (0..3) |i| {
|
||||||
|
y += camera.matrix[i + 4] * position[i];
|
||||||
|
}
|
||||||
|
for (0..3) |i| {
|
||||||
|
w += camera.matrix[i + 12] * position[i];
|
||||||
|
}
|
||||||
|
@setRuntimeSafety(false);
|
||||||
|
const wmod = 1 / w;
|
||||||
|
return .{ x * wmod, y * wmod };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mouse_in_quad(camera: Camera, mouse: @Vector(2, f32), quad_transform: Transform) bool {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
|
const matrix = Transform.multiplyMatrix(camera.matrix, quad_transform.matrix());
|
||||||
|
|
||||||
|
const pi: [4]@Vector(2, f32) = .{
|
||||||
|
.{ -0.5, -0.5 },
|
||||||
|
.{ -0.5, 0.5 },
|
||||||
|
.{ 0.5, 0.5 },
|
||||||
|
.{ 0.5, -0.5 },
|
||||||
|
};
|
||||||
|
var po: [4]@Vector(2, f32) = undefined;
|
||||||
|
for (0..4) |i| {
|
||||||
|
const x = matrix[0] * pi[i][0] + matrix[1] * pi[i][1] + matrix[3];
|
||||||
|
const y = matrix[4] * pi[i][0] + matrix[5] * pi[i][1] + matrix[7];
|
||||||
|
const w = matrix[12] * pi[i][0] + matrix[13] * pi[i][1] + matrix[15];
|
||||||
|
@setRuntimeSafety(false);
|
||||||
|
po[i] = .{ x / w, y / w };
|
||||||
|
}
|
||||||
|
inline for (0..4) |i| {
|
||||||
|
const a = po[i];
|
||||||
|
const b = po[(i + 1) % 4];
|
||||||
|
const c = mouse;
|
||||||
|
if ((c[0] - a[0]) * (b[1] - a[1]) - (c[1] - a[1]) * (b[0] - a[0]) < 0.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raycast(camera: Camera, mouse: @Vector(2, f32), plane: @Vector(4, f32)) @Vector(3, f32) {
|
||||||
|
const matrix = camera.transform.matrix();
|
||||||
|
|
||||||
|
const local = @Vector(3, f32){
|
||||||
|
mouse[0] * camera.lens * camera.aspect,
|
||||||
|
mouse[1] * camera.lens,
|
||||||
|
-1,
|
||||||
|
};
|
||||||
|
var global = @Vector(3, f32){
|
||||||
|
matrix[3],
|
||||||
|
matrix[7],
|
||||||
|
matrix[11],
|
||||||
|
};
|
||||||
|
for (0..3) |i| {
|
||||||
|
for (0..3) |j| {
|
||||||
|
global[i] += local[j] * matrix[4 * i + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return math.raycast(camera.transform.position, global, plane);
|
||||||
}
|
}
|
||||||
|
30
src/math.zig
30
src/math.zig
@@ -123,7 +123,7 @@ pub fn Sway(comptime T: type) type {
|
|||||||
const sin = std.math.sin(dist);
|
const sin = std.math.sin(dist);
|
||||||
const cos = std.math.cos(dist);
|
const cos = std.math.cos(dist);
|
||||||
var len = length(@Vector(2, T){ self.value, self.velocity });
|
var len = length(@Vector(2, T){ self.value, self.velocity });
|
||||||
if(len < 0.001) {
|
if (len < 0.001) {
|
||||||
self.value = 0.001;
|
self.value = 0.001;
|
||||||
self.velocity = 0;
|
self.velocity = 0;
|
||||||
len = 0.001;
|
len = 0.001;
|
||||||
@@ -137,3 +137,31 @@ pub fn Sway(comptime T: type) type {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn raycast(
|
||||||
|
origin: @Vector(3, f32),
|
||||||
|
target: @Vector(3, f32),
|
||||||
|
plane: @Vector(4, f32),
|
||||||
|
) @Vector(3, f32) {
|
||||||
|
@setFloatMode(.optimized);
|
||||||
|
|
||||||
|
const offset = target - origin;
|
||||||
|
const plane_dir = @Vector(3, f32){ plane[0], plane[1], plane[2] };
|
||||||
|
const dist = plane[3];
|
||||||
|
const dist_mod = dist / dot(plane_dir, plane_dir);
|
||||||
|
const num = dot(plane_dir, plane_dir * @as(@Vector(3, f32), @splat(dist_mod)) - origin);
|
||||||
|
var den = dot(offset, plane_dir);
|
||||||
|
if (@abs(den) < 0.0001) {
|
||||||
|
den = 0.0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
return origin + offset * @as(@Vector(3, f32), @splat(num / den));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(vector: anytype, value: f32) @TypeOf(vector) {
|
||||||
|
const max = @reduce(.Max, vector);
|
||||||
|
if (max > value)
|
||||||
|
return vector * @as(@TypeOf(vector), @splat(value / max))
|
||||||
|
else
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
@@ -3,7 +3,15 @@ const key_store = @import("data/keystore.zig");
|
|||||||
|
|
||||||
buttons: key_store.KeyStore(@TypeOf(sdl.BUTTON_LEFT), 4, 0) = .{},
|
buttons: key_store.KeyStore(@TypeOf(sdl.BUTTON_LEFT), 4, 0) = .{},
|
||||||
|
|
||||||
x: f32 = 0,
|
x_screen: f32 = 0,
|
||||||
y: f32 = 0,
|
y_screen: f32 = 0,
|
||||||
|
x_norm: f32 = 0,
|
||||||
|
y_norm: f32 = 0,
|
||||||
dx: f32 = 0,
|
dx: f32 = 0,
|
||||||
dy: f32 = 0,
|
dy: f32 = 0,
|
||||||
|
wheel: i32 = 0,
|
||||||
|
|
||||||
|
pub fn reset(mouse: *@This()) void {
|
||||||
|
mouse.buttons.reset();
|
||||||
|
mouse.wheel = 0;
|
||||||
|
}
|
||||||
|
5
src/offline.zig
Normal file
5
src/offline.zig
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const client = @import("client.zig");
|
||||||
|
|
||||||
|
pub fn main() void {
|
||||||
|
client.main();
|
||||||
|
}
|
5
src/server.zig
Normal file
5
src/server.zig
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn main() void {
|
||||||
|
std.debug.print("Server started!\n", .{});
|
||||||
|
}
|
51
src/time.zig
51
src/time.zig
@@ -1,51 +0,0 @@
|
|||||||
const TimeType = u64;
|
|
||||||
const TIME_UNIT: TimeType = 1 << 32;
|
|
||||||
const TIME_MULT = 1.0 / @as(f32, @floatFromInt(TIME_UNIT));
|
|
||||||
const Time = @This();
|
|
||||||
|
|
||||||
pub const ZERO = Time{ .clock = 0 };
|
|
||||||
|
|
||||||
clock: TimeType,
|
|
||||||
|
|
||||||
pub fn tick(self: *Time, units: f32) void {
|
|
||||||
self.clock += durationFromUnits(units);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn past(self: *Time, goal: Time) bool {
|
|
||||||
return self.clock >= goal.clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn offset(self: Time, units: f32) Time {
|
|
||||||
return Time{
|
|
||||||
.clock = self.clock + durationFromUnits(units),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unitsSince(self: *Time, from: Time) f32 {
|
|
||||||
if (from.clock > self.clock) return 0;
|
|
||||||
return @as(f32, @floatFromInt(self.clock - from.clock)) * TIME_MULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn progress(self: *Time, from: Time, to: Time) f32 {
|
|
||||||
if (from.clock > to.clock) return 1.0;
|
|
||||||
if (self.clock > to.clock) return 1.0;
|
|
||||||
|
|
||||||
const duration = to.clock - from.clock;
|
|
||||||
return @as(f32, @floatFromInt(self.clock - from.clock)) / @as(f32, @floatFromInt(duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unitsFromDuration(duration: TimeType) f32 {
|
|
||||||
return @as(f32, @floatFromInt(duration)) * TIME_MULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn durationFromUnits(units: f32) TimeType {
|
|
||||||
return @intFromFloat(@as(f32, @floatFromInt(TIME_UNIT)) * units);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn earliest(a: Time, b: Time) Time {
|
|
||||||
return .{ .clock = @min(a.clock, b.clock) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn plus(time: Time, ticks: TimeType) Time {
|
|
||||||
return .{ .clock = time.clock + ticks };
|
|
||||||
}
|
|
277
src/world.zig
277
src/world.zig
@@ -1,82 +1,261 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const sdl = @import("sdl");
|
||||||
|
const math = @import("math.zig");
|
||||||
|
const err = @import("error.zig");
|
||||||
|
const Game = @import("game.zig");
|
||||||
const Graphics = @import("graphics.zig");
|
const Graphics = @import("graphics.zig");
|
||||||
const Assets = @import("assets.zig");
|
const Assets = @import("assets.zig");
|
||||||
const Entity = @import("entity.zig");
|
|
||||||
const Time = @import("time.zig");
|
|
||||||
const comp = @import("components.zig");
|
|
||||||
|
|
||||||
pub var time: Time = undefined;
|
const Id = u32;
|
||||||
var next_stop: Time = undefined;
|
const Order = i32;
|
||||||
pub var entities: comp.Storage(Entity, .{}) = undefined;
|
|
||||||
|
|
||||||
|
pub var object_map: std.AutoHashMapUnmanaged(Id, usize) = .{};
|
||||||
|
pub var objects: std.ArrayListUnmanaged(Object) = .{};
|
||||||
pub var plane_mesh: Graphics.Mesh = undefined;
|
pub var plane_mesh: Graphics.Mesh = undefined;
|
||||||
pub var cube_mesh: Graphics.Mesh = undefined;
|
pub var cube_mesh: Graphics.Mesh = undefined;
|
||||||
|
pub var table_mesh: Graphics.Mesh = undefined;
|
||||||
pub var texture: Assets.Texture = undefined;
|
pub var texture: Assets.Texture = undefined;
|
||||||
|
pub var hand_texture: Assets.Texture = undefined;
|
||||||
|
pub var camera_position: @Vector(2, f32) = @splat(0);
|
||||||
|
pub var hover: ?Id = null;
|
||||||
|
pub var hand_transform: Graphics.Transform = .{};
|
||||||
|
pub var panning = false;
|
||||||
|
pub var zoom: i32 = 0;
|
||||||
|
pub var hand_objects: u32 = 0;
|
||||||
|
pub var min_order: Order = undefined;
|
||||||
|
pub var max_order: Order = undefined;
|
||||||
|
|
||||||
|
const Object = struct {
|
||||||
|
transform: Graphics.Transform = .{},
|
||||||
|
scale: Graphics.Transform.Scale,
|
||||||
|
mesh: Graphics.Mesh,
|
||||||
|
texture: Assets.Texture,
|
||||||
|
order: Order,
|
||||||
|
id: Id,
|
||||||
|
parent: enum {
|
||||||
|
none,
|
||||||
|
hand,
|
||||||
|
} = .none,
|
||||||
|
hand_index: u32 = 0,
|
||||||
|
parent_infl: f32 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
const World = @This();
|
const World = @This();
|
||||||
pub fn initDebug() void {
|
pub fn initDebug() void {
|
||||||
entities = comp.Storage(Entity, .{}).init();
|
for (0..10) |i| {
|
||||||
_ = entities.add(.{
|
(World.objects.addOne(Game.alloc) catch err.oom()).* = .{
|
||||||
.position = .{ 0, 0 },
|
.scale = @splat(0.5),
|
||||||
.player = true,
|
.mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane(
|
||||||
});
|
15.0 / 16.0,
|
||||||
time = Time.ZERO;
|
@as(f32, @floatFromInt(i)) / 16.0,
|
||||||
|
16.0 / 16.0,
|
||||||
|
@as(f32, @floatFromInt(i + 1)) / 16.0,
|
||||||
|
))),
|
||||||
|
.texture = Assets.load(.texture, "data/yakuza.png"),
|
||||||
|
.order = @intCast(i),
|
||||||
|
.id = @intCast(i),
|
||||||
|
};
|
||||||
|
World.object_map.put(Game.alloc, @intCast(i), i) catch err.oom();
|
||||||
|
}
|
||||||
World.plane_mesh = Graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA));
|
World.plane_mesh = Graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA));
|
||||||
World.cube_mesh = Graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA));
|
World.cube_mesh = Graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA));
|
||||||
World.texture = Assets.load(.texture, "data/wawa.png");
|
World.table_mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane(0, 0, 0.5, 0.5)));
|
||||||
|
World.texture = Assets.load(.texture, "data/yakuza.png");
|
||||||
|
World.hand_texture = Assets.load(.texture, "data/hand.png");
|
||||||
|
World.camera_position = @splat(0);
|
||||||
|
World.hover = null;
|
||||||
|
World.hand_transform = .{
|
||||||
|
.scale = @splat(0.5),
|
||||||
|
};
|
||||||
|
World.panning = false;
|
||||||
|
World.zoom = 0;
|
||||||
|
World.min_order = 0;
|
||||||
|
World.max_order = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
Graphics.unloadMesh(World.plane_mesh);
|
Graphics.unloadMesh(World.plane_mesh);
|
||||||
Graphics.unloadMesh(World.cube_mesh);
|
Graphics.unloadMesh(World.cube_mesh);
|
||||||
|
Graphics.unloadMesh(World.table_mesh);
|
||||||
Assets.free(World.texture);
|
Assets.free(World.texture);
|
||||||
World.entities.deinit();
|
Assets.free(World.hand_texture);
|
||||||
|
for (World.objects.items) |*object| {
|
||||||
|
Assets.free(object.texture);
|
||||||
|
Graphics.unloadMesh(object.mesh);
|
||||||
|
}
|
||||||
|
World.objects.clearAndFree(Game.alloc);
|
||||||
|
World.object_map.clearAndFree(Game.alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn updateReal(delta: f32) void {
|
pub fn update(delta: f32) void {
|
||||||
const update_until = World.time.plus(Time.durationFromUnits(delta));
|
const hand_target = Graphics.camera.raycast(.{ Game.mouse.x_norm, Game.mouse.y_norm }, .{ 0, 0, 1, 0 });
|
||||||
while (!World.time.past(update_until)) {
|
World.hand_transform.position = math.lerpTimeLn(
|
||||||
const current = Time.earliest(World.next_stop, update_until);
|
World.hand_transform.position,
|
||||||
defer World.time = current;
|
hand_target + @Vector(3, f32){ World.hand_transform.scale[0] * 0.5, -World.hand_transform.scale[1] * 0.5, 0.25 },
|
||||||
|
delta,
|
||||||
|
-16,
|
||||||
|
);
|
||||||
|
World.hover = null;
|
||||||
|
World.hand_objects = 0;
|
||||||
|
for (World.objects.items) |*object| {
|
||||||
|
updateHover(object);
|
||||||
|
}
|
||||||
|
for (World.objects.items) |*object| {
|
||||||
|
updateObject(object, delta);
|
||||||
|
}
|
||||||
|
if (Game.mouse.buttons.is_just_pressed(sdl.BUTTON_LEFT)) {
|
||||||
|
World.panning = !World.tryPick();
|
||||||
|
}
|
||||||
|
if (Game.mouse.buttons.is_just_pressed(sdl.BUTTON_RIGHT)) {
|
||||||
|
_ = World.tryRelease();
|
||||||
|
}
|
||||||
|
World.updateCamera(delta);
|
||||||
|
}
|
||||||
|
|
||||||
var iter = World.entities.iter();
|
pub fn tryPick() bool {
|
||||||
while (iter.next()) |entity| {
|
if (World.hover) |hover_id| {
|
||||||
entity.update();
|
World.panning = false;
|
||||||
|
World.getObject(hover_id).?.parent = .hand;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
pub fn tryRelease() bool {
|
||||||
|
var last: ?*Object = null;
|
||||||
|
for (World.objects.items) |*object| {
|
||||||
|
if (object.parent != .hand) continue;
|
||||||
|
last = object;
|
||||||
|
}
|
||||||
|
if (last) |object| {
|
||||||
|
object.parent = .none;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateHover(object: *Object) void {
|
||||||
|
if (object.parent == .hand) {
|
||||||
|
object.hand_index = World.hand_objects;
|
||||||
|
World.hand_objects += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Graphics.camera.mouse_in_quad(.{ Game.mouse.x_norm, Game.mouse.y_norm }, object.transform)) {
|
||||||
|
if (World.hover == null or World.getObject(World.hover.?).?.transform.position[2] < object.transform.position[2]) {
|
||||||
|
World.hover = object.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(delta: f32) void {
|
pub fn updateObject(object: *Object, delta: f32) void {
|
||||||
Graphics.drawMesh(World.plane_mesh, World.texture, Graphics.Transform.matrix(.{ .scale = @splat(5) }));
|
switch (object.parent) {
|
||||||
var iter = World.entities.iter();
|
.none => {
|
||||||
while (iter.next()) |entity| {
|
object.transform.position[2] = math.lerpTimeLn(
|
||||||
entity.draw(delta);
|
object.transform.position[2],
|
||||||
|
if (World.hover == object.id) @as(f32, 0.125) else @as(f32, 0.0625),
|
||||||
|
delta,
|
||||||
|
-8,
|
||||||
|
);
|
||||||
|
object.transform.scale = math.lerpTimeLn(
|
||||||
|
object.transform.scale,
|
||||||
|
if (World.hover == object.id) object.scale * @as(@Vector(3, f32), @splat(1.25)) else object.scale,
|
||||||
|
delta,
|
||||||
|
-4,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
.hand => {
|
||||||
|
var target_position = World.hand_transform.position;
|
||||||
|
var target_scale = object.scale;
|
||||||
|
target_position[2] *= 0.5;
|
||||||
|
const hand_order = hand_objects - object.hand_index - 1;
|
||||||
|
switch (hand_order) {
|
||||||
|
0 => {
|
||||||
|
target_position[0] -= World.hand_transform.scale[0] * 0.5;
|
||||||
|
target_position[1] += World.hand_transform.scale[1] * 0.5;
|
||||||
|
},
|
||||||
|
else => |i| {
|
||||||
|
target_position[0] += World.hand_transform.scale[0] * if ((i - 1) & 1 == 0) @as(f32, 0.5) else @as(f32, 1);
|
||||||
|
target_position[1] += World.hand_transform.scale[1] * if ((i - 1) & 2 == 0) @as(f32, 0.25) else @as(f32, -0.25);
|
||||||
|
target_position[2] -= @as(f32, @floatFromInt((hand_order - 1) / 4)) * 0.01;
|
||||||
|
target_scale = math.limit(target_scale, World.hand_transform.scale[1] * 0.5);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
object.transform.position = math.lerpTimeLn(
|
||||||
|
object.transform.position,
|
||||||
|
target_position,
|
||||||
|
delta,
|
||||||
|
-16,
|
||||||
|
);
|
||||||
|
object.transform.scale = math.lerpTimeLn(
|
||||||
|
object.transform.scale,
|
||||||
|
target_scale,
|
||||||
|
delta,
|
||||||
|
-4,
|
||||||
|
);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requestUpdate(at: Time) void {
|
pub fn draw() void {
|
||||||
World.next_stop = Time.earliest(at, World.next_stop);
|
Graphics.drawMesh(World.table_mesh, World.texture, Graphics.Transform.matrix(.{ .scale = @splat(8) }));
|
||||||
}
|
for (World.objects.items) |*object| {
|
||||||
|
Graphics.drawMesh(object.mesh, object.texture, object.transform.matrix());
|
||||||
pub fn entityAt(position: @Vector(2, i32)) ?*Entity {
|
|
||||||
var iter = World.entities.iter();
|
|
||||||
while (iter.next()) |entity| {
|
|
||||||
if (@reduce(.And, entity.position == position))
|
|
||||||
return entity;
|
|
||||||
}
|
}
|
||||||
return null;
|
Graphics.drawMesh(World.plane_mesh, World.hand_texture, World.hand_transform.matrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isFree(position: @Vector(2, i32)) bool {
|
pub fn updateCamera(delta: f32) void {
|
||||||
return World.entityAt(position) == null;
|
World.zoom = std.math.clamp(World.zoom + Game.mouse.wheel, -4, 8);
|
||||||
}
|
const zoom_factor = std.math.exp(@as(f32, @floatFromInt(zoom)) * @log(2.0) * -0.5);
|
||||||
|
|
||||||
pub fn getPlayer() ?*Entity {
|
if (Game.mouse.buttons.is_pressed(sdl.BUTTON_LEFT)) {
|
||||||
var iter = World.entities.iter();
|
if (World.panning) {
|
||||||
while (iter.next()) |entity| {
|
World.camera_position[0] += zoom_factor * Game.mouse.dx / @as(f32, @floatFromInt(Graphics.getWidth())) * -15;
|
||||||
if (entity.player)
|
World.camera_position[1] += zoom_factor * Game.mouse.dy / @as(f32, @floatFromInt(Graphics.getWidth())) * 15;
|
||||||
return entity;
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
const offset = @Vector(3, f32){ 0.0, -1.0 * zoom_factor, 4.0 * zoom_factor };
|
||||||
|
const target_position = @Vector(3, f32){ World.camera_position[0], World.camera_position[1], 0.0 };
|
||||||
|
Graphics.camera.transform.position = math.lerpTimeLn(
|
||||||
|
Graphics.camera.transform.position,
|
||||||
|
target_position + offset,
|
||||||
|
delta,
|
||||||
|
-32,
|
||||||
|
);
|
||||||
|
|
||||||
|
const ORIGIN_DIR = @Vector(3, f32){ 0.0, 0.0, -1.0 };
|
||||||
|
const INIT_ROTATION = Graphics.Transform.rotationByAxis(.{ 1.0, 0.0, 0.0 }, std.math.pi * 0.5);
|
||||||
|
|
||||||
|
const ROTATED_DIR = Graphics.Transform.rotateVector(ORIGIN_DIR, INIT_ROTATION);
|
||||||
|
|
||||||
|
const target_rotation = Graphics.Transform.combineRotations(
|
||||||
|
INIT_ROTATION,
|
||||||
|
Graphics.Transform.rotationToward(
|
||||||
|
ROTATED_DIR,
|
||||||
|
math.lerp(-offset, target_position - Graphics.camera.transform.position, 0.125),
|
||||||
|
.{ .normalize_to = true },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Graphics.camera.transform.rotation = Graphics.Transform.normalizeRotation(math.slerpTimeLn(
|
||||||
|
Graphics.camera.transform.rotation,
|
||||||
|
target_rotation,
|
||||||
|
delta,
|
||||||
|
-16,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getObject(id: Id) ?*Object {
|
||||||
|
const index = World.object_map.get(id) orelse return null;
|
||||||
|
if (index >= World.objects.items.len) return null;
|
||||||
|
return &World.objects.items[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bringToTop(object: *Object) void {
|
||||||
|
World.max_order += 1;
|
||||||
|
object.order = World.max_order;
|
||||||
|
}
|
||||||
|
fn bringToBottom(object: *Object) void {
|
||||||
|
World.min_order -= 1;
|
||||||
|
object.order = World.min_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CUBE_MESH_DATA = [_]f32{
|
const CUBE_MESH_DATA = [_]f32{
|
||||||
@@ -130,9 +309,3 @@ const PLANE_MESH_DATA = [_]f32{
|
|||||||
-0.5, -0.5, 0, 0.0, 1.0,
|
-0.5, -0.5, 0, 0.0, 1.0,
|
||||||
0.5, -0.5, 0, 1.0, 1.0,
|
0.5, -0.5, 0, 1.0, 1.0,
|
||||||
};
|
};
|
||||||
const TEXTURE_DATA = [_]u8{
|
|
||||||
255, 64, 64, 255,
|
|
||||||
64, 255, 64, 255,
|
|
||||||
64, 64, 255, 255,
|
|
||||||
64, 64, 64, 255,
|
|
||||||
};
|
|
||||||
|
Reference in New Issue
Block a user