diff --git a/src/asset_pipeline.zig b/src/asset_pipeline.zig index c1b2f4d..ca3209d 100644 --- a/src/asset_pipeline.zig +++ b/src/asset_pipeline.zig @@ -17,7 +17,7 @@ pub const Texture = struct { 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); 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(); while (it.next()) |entry| { allocator.free(entry.key_ptr.*); diff --git a/src/game.zig b/src/game.zig index e7f7285..c101523 100644 --- a/src/game.zig +++ b/src/game.zig @@ -8,78 +8,36 @@ const sapp = sokol.app; const sg = sokol.gfx; const sglue = sokol.glue; -const ap = @import("asset_pipeline.zig"); const main = @import("main.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 Quaternion = math.Quaternion; +const Vector2 = math.Vector2; const Vector3 = math.Vector3; var show_console: 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 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 { - 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(.{ .size = @sizeOf([4]shaders.PointLight), .usage = .{ @@ -87,6 +45,9 @@ pub fn init() void { .storage_buffer = true, }, }); + point_light_buffer_view = sg.makeView(.{ + .storage_buffer = .{ .buffer = point_light_buffer }, + }); directional_light_buffer = sg.makeBuffer(.{ .size = @sizeOf([4]shaders.DirectionalLight), @@ -95,86 +56,33 @@ pub fn init() void { .storage_buffer = true, }, }); - - 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(.{ + directional_light_buffer_view = sg.makeView(.{ .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 { - _ = dt; + sg.destroyView(directional_light_buffer_view); + 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) { showConsole(); @@ -205,44 +113,85 @@ pub fn update(dt: f64) void { .swapchain = sglue.swapchain(), }); - sg.applyPipeline(pipeline); - sg.applyBindings(bindings); + tiles.bind(); - const matrix_ws_to_vs = Matrix4x4.identity; - const matrix_vs_to_cs = Matrix4x4.identity; - const ambient_light = Vector3.init(0.01, 0.01, 0.01); + const camera_rotation = Quaternion.mulQuaternion( + .initRotationXY(camera_yaw), + .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{ ._MatrixWStoVS = matrix_ws_to_vs.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{ ._MatrixWStoVS = matrix_ws_to_vs.asArray(), ._AmbientLight = ambient_light.asArray(), - ._PointLightCount = 0, + ._PointLightCount = @intCast(point_lights.len), ._DirectionalLightCount = 0, })); - sg.applyUniforms(shaders.UB_Material_Uniforms, sg.asRange(&shaders.MaterialUniforms{ - ._TextureIndex = 0, - })); + var it = Iterator3.init(.init(-10, -10, 0), .init(10, 10, 0), .one); + 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(); } +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 { const no_mods = mods == 0; + if (key_code == .ESCAPE and no_mods) { + sapp.requestQuit(); + } + if (key_code == .GRAVE_ACCENT and no_mods) { 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) { 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 { diff --git a/src/main.zig b/src/main.zig index 62d7788..a3e5ffd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -47,6 +47,8 @@ fn init() callconv(.c) void { .logger = .{ .func = sokolLogFn }, }); + sapp.lockMouse(true); + game.init(); } @@ -66,7 +68,7 @@ fn frame() callconv(.c) void { .dpi_scale = sapp.dpiScale(), }); - game.update(dt); + game.update(@floatCast(dt)); // --- 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) { 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); } } diff --git a/src/math.zig b/src/math.zig index e9926a4..5c940f5 100644 --- a/src/math.zig +++ b/src/math.zig @@ -1,3 +1,4 @@ +pub const Iterator3 = @import("math/Iterator3.zig"); pub const Matrix4x4 = @import("math/Matrix4x4.zig").Matrix4x4; pub const Quaternion = @import("math/Quaternion.zig").Quaternion; pub const Vector2 = @import("math/Vector2.zig").Vector2; diff --git a/src/math/Iterator3.zig b/src/math/Iterator3.zig new file mode 100644 index 0000000..0a51f14 --- /dev/null +++ b/src/math/Iterator3.zig @@ -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; +} diff --git a/src/math/Vector2.zig b/src/math/Vector2.zig index 4d5d033..419f326 100644 --- a/src/math/Vector2.zig +++ b/src/math/Vector2.zig @@ -30,12 +30,12 @@ pub const Vector2 = extern struct { pub inline fn asVector3(self: Vector2, z: f32) Vector3 { 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 { 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 --- @@ -50,12 +50,12 @@ pub const Vector2 = extern struct { pub inline fn setX(self: Vector2, x: f32) Vector2 { 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 { 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 --- @@ -138,8 +138,8 @@ pub const Vector2 = extern struct { const s = @sin(angle_rad); return .{ .vector = .{ - self.x * c - self.y * s, - self.x * s + self.x * c, + self.getX() * c - self.getY() * s, + self.getX() * s + self.getY() * c, }, }; } diff --git a/src/math/Vector3.zig b/src/math/Vector3.zig index 05904b8..f9229af 100644 --- a/src/math/Vector3.zig +++ b/src/math/Vector3.zig @@ -31,12 +31,12 @@ pub const Vector3 = extern struct { } 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 { 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 --- @@ -55,17 +55,17 @@ pub const Vector3 = extern struct { pub inline fn setX(self: Vector3, x: f32) Vector3 { 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 { 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 { 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 --- diff --git a/src/math/Vector4.zig b/src/math/Vector4.zig index 9ecd3c3..3e64694 100644 --- a/src/math/Vector4.zig +++ b/src/math/Vector4.zig @@ -31,11 +31,11 @@ pub const Vector4 = extern struct { } 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 { - return .{ .vector = @shuffle(f32, self.vector, undefined, .{ 0, 1, 2 }) }; + return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1, 2 }) }; } // --- ACCESSORS --- @@ -58,22 +58,22 @@ pub const Vector4 = extern struct { pub inline fn setX(self: Vector4, x: f32) Vector4 { 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 { 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 { 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 { 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 --- diff --git a/src/Samplers.zig b/src/samplers.zig similarity index 100% rename from src/Samplers.zig rename to src/samplers.zig diff --git a/src/tiles.zig b/src/tiles.zig new file mode 100644 index 0000000..51021b3 --- /dev/null +++ b/src/tiles.zig @@ -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); +}