Camera controls, fix some math; lighting probably broken

This commit is contained in:
2025-11-13 17:01:10 +01:00
parent 4cbf151fd9
commit a011603195
10 changed files with 394 additions and 169 deletions

View File

@@ -17,7 +17,7 @@ pub const Texture = struct {
pub const AssetMap = std.hash_map.StringHashMapUnmanaged; pub const AssetMap = std.hash_map.StringHashMapUnmanaged;
pub fn visitTextures(allocator: std.mem.Allocator) std.hash_map.StringHashMapUnmanaged(Texture) { pub fn visitTextures(allocator: std.mem.Allocator) AssetMap(Texture) {
zstbi.init(allocator); zstbi.init(allocator);
defer zstbi.deinit(); defer zstbi.deinit();
@@ -153,7 +153,7 @@ fn rearrange(grid_w: u32, grid_h: u32, tile_w: u32, tile_h: u32, inbuf: []const
} }
} }
pub fn deinitTextures(allocator: std.mem.Allocator, map: AssetMap(Texture)) void { pub fn deinitTextures(allocator: std.mem.Allocator, map: *AssetMap(Texture)) void {
var it = map.iterator(); var it = map.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
allocator.free(entry.key_ptr.*); allocator.free(entry.key_ptr.*);

View File

@@ -8,78 +8,36 @@ const sapp = sokol.app;
const sg = sokol.gfx; const sg = sokol.gfx;
const sglue = sokol.glue; const sglue = sokol.glue;
const ap = @import("asset_pipeline.zig");
const main = @import("main.zig"); const main = @import("main.zig");
const math = @import("math.zig"); const math = @import("math.zig");
const Samplers = @import("Samplers.zig"); const tiles = @import("tiles.zig");
const samplers = @import("samplers.zig");
const Iterator3 = math.Iterator3;
const Matrix4x4 = math.Matrix4x4; const Matrix4x4 = math.Matrix4x4;
const Quaternion = math.Quaternion;
const Vector2 = math.Vector2;
const Vector3 = math.Vector3; const Vector3 = math.Vector3;
var show_console: bool = false; var show_console: bool = false;
var show_demo_window: bool = false; var show_demo_window: bool = false;
var vertex_buffer: sg.Buffer = undefined;
var index_buffer: sg.Buffer = undefined;
var pipeline: sg.Pipeline = undefined;
var point_light_buffer: sg.Buffer = undefined; var point_light_buffer: sg.Buffer = undefined;
var directional_light_buffer: sg.Buffer = undefined; var directional_light_buffer: sg.Buffer = undefined;
var sampler: sg.Sampler = undefined;
var base_color_texture: sg.Image = undefined;
var occlusion_roughness_metallic_texture: sg.Image = undefined;
var normal_texture: sg.Image = undefined;
var bindings: sg.Bindings = undefined; pub var point_light_buffer_view: sg.View = undefined;
pub var directional_light_buffer_view: sg.View = undefined;
var textures: ap.AssetMap(ap.Texture) = undefined; var camera_position: Vector3 = .init(0, 0, 1.62);
var camera_pitch: f32 = 0;
var camera_yaw: f32 = 0;
const camera_near_plane = 0.1;
const camera_vertical_fov_deg = 90.0;
const camera_half_vertical_fov_rad = 0.5 * camera_vertical_fov_deg * std.math.rad_per_deg;
const camera_mouse_sensitivity = 0.0015;
pub fn init() void { pub fn init() void {
textures = ap.visitTextures(main.allocator);
const base_color = textures.getPtr("BaseColor").?;
const normal = textures.getPtr("Normal").?;
const occlusion_roughness_metallic = textures.getPtr("OcclusionRoughnessMetallic").?;
vertex_buffer = sg.makeBuffer(.{
.data = sg.asRange(&[_]f32{
// positionOS texCoord normalOS tangentOS
-0.5, -0.5, 0, 0, 1, 0, 0, 1, 1, 0, 0, -1,
0.5, -0.5, 0, 1, 1, 0, 0, 1, 1, 0, 0, -1,
-0.5, 0.5, 0, 0, 0, 0, 0, 1, 1, 0, 0, -1,
0.5, 0.5, 0, 1, 0, 0, 0, 1, 1, 0, 0, -1,
}),
.usage = .{
.immutable = true,
.vertex_buffer = true,
},
});
index_buffer = sg.makeBuffer(.{
.data = sg.asRange(&[_]u16{ 0, 1, 2, 2, 1, 3 }),
.usage = .{
.immutable = true,
.index_buffer = true,
},
});
pipeline = sg.makePipeline(.{
.shader = sg.makeShader(shaders.mainShaderDesc(sg.queryBackend())),
.layout = blk: {
var ret: sg.VertexLayoutState = .{};
ret.attrs[shaders.ATTR_main_positionOS].format = .FLOAT3;
ret.attrs[shaders.ATTR_main_texCoord].format = .FLOAT2;
ret.attrs[shaders.ATTR_main_normalOS].format = .FLOAT3;
ret.attrs[shaders.ATTR_main_tangentOS].format = .FLOAT4;
break :blk ret;
},
.primitive_type = .TRIANGLES,
.index_type = .UINT16,
.cull_mode = .BACK,
.face_winding = .CCW,
});
point_light_buffer = sg.makeBuffer(.{ point_light_buffer = sg.makeBuffer(.{
.size = @sizeOf([4]shaders.PointLight), .size = @sizeOf([4]shaders.PointLight),
.usage = .{ .usage = .{
@@ -87,6 +45,9 @@ pub fn init() void {
.storage_buffer = true, .storage_buffer = true,
}, },
}); });
point_light_buffer_view = sg.makeView(.{
.storage_buffer = .{ .buffer = point_light_buffer },
});
directional_light_buffer = sg.makeBuffer(.{ directional_light_buffer = sg.makeBuffer(.{
.size = @sizeOf([4]shaders.DirectionalLight), .size = @sizeOf([4]shaders.DirectionalLight),
@@ -95,86 +56,33 @@ pub fn init() void {
.storage_buffer = true, .storage_buffer = true,
}, },
}); });
directional_light_buffer_view = sg.makeView(.{
sampler = Samplers.getOrCreateSampler(.{
.wrap_u = .REPEAT,
.wrap_v = .REPEAT,
.wrap_w = .REPEAT,
.min_filter = .LINEAR,
.mag_filter = .LINEAR,
.mipmap_filter = .LINEAR,
}) catch unreachable;
base_color_texture = sg.makeImage(.{
.type = .ARRAY,
.usage = .{ .immutable = true },
.width = @intCast(base_color.width),
.height = @intCast(base_color.height),
.num_slices = @intCast(base_color.depth),
.pixel_format = .RGBA8,
.data = blk: {
var ret: sg.ImageData = .{};
ret.mip_levels[0] = sg.asRange(base_color.data);
break :blk ret;
},
});
occlusion_roughness_metallic_texture = sg.makeImage(.{
.type = .ARRAY,
.usage = .{ .immutable = true },
.width = @intCast(occlusion_roughness_metallic.width),
.height = @intCast(occlusion_roughness_metallic.height),
.num_slices = @intCast(occlusion_roughness_metallic.depth),
.pixel_format = .RGBA8,
.data = blk: {
var ret: sg.ImageData = .{};
ret.mip_levels[0] = sg.asRange(occlusion_roughness_metallic.data);
break :blk ret;
},
});
normal_texture = sg.makeImage(.{
.type = .ARRAY,
.usage = .{ .immutable = true },
.width = @intCast(normal.width),
.height = @intCast(normal.height),
.num_slices = @intCast(normal.depth),
.pixel_format = .RGBA8,
.data = blk: {
var ret: sg.ImageData = .{};
ret.mip_levels[0] = sg.asRange(normal.data);
break :blk ret;
},
});
bindings = .{};
bindings.vertex_buffers[0] = vertex_buffer;
bindings.index_buffer = index_buffer;
bindings.views[shaders.VIEW_Point_Lights] = sg.makeView(.{
.storage_buffer = .{ .buffer = point_light_buffer },
});
bindings.views[shaders.VIEW_Directional_Lights] = sg.makeView(.{
.storage_buffer = .{ .buffer = directional_light_buffer }, .storage_buffer = .{ .buffer = directional_light_buffer },
}); });
bindings.views[shaders.VIEW__BaseColorTexture] = sg.makeView(.{
.texture = .{ .image = base_color_texture },
});
bindings.views[shaders.VIEW__OcclusionRoughnessMetallicTexture] = sg.makeView(.{
.texture = .{ .image = occlusion_roughness_metallic_texture },
});
bindings.views[shaders.VIEW__NormalTexture] = sg.makeView(.{
.texture = .{ .image = normal_texture },
});
bindings.samplers[0] = sampler; tiles.init();
} }
pub fn deinit() void {} pub fn deinit() void {
tiles.deinit();
pub fn update(dt: f64) void { sg.destroyView(directional_light_buffer_view);
_ = dt; sg.destroyBuffer(directional_light_buffer);
sg.destroyView(point_light_buffer_view);
sg.destroyBuffer(point_light_buffer);
samplers.deinit();
}
pub fn update(dt: f32) void {
var camera_d = Vector2.init(
@as(f32, @floatFromInt(@intFromBool(input_right))) - @as(f32, @floatFromInt(@intFromBool(input_left))),
@as(f32, @floatFromInt(@intFromBool(input_forwards))) - @as(f32, @floatFromInt(@intFromBool(input_backwards))),
).rotate(camera_yaw).mulScalar(player_speed * dt);
camera_position = Vector3.add(camera_position, camera_d.asVector3(0));
const framebuffer_size = Vector2.init(sapp.widthf(), sapp.heightf());
if (show_console) { if (show_console) {
showConsole(); showConsole();
@@ -205,44 +113,85 @@ pub fn update(dt: f64) void {
.swapchain = sglue.swapchain(), .swapchain = sglue.swapchain(),
}); });
sg.applyPipeline(pipeline); tiles.bind();
sg.applyBindings(bindings);
const matrix_ws_to_vs = Matrix4x4.identity; const camera_rotation = Quaternion.mulQuaternion(
const matrix_vs_to_cs = Matrix4x4.identity; .initRotationXY(camera_yaw),
const ambient_light = Vector3.init(0.01, 0.01, 0.01); .initRotationYZ(camera_pitch),
);
const camera_aspect_ratio = framebuffer_size.getX() / framebuffer_size.getY();
const camera_yscale = 1.0 / @tan(camera_half_vertical_fov_rad);
const camera_xscale = camera_yscale / camera_aspect_ratio;
const matrix_ws_to_vs = Matrix4x4.mulMatrix(
Matrix4x4.initRotation(camera_rotation.conjugate()),
Matrix4x4.initTranslation(camera_position.negate()),
);
// zig fmt: off
const matrix_vs_to_cs = Matrix4x4.init(
camera_xscale, 0, 0, 0,
0, 0, camera_near_plane, 1,
0, camera_yscale, 0, 0,
0, 0, 0, 0,
);
// zig fmt: on
const ambient_light = Vector3.init(0, 0, 0);
sg.applyUniforms(shaders.UB_Global_Uniforms_Vertex, sg.asRange(&shaders.GlobalUniformsVertex{ sg.applyUniforms(shaders.UB_Global_Uniforms_Vertex, sg.asRange(&shaders.GlobalUniformsVertex{
._MatrixWStoVS = matrix_ws_to_vs.asArray(), ._MatrixWStoVS = matrix_ws_to_vs.asArray(),
._MatrixVStoCS = matrix_vs_to_cs.asArray(), ._MatrixVStoCS = matrix_vs_to_cs.asArray(),
})); }));
const point_lights: []const shaders.PointLight = &.{
.{
.positionWS = .{ 0, 0, 1.62 },
.color = .{ 11, 11, 10 },
},
.{
.positionWS = .{ -10, 10, 1.62 },
.color = .{ 5, 0, 0 },
},
.{
.positionWS = .{ 0, 10, 1.62 },
.color = .{ 0, 5, 0 },
},
.{
.positionWS = .{ 10, 10, 1.62 },
.color = .{ 0, 0, 5 },
},
};
sg.updateBuffer(point_light_buffer, sg.asRange(point_lights));
sg.applyUniforms(shaders.UB_Global_Uniforms_Fragment, sg.asRange(&shaders.GlobalUniformsFragment{ sg.applyUniforms(shaders.UB_Global_Uniforms_Fragment, sg.asRange(&shaders.GlobalUniformsFragment{
._MatrixWStoVS = matrix_ws_to_vs.asArray(), ._MatrixWStoVS = matrix_ws_to_vs.asArray(),
._AmbientLight = ambient_light.asArray(), ._AmbientLight = ambient_light.asArray(),
._PointLightCount = 0, ._PointLightCount = @intCast(point_lights.len),
._DirectionalLightCount = 0, ._DirectionalLightCount = 0,
})); }));
sg.applyUniforms(shaders.UB_Material_Uniforms, sg.asRange(&shaders.MaterialUniforms{ var it = Iterator3.init(.init(-10, -10, 0), .init(10, 10, 0), .one);
._TextureIndex = 0, var tile_index: u32 = 0;
})); while (it.next()) |pos| : (tile_index = (tile_index + 1) % 16) {
tiles.draw(pos, .identity, tile_index);
}
const matrix_os_to_ws = Matrix4x4.identity;
const matrix_os_to_ws_normal = Matrix4x4.identity;
sg.applyUniforms(shaders.UB_Object_Uniforms, sg.asRange(&shaders.ObjectUniforms{
._MatrixOStoWS = matrix_os_to_ws.asArray(),
._MatrixOStoWSNormal = matrix_os_to_ws_normal.asArray(),
}));
sg.draw(0, 6, 1);
sg.endPass(); sg.endPass();
} }
var input_forwards: bool = false;
var input_backwards: bool = false;
var input_left: bool = false;
var input_right: bool = false;
const player_speed = 5.0;
pub fn onKeyDown(key_code: sapp.Keycode, mods: u32) void { pub fn onKeyDown(key_code: sapp.Keycode, mods: u32) void {
const no_mods = mods == 0; const no_mods = mods == 0;
if (key_code == .ESCAPE and no_mods) {
sapp.requestQuit();
}
if (key_code == .GRAVE_ACCENT and no_mods) { if (key_code == .GRAVE_ACCENT and no_mods) {
show_console = !show_console; show_console = !show_console;
} }
@@ -250,6 +199,48 @@ pub fn onKeyDown(key_code: sapp.Keycode, mods: u32) void {
if (key_code == .F1 and no_mods) { if (key_code == .F1 and no_mods) {
show_demo_window = true; show_demo_window = true;
} }
if (key_code == .W) {
input_forwards = true;
input_backwards = false;
}
if (key_code == .S) {
input_backwards = true;
input_forwards = false;
}
if (key_code == .A) {
input_left = true;
input_right = false;
}
if (key_code == .D) {
input_right = true;
input_left = false;
}
}
pub fn onKeyUp(key_code: sapp.Keycode, mods: u32) void {
_ = mods;
if (key_code == .W) {
input_forwards = false;
}
if (key_code == .S) {
input_backwards = false;
}
if (key_code == .A) {
input_left = false;
}
if (key_code == .D) {
input_right = false;
}
}
pub fn onMouseMove(dx: f32, dy: f32) void {
camera_pitch -= dy * camera_mouse_sensitivity;
camera_yaw -= dx * camera_mouse_sensitivity;
camera_pitch = std.math.clamp(camera_pitch, -0.5 * std.math.pi, 0.5 * std.math.pi);
camera_yaw = @mod(camera_yaw, 2 * std.math.pi);
} }
fn showConsole() void { fn showConsole() void {

View File

@@ -47,6 +47,8 @@ fn init() callconv(.c) void {
.logger = .{ .func = sokolLogFn }, .logger = .{ .func = sokolLogFn },
}); });
sapp.lockMouse(true);
game.init(); game.init();
} }
@@ -66,7 +68,7 @@ fn frame() callconv(.c) void {
.dpi_scale = sapp.dpiScale(), .dpi_scale = sapp.dpiScale(),
}); });
game.update(dt); game.update(@floatCast(dt));
// --- BEGIN IMGUI PASS --- // --- BEGIN IMGUI PASS ---
@@ -90,6 +92,13 @@ fn event(_ev: [*c]const sapp.Event) callconv(.c) void {
if (ev.type == .KEY_DOWN and !ev.key_repeat) { if (ev.type == .KEY_DOWN and !ev.key_repeat) {
game.onKeyDown(ev.key_code, ev.modifiers); game.onKeyDown(ev.key_code, ev.modifiers);
} }
if (ev.type == .KEY_UP and !ev.key_repeat) {
game.onKeyUp(ev.key_code, ev.modifiers);
}
}
if (ev.type == .MOUSE_MOVE) {
game.onMouseMove(ev.mouse_dx, ev.mouse_dy);
} }
} }

View File

@@ -1,3 +1,4 @@
pub const Iterator3 = @import("math/Iterator3.zig");
pub const Matrix4x4 = @import("math/Matrix4x4.zig").Matrix4x4; pub const Matrix4x4 = @import("math/Matrix4x4.zig").Matrix4x4;
pub const Quaternion = @import("math/Quaternion.zig").Quaternion; pub const Quaternion = @import("math/Quaternion.zig").Quaternion;
pub const Vector2 = @import("math/Vector2.zig").Vector2; pub const Vector2 = @import("math/Vector2.zig").Vector2;

38
src/math/Iterator3.zig Normal file
View File

@@ -0,0 +1,38 @@
const Vector3 = @import("Vector3.zig").Vector3;
min: Vector3,
max: Vector3,
step: Vector3,
current: ?Vector3,
pub fn init(min: Vector3, max: Vector3, step: Vector3) @This() {
return .{
.min = min,
.max = max,
.step = step,
.current = min,
};
}
pub fn next(self: *@This()) ?Vector3 {
const current = self.current orelse return null;
var next_current = current;
next_current = next_current.setX(next_current.getX() + self.step.getX());
if (next_current.getX() > self.max.getX()) {
next_current = next_current.setX(self.min.getX());
next_current = next_current.setY(next_current.getY() + self.step.getY());
if (next_current.getY() > self.max.getY()) {
next_current = next_current.setY(self.min.getY());
next_current = next_current.setZ(next_current.getZ() + self.step.getZ());
if (next_current.getZ() > self.max.getZ()) {
self.current = null;
return null;
}
}
}
self.current = next_current;
return next_current;
}

View File

@@ -30,12 +30,12 @@ pub const Vector2 = extern struct {
pub inline fn asVector3(self: Vector2, z: f32) Vector3 { pub inline fn asVector3(self: Vector2, z: f32) Vector3 {
const z_vector: @Vector(3, f32) = .{ undefined, undefined, z }; const z_vector: @Vector(3, f32) = .{ undefined, undefined, z };
return .{ .vector = @shuffle(f32, self.vector, z_vector, .{ 0, 1, ~@as(i32, -2) }) }; return .{ .vector = @shuffle(f32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };
} }
pub inline fn asVector4(self: Vector4, z: f32, w: f32) Vector4 { pub inline fn asVector4(self: Vector4, z: f32, w: f32) Vector4 {
const zw_vector: @Vector(4, f32) = .{ undefined, undefined, z, w }; const zw_vector: @Vector(4, f32) = .{ undefined, undefined, z, w };
return .{ .vector = @shuffle(f32, self.vector, zw_vector, .{ 0, 1, ~@as(i32, -2), ~@as(i32, -3) }) }; return .{ .vector = @shuffle(f32, self.vector, zw_vector, [_]i32{ 0, 1, ~@as(i32, 2), ~@as(i32, 3) }) };
} }
// --- ACCESSORS --- // --- ACCESSORS ---
@@ -50,12 +50,12 @@ pub const Vector2 = extern struct {
pub inline fn setX(self: Vector2, x: f32) Vector2 { pub inline fn setX(self: Vector2, x: f32) Vector2 {
const x_vector: Vector = @splat(x); const x_vector: Vector = @splat(x);
return .{ .vector = @shuffle(f32, self, x_vector, .{ ~@as(i32, 0), 1 }) }; return .{ .vector = @shuffle(f32, self.vector, x_vector, .{ ~@as(i32, 0), 1 }) };
} }
pub inline fn setY(self: Vector2, y: f32) Vector2 { pub inline fn setY(self: Vector2, y: f32) Vector2 {
const y_vector: Vector = @splat(y); const y_vector: Vector = @splat(y);
return .{ .vector = @shuffle(f32, self, y_vector, .{ 0, ~@as(i32, 1) }) }; return .{ .vector = @shuffle(f32, self.vector, y_vector, .{ 0, ~@as(i32, 1) }) };
} }
// --- COMPONENT-WISE --- // --- COMPONENT-WISE ---
@@ -138,8 +138,8 @@ pub const Vector2 = extern struct {
const s = @sin(angle_rad); const s = @sin(angle_rad);
return .{ return .{
.vector = .{ .vector = .{
self.x * c - self.y * s, self.getX() * c - self.getY() * s,
self.x * s + self.x * c, self.getX() * s + self.getY() * c,
}, },
}; };
} }

View File

@@ -31,12 +31,12 @@ pub const Vector3 = extern struct {
} }
pub inline fn asVector2(self: Vector3) Vector2 { pub inline fn asVector2(self: Vector3) Vector2 {
return .{ .vector = @shuffle(f32, self.vector, undefined, .{ 0, 1 }) }; return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
} }
pub inline fn asVector4(self: Vector3, w: f32) Vector4 { pub inline fn asVector4(self: Vector3, w: f32) Vector4 {
const w_vector: @Vector(4, f32) = .{ undefined, undefined, undefined, w }; const w_vector: @Vector(4, f32) = .{ undefined, undefined, undefined, w };
return .{ .vector = @shuffle(f32, self.vector, w_vector, .{ 0, 1, 2, ~@as(i32, -3) }) }; return .{ .vector = @shuffle(f32, self.vector, w_vector, [_]i32{ 0, 1, 2, ~@as(i32, 3) }) };
} }
// --- ACCESSORS --- // --- ACCESSORS ---
@@ -55,17 +55,17 @@ pub const Vector3 = extern struct {
pub inline fn setX(self: Vector3, x: f32) Vector3 { pub inline fn setX(self: Vector3, x: f32) Vector3 {
const x_vector: Vector = @splat(x); const x_vector: Vector = @splat(x);
return .{ .vector = @shuffle(f32, self, x_vector, .{ ~@as(i32, 0), 1, 2 }) }; return .{ .vector = @shuffle(f32, self.vector, x_vector, [_]i32{ ~@as(i32, 0), 1, 2 }) };
} }
pub inline fn setY(self: Vector3, y: f32) Vector3 { pub inline fn setY(self: Vector3, y: f32) Vector3 {
const y_vector: Vector = @splat(y); const y_vector: Vector = @splat(y);
return .{ .vector = @shuffle(f32, self, y_vector, .{ 0, ~@as(i32, 1), 2 }) }; return .{ .vector = @shuffle(f32, self.vector, y_vector, [_]i32{ 0, ~@as(i32, 1), 2 }) };
} }
pub inline fn setZ(self: Vector3, z: f32) Vector3 { pub inline fn setZ(self: Vector3, z: f32) Vector3 {
const z_vector: Vector = @splat(z); const z_vector: Vector = @splat(z);
return .{ .vector = @shuffle(f32, self, z_vector, .{ 0, 1, ~@as(i32, 2) }) }; return .{ .vector = @shuffle(f32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };
} }
// --- COMPONENT-WISE --- // --- COMPONENT-WISE ---

View File

@@ -31,11 +31,11 @@ pub const Vector4 = extern struct {
} }
pub inline fn asVector2(self: Vector4) Vector2 { pub inline fn asVector2(self: Vector4) Vector2 {
return .{ .vector = @shuffle(f32, self.vector, undefined, .{ 0, 1 }) }; return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
} }
pub inline fn asVector3(self: Vector4) Vector3 { pub inline fn asVector3(self: Vector4) Vector3 {
return .{ .vector = @shuffle(f32, self.vector, undefined, .{ 0, 1, 2 }) }; return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1, 2 }) };
} }
// --- ACCESSORS --- // --- ACCESSORS ---
@@ -58,22 +58,22 @@ pub const Vector4 = extern struct {
pub inline fn setX(self: Vector4, x: f32) Vector4 { pub inline fn setX(self: Vector4, x: f32) Vector4 {
const x_vector: Vector = @splat(x); const x_vector: Vector = @splat(x);
return .{ .vector = @shuffle(f32, self, x_vector, .{ ~@as(i32, 0), 1, 2, 3 }) }; return .{ .vector = @shuffle(f32, self.vector, x_vector, [_]i32{ ~@as(i32, 0), 1, 2, 3 }) };
} }
pub inline fn setY(self: Vector4, y: f32) Vector4 { pub inline fn setY(self: Vector4, y: f32) Vector4 {
const y_vector: Vector = @splat(y); const y_vector: Vector = @splat(y);
return .{ .vector = @shuffle(f32, self, y_vector, .{ 0, ~@as(i32, 1), 2, 3 }) }; return .{ .vector = @shuffle(f32, self.vector, y_vector, [_]i32{ 0, ~@as(i32, 1), 2, 3 }) };
} }
pub inline fn setZ(self: Vector4, z: f32) Vector4 { pub inline fn setZ(self: Vector4, z: f32) Vector4 {
const z_vector: Vector = @splat(z); const z_vector: Vector = @splat(z);
return .{ .vector = @shuffle(f32, self, z_vector, .{ 0, 1, ~@as(i32, 2), 3 }) }; return .{ .vector = @shuffle(f32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2), 3 }) };
} }
pub inline fn setW(self: Vector4, w: f32) Vector4 { pub inline fn setW(self: Vector4, w: f32) Vector4 {
const w_vector: Vector = @splat(w); const w_vector: Vector = @splat(w);
return .{ .vector = @shuffle(f32, self, w_vector, .{ 0, 1, 2, ~@as(i32, 3) }) }; return .{ .vector = @shuffle(f32, self.vector, w_vector, [_]i32{ 0, 1, 2, ~@as(i32, 3) }) };
} }
// --- COMPONENT-WISE --- // --- COMPONENT-WISE ---

186
src/tiles.zig Normal file
View File

@@ -0,0 +1,186 @@
const shaders = @import("shaders");
const sokol = @import("sokol");
const sg = sokol.gfx;
const ap = @import("asset_pipeline.zig");
const game = @import("game.zig");
const main = @import("main.zig");
const math = @import("math.zig");
const samplers = @import("samplers.zig");
const Matrix4x4 = math.Matrix4x4;
const Quaternion = math.Quaternion;
const Vector3 = math.Vector3;
var vertex_buffer: sg.Buffer = undefined;
var index_buffer: sg.Buffer = undefined;
var pipeline: sg.Pipeline = undefined;
var base_color_texture: sg.Image = undefined;
var base_color_texture_view: sg.View = undefined;
var occlusion_roughness_metallic_texture: sg.Image = undefined;
var occlusion_roughness_metallic_texture_view: sg.View = undefined;
var normal_texture: sg.Image = undefined;
var normal_texture_view: sg.View = undefined;
var bindings: sg.Bindings = undefined;
pub fn init() void {
var textures = ap.visitTextures(main.temp_allocator);
defer ap.deinitTextures(main.temp_allocator, &textures);
const base_color_image = textures.getPtr("BaseColor").?;
const normal_image = textures.getPtr("Normal").?;
const occlusion_roughness_metallic_image = textures.getPtr("OcclusionRoughnessMetallic").?;
vertex_buffer = sg.makeBuffer(.{
.data = sg.asRange(&[_]f32{
// positionOS texCoord normalOS tangentOS
-0.5, -0.5, 0, 0, 1, 0, 0, 1, 1, 0, 0, -1,
0.5, -0.5, 0, 1, 1, 0, 0, 1, 1, 0, 0, -1,
-0.5, 0.5, 0, 0, 0, 0, 0, 1, 1, 0, 0, -1,
0.5, 0.5, 0, 1, 0, 0, 0, 1, 1, 0, 0, -1,
}),
.usage = .{
.immutable = true,
.vertex_buffer = true,
},
});
index_buffer = sg.makeBuffer(.{
.data = sg.asRange(&[_]u16{ 0, 1, 2, 2, 1, 3 }),
.usage = .{
.immutable = true,
.index_buffer = true,
},
});
pipeline = sg.makePipeline(.{
.shader = sg.makeShader(shaders.mainShaderDesc(sg.queryBackend())),
.layout = blk: {
var ret: sg.VertexLayoutState = .{};
ret.attrs[shaders.ATTR_main_positionOS].format = .FLOAT3;
ret.attrs[shaders.ATTR_main_texCoord].format = .FLOAT2;
ret.attrs[shaders.ATTR_main_normalOS].format = .FLOAT3;
ret.attrs[shaders.ATTR_main_tangentOS].format = .FLOAT4;
break :blk ret;
},
.depth = .{
.compare = .GREATER,
},
.primitive_type = .TRIANGLES,
.index_type = .UINT16,
.cull_mode = .BACK,
.face_winding = .CCW,
});
const sampler = samplers.getOrCreateSampler(.{
.wrap_u = .REPEAT,
.wrap_v = .REPEAT,
.wrap_w = .REPEAT,
.min_filter = .LINEAR,
.mag_filter = .LINEAR,
.mipmap_filter = .LINEAR,
}) catch unreachable;
base_color_texture = sg.makeImage(.{
.type = .ARRAY,
.usage = .{ .immutable = true },
.width = @intCast(base_color_image.width),
.height = @intCast(base_color_image.height),
.num_slices = @intCast(base_color_image.depth),
.pixel_format = .RGBA8,
.data = blk: {
var ret: sg.ImageData = .{};
ret.mip_levels[0] = sg.asRange(base_color_image.data);
break :blk ret;
},
});
base_color_texture_view = sg.makeView(.{
.texture = .{ .image = base_color_texture },
});
occlusion_roughness_metallic_texture = sg.makeImage(.{
.type = .ARRAY,
.usage = .{ .immutable = true },
.width = @intCast(occlusion_roughness_metallic_image.width),
.height = @intCast(occlusion_roughness_metallic_image.height),
.num_slices = @intCast(occlusion_roughness_metallic_image.depth),
.pixel_format = .RGBA8,
.data = blk: {
var ret: sg.ImageData = .{};
ret.mip_levels[0] = sg.asRange(occlusion_roughness_metallic_image.data);
break :blk ret;
},
});
occlusion_roughness_metallic_texture_view = sg.makeView(.{
.texture = .{ .image = occlusion_roughness_metallic_texture },
});
normal_texture = sg.makeImage(.{
.type = .ARRAY,
.usage = .{ .immutable = true },
.width = @intCast(normal_image.width),
.height = @intCast(normal_image.height),
.num_slices = @intCast(normal_image.depth),
.pixel_format = .RGBA8,
.data = blk: {
var ret: sg.ImageData = .{};
ret.mip_levels[0] = sg.asRange(normal_image.data);
break :blk ret;
},
});
normal_texture_view = sg.makeView(.{
.texture = .{ .image = normal_texture },
});
bindings = .{};
bindings.vertex_buffers[0] = vertex_buffer;
bindings.index_buffer = index_buffer;
bindings.views[shaders.VIEW_Point_Lights] = game.point_light_buffer_view;
bindings.views[shaders.VIEW_Directional_Lights] = game.directional_light_buffer_view;
bindings.views[shaders.VIEW__BaseColorTexture] = base_color_texture_view;
bindings.views[shaders.VIEW__OcclusionRoughnessMetallicTexture] = occlusion_roughness_metallic_texture_view;
bindings.views[shaders.VIEW__NormalTexture] = normal_texture_view;
bindings.samplers[shaders.SMP__Sampler] = sampler;
}
pub fn deinit() void {
sg.destroyView(normal_texture_view);
sg.destroyImage(normal_texture);
sg.destroyView(occlusion_roughness_metallic_texture_view);
sg.destroyImage(occlusion_roughness_metallic_texture);
sg.destroyView(base_color_texture_view);
sg.destroyImage(base_color_texture);
sg.destroyPipeline(pipeline);
sg.destroyBuffer(index_buffer);
sg.destroyBuffer(vertex_buffer);
}
pub fn bind() void {
sg.applyPipeline(pipeline);
sg.applyBindings(bindings);
}
pub fn draw(translation: Vector3, rotation: Quaternion, texture_index: u32) void {
const matrix_os_to_ws = Matrix4x4.initTranslationRotationScale(translation, rotation, .one);
const matrix_os_to_ws_normal = Matrix4x4.inverseTransposeAffine(matrix_os_to_ws);
sg.applyUniforms(shaders.UB_Material_Uniforms, sg.asRange(&shaders.MaterialUniforms{
._TextureIndex = @intCast(texture_index),
}));
sg.applyUniforms(shaders.UB_Object_Uniforms, sg.asRange(&shaders.ObjectUniforms{
._MatrixOStoWS = matrix_os_to_ws.asArray(),
._MatrixOStoWSNormal = matrix_os_to_ws_normal.asArray(),
}));
sg.draw(0, 6, 1);
}