diff --git a/.ignore b/.ignore new file mode 100644 index 0000000..f6ebcfc --- /dev/null +++ b/.ignore @@ -0,0 +1,5 @@ +/data/**.png +/data/**.bin +/data/**.blend +/data/**.blend1 +/.git diff --git a/data/cubemap.bin b/data/cubemap.bin new file mode 100644 index 0000000..b44dd6f Binary files /dev/null and b/data/cubemap.bin differ diff --git a/data/cubemap.gltf b/data/cubemap.gltf new file mode 100644 index 0000000..99b448a --- /dev/null +++ b/data/cubemap.gltf @@ -0,0 +1,142 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"cubemap" + } + ], + "materials":[ + { + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material_0", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Cube.001", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"cubemap.png", + "uri":"cubemap.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 0.4999999701976776, + 0.5, + 0.5 + ], + "min":[ + -0.4999999701976776, + -0.5, + -0.5 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":576, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9729, + "minFilter":9987 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"cubemap.bin" + } + ] +} diff --git a/data/hand.bin b/data/hand.bin new file mode 100644 index 0000000..230f2b9 Binary files /dev/null and b/data/hand.bin differ diff --git a/data/hand.gltf b/data/hand.gltf new file mode 100644 index 0000000..e09a730 --- /dev/null +++ b/data/hand.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"hand" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane.001", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"hand.png", + "uri":"hand.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":4, + "max":[ + 0.5, + 0.5, + 0 + ], + "min":[ + -0.5, + -0.5, + 0 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":4, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":4, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":6, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":48, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":48, + "byteOffset":48, + "target":34962 + }, + { + "buffer":0, + "byteLength":32, + "byteOffset":96, + "target":34962 + }, + { + "buffer":0, + "byteLength":12, + "byteOffset":128, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":140, + "uri":"hand.bin" + } + ] +} diff --git a/data/yakuza/card.bin b/data/yakuza/card.bin new file mode 100644 index 0000000..2967a5f Binary files /dev/null and b/data/yakuza/card.bin differ diff --git a/data/yakuza/card.gltf b/data/yakuza/card.gltf new file mode 100644 index 0000000..c83e0b1 --- /dev/null +++ b/data/yakuza/card.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"card" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane.002", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"game.png", + "uri":"game.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":120, + "max":[ + 0.25, + 0.25, + 0.00390625 + ], + "min":[ + -0.25, + -0.25, + -0.00390625 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":120, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":120, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":228, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":1440, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":1440, + "byteOffset":1440, + "target":34962 + }, + { + "buffer":0, + "byteLength":960, + "byteOffset":2880, + "target":34962 + }, + { + "buffer":0, + "byteLength":456, + "byteOffset":3840, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":4296, + "uri":"card.bin" + } + ] +} diff --git a/data/yakuza.png b/data/yakuza/game.png similarity index 100% rename from data/yakuza.png rename to data/yakuza/game.png diff --git a/data/yakuza/pad.bin b/data/yakuza/pad.bin new file mode 100644 index 0000000..fab5724 Binary files /dev/null and b/data/yakuza/pad.bin differ diff --git a/data/yakuza/pad.gltf b/data/yakuza/pad.gltf new file mode 100644 index 0000000..ee42156 --- /dev/null +++ b/data/yakuza/pad.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"pad" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane.003", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"game.png", + "uri":"game.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":1272, + "max":[ + 0.4140625, + 0.4140625, + 0.00390625 + ], + "min":[ + -0.4140625, + -0.4140625, + -0.00390625 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":1272, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":1272, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":2532, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":15264, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":15264, + "byteOffset":15264, + "target":34962 + }, + { + "buffer":0, + "byteLength":10176, + "byteOffset":30528, + "target":34962 + }, + { + "buffer":0, + "byteLength":5064, + "byteOffset":40704, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":45768, + "uri":"pad.bin" + } + ] +} diff --git a/data/yakuza/table.bin b/data/yakuza/table.bin new file mode 100644 index 0000000..e560025 Binary files /dev/null and b/data/yakuza/table.bin differ diff --git a/data/yakuza/table.gltf b/data/yakuza/table.gltf new file mode 100644 index 0000000..b9a135a --- /dev/null +++ b/data/yakuza/table.gltf @@ -0,0 +1,144 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.5.48", + "version":"2.0" + }, + "extensionsUsed":[ + "KHR_materials_unlit" + ], + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"table" + } + ], + "materials":[ + { + "alphaMode":"BLEND", + "doubleSided":true, + "extensions":{ + "KHR_materials_unlit":{} + }, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorTexture":{ + "index":0 + }, + "metallicFactor":0, + "roughnessFactor":0.9 + } + } + ], + "meshes":[ + { + "name":"Plane", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "textures":[ + { + "sampler":0, + "source":0 + } + ], + "images":[ + { + "mimeType":"image/png", + "name":"game.png", + "uri":"game.png" + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 4, + 4, + 0 + ], + "min":[ + -4, + -4, + -0.5 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":576, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "samplers":[ + { + "magFilter":9728, + "minFilter":9984 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"table.bin" + } + ] +} diff --git a/src/assets.zig b/src/assets.zig index ba8e505..99c4471 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -1,11 +1,17 @@ const std = @import("std"); +const builtin = @import("builtin"); const err = @import("error.zig"); const Game = @import("game.zig"); const FileLoader = @import("assets/file.zig"); const TextureLoader = @import("assets/texture.zig"); +const GltfLoader = @import("assets/gltf.zig"); const Assets = @This(); +// TODO: Unload assets in correct order to account for asset dependencies + +pub const File = AssetContainer(FileLoader); pub const Texture = AssetContainer(TextureLoader); +pub const Object = AssetContainer(GltfLoader); const WORKERS_MAX = 4; var next_worker_update: usize = 0; @@ -50,16 +56,18 @@ pub const LoadError = error{ ParsingError, SdlError, FileTooBig, -} || std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError; +} || std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.json.ParseError(std.json.Scanner); pub const AssetType = enum { file, texture, + gltf, pub fn getType(comptime self: @This()) type { return switch (self) { .file => FileLoader, .texture => TextureLoader, + .gltf => GltfLoader, }; } }; @@ -82,6 +90,8 @@ pub const AssetCell = struct { fn load(self: *AssetCell, alloc: std.mem.Allocator) void { self.loader(self, alloc) catch |e| { + if (builtin.mode == .Debug) + std.debug.panic("Asset loading error: {s} - {}!\n", .{ self.path, e }); self.state = .{ .fail = e }; return; }; @@ -119,6 +129,8 @@ pub fn AssetContainer(comptime T: type) type { }, } } + // TODO: Add smth like `Assets.immediateLoad` + /// To be used by worker threads to request other assets pub fn getSync(self: *@This()) !*T { sw: switch (self.last_state) { @@ -168,6 +180,7 @@ pub fn deinit() void { std.debug.assert(asset.*.counter == 0); if (asset.*.state == .loaded) asset.*.unload(Game.alloc); + Game.alloc.free(asset.*.path); Game.alloc.destroy(asset.*); } Assets.asset_map.clearAndFree(Game.alloc); @@ -202,6 +215,7 @@ pub fn update() void { if (!Assets.asset_map.remove(.{ .type = request.type, .path = request.path })) continue; if (request.state == .loaded) request.unload(Game.alloc); + Game.alloc.free(request.path); Game.alloc.destroy(request); } } @@ -259,7 +273,7 @@ fn mapAsset(comptime asset_type: AssetType, path: []const u8) *AssetCell { .mutex = .{}, .type = asset_type, .data = undefined, - .path = path, + .path = Game.alloc.dupe(u8, path) catch err.oom(), .loader = Assets.makeLoader(asset_type.getType(), asset_type.getType().load), .unloader = Assets.makeUnloader(asset_type.getType(), asset_type.getType().unload), .state = .not_loaded, diff --git a/src/assets/gltf.zig b/src/assets/gltf.zig new file mode 100644 index 0000000..0a5e96d --- /dev/null +++ b/src/assets/gltf.zig @@ -0,0 +1,389 @@ +const std = @import("std"); +const sdl = @import("sdl"); +const err = @import("../error.zig"); +const Assets = @import("../assets.zig"); +const Graphics = @import("../graphics.zig"); + +nodes: []Node, +meshes: []Mesh, + +const Node = struct { + mesh: u32, +}; + +const Mesh = struct { + primitives: []Primitive, +}; + +const Primitive = struct { + vertex_buffer: *sdl.GPUBuffer, + index_buffer: ?*sdl.GPUBuffer = null, + + vertices: u32, + indices: u32 = 0, + texture: Assets.Texture, +}; + +const GltfJson = struct { + scene: u32, + scenes: []GltfSceneJson, + nodes: []GltfNodeJson, + materials: []GltfMaterialJson, + meshes: []GltfMeshJson, + textures: []GltfTextureJson, + images: []GltfImageJson, + accessors: []GltfAccessorJson, + bufferViews: []GltfBufferViewJson, + samplers: []GltfSamplerJson, + buffers: []GltfBufferJson, + + const GltfSceneJson = struct { + nodes: []u32, + }; + const GltfNodeJson = struct { + mesh: u32, + }; + const GltfMaterialJson = struct { + pbrMetallicRoughness: GltfPbrMRJson, + + const GltfPbrMRJson = struct { + baseColorTexture: GltfPbrMRBaseColorTexture, + + const GltfPbrMRBaseColorTexture = struct { + index: u32, + }; + }; + }; + const GltfMeshJson = struct { + primitives: []GltfPrimitiveJson, + + const GltfPrimitiveJson = struct { + attributes: GltfAttributesJson, + indices: u32, + material: u32, + + const GltfAttributesJson = struct { + POSITION: u32, + TEXCOORD_0: u32, + }; + }; + }; + const GltfTextureJson = struct { + sampler: ?u32, + source: u32, + }; + const GltfImageJson = struct { + uri: []u8, + }; + const GltfAccessorJson = struct { + bufferView: u32, + componentType: GltfComponentTypeJson, + count: u32, + type: GltfAccessorTypeJson, + + const GltfComponentTypeJson = enum(u32) { + i8 = 5120, + u8 = 5121, + i16 = 5122, + u16 = 5123, + i32 = 5125, + f32 = 5126, + }; + const GltfAccessorTypeJson = enum { + SCALAR, + VEC2, + VEC3, + VEC4, + MAT2, + MAT3, + MAT4, + }; + + pub fn slice(accessor: @This(), comptime T: type, views: []const GltfBufferViewJson, buffers: []Assets.File) !?[]align(1) T { + if (accessor.bufferView >= views.len) return null; + const view = &views[accessor.bufferView]; + + if (view.buffer >= buffers.len) return null; + const buffer = try buffers[view.buffer].getSync(); + + const component_length = switch (T) { + [3]f32 => 12, + [2]f32 => 8, + u16 => 2, + else => @compileError("Accessor of " ++ @tagName(accessor.type) ++ " of " ++ @tagName(accessor.componentType) ++ " is not supported."), + }; + + const start = view.byteOffset; + const length = component_length * accessor.count; + const end = start + length; + + if (length != view.byteLength) return error.ParsingError; + + return @as([]align(1) T, @ptrCast(buffer.bytes[start..end])); + } + }; + const GltfBufferViewJson = struct { + buffer: u32, + byteLength: u32, + byteOffset: u32 = 0, + }; + const GltfSamplerJson = struct { + magFilter: GltfFilterJson = .NEAREST, + minFilter: GltfFilterJson = .LINEAR, + wrapS: GltfWrapJson = .CLAMP_TO_EDGE, + wrapT: GltfWrapJson = .CLAMP_TO_EDGE, + + const GltfFilterJson = enum(u32) { + NEAREST = 9728, + LINEAR = 9729, + NEAREST_MIPMAP_NEAREST = 9984, + LINEAR_MIPMAP_NEAREST = 9985, + NEAREST_MIPMAP_LINEAR = 9986, + LINEAR_MIPMAP_LINEAR = 9987, + }; + const GltfWrapJson = enum(u32) { + CLAMP_TO_EDGE = 33071, + MIRRORED_REPEAT = 33648, + REPEAT = 10497, + }; + }; + const GltfBufferJson = struct { + // byteLength: u32, + uri: []u8, + }; +}; + +pub fn load(path: []const u8, alloc: std.mem.Allocator) Assets.LoadError!@This() { + var json = Assets.load(.file, path); + defer Assets.free(json); + + const parsed_gltf = try std.json.parseFromSlice( + GltfJson, + alloc, + (try json.getSync()).bytes, + .{ .ignore_unknown_fields = true }, + ); + defer parsed_gltf.deinit(); + const gltf = &parsed_gltf.value; + + if (gltf.scene >= gltf.scenes.len) return error.ParsingError; + const scene = &gltf.scenes[gltf.scene]; + + var buffers_init: u32 = 0; + const buffers = try alloc.alloc(Assets.File, gltf.buffers.len); + defer alloc.free(buffers); + + defer for (buffers[0..buffers_init]) |buffer| { + Assets.free(buffer); + }; + + for (0.., buffers) |i, *buffer| { + const buffer_path = try std.fs.path.join(alloc, &.{ std.fs.path.dirname(path) orelse return error.ParsingError, gltf.buffers[i].uri }); + defer alloc.free(buffer_path); + buffer.* = Assets.load(.file, buffer_path); + buffers_init += 1; + } + + const nodes = try alloc.alloc(Node, scene.nodes.len); + errdefer alloc.free(nodes); + + var meshes_init: u32 = 0; + const meshes = try alloc.alloc(Mesh, gltf.meshes.len); + errdefer alloc.free(nodes); + + errdefer for (meshes[0..meshes_init]) |*mesh| { + for (mesh.primitives) |*primitive| { + sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + if (primitive.index_buffer) |buf| sdl.ReleaseGPUBuffer(Graphics.device, buf); + Assets.free(primitive.texture); + } + }; + + for (0.., nodes) |i, *node| { + const node_index = scene.nodes[i]; + if (node_index >= gltf.nodes.len) return error.ParsingError; + const gltf_node = &gltf.nodes[node_index]; + if (gltf_node.mesh >= gltf.meshes.len) return error.ParsingError; + node.mesh = gltf_node.mesh; + } + for (0.., meshes) |i, *mesh| { + const gltf_mesh = &gltf.meshes[i]; + + var primitivs_init: u32 = 0; + const primitives = try alloc.alloc(Primitive, gltf_mesh.primitives.len); + errdefer alloc.free(primitives); + + errdefer for (primitives[0..primitivs_init]) |*primitive| { + sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + if (primitive.index_buffer) |buf| sdl.ReleaseGPUBuffer(Graphics.device, buf); + Assets.free(primitive.texture); + }; + + for (0.., primitives) |j, *primitive| { + const gltf_primitive = gltf_mesh.primitives[j]; + + if (gltf_primitive.attributes.POSITION >= gltf.accessors.len) return error.ParsingError; + if (gltf_primitive.attributes.TEXCOORD_0 >= gltf.accessors.len) return error.ParsingError; + if (gltf_primitive.material >= gltf.materials.len) return error.ParsingError; + const material = &gltf.materials[gltf_primitive.material]; + const texture_index = material.pbrMetallicRoughness.baseColorTexture.index; + if (texture_index >= gltf.textures.len) return error.ParsingError; + const texture = &gltf.textures[texture_index]; + if (texture.source >= gltf.images.len) return error.ParsingError; + const image = &gltf.images[texture.source]; + + const position = try gltf.accessors[gltf_primitive.attributes.POSITION].slice([3]f32, gltf.bufferViews, buffers) orelse return error.ParsingError; + const uv = try gltf.accessors[gltf_primitive.attributes.TEXCOORD_0].slice([2]f32, gltf.bufferViews, buffers) orelse return error.ParsingError; + const index = try gltf.accessors[gltf_primitive.indices].slice(u16, gltf.bufferViews, buffers) orelse return error.ParsingError; + + primitive.vertex_buffer, primitive.index_buffer = try loadMesh(position, uv, index, alloc); + errdefer sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + errdefer sdl.ReleaseGPUBuffer(Graphics.device, primitive.index_buffer); + + primitive.vertices = @intCast(position.len); + primitive.indices = @intCast(index.len); + + const texture_path = try std.fs.path.join(alloc, &.{ std.fs.path.dirname(path) orelse return error.ParsingError, image.uri }); + defer alloc.free(texture_path); + primitive.texture = Assets.load(.texture, texture_path); + + primitivs_init += 1; + } + + mesh.primitives = primitives; + meshes_init += 1; + } + + return .{ + .nodes = nodes, + .meshes = meshes, + }; +} + +pub fn unload(self: @This(), alloc: std.mem.Allocator) void { + for (self.meshes) |mesh| { + for (mesh.primitives) |*primitive| { + sdl.ReleaseGPUBuffer(Graphics.device, primitive.vertex_buffer); + sdl.ReleaseGPUBuffer(Graphics.device, primitive.index_buffer); + Assets.free(primitive.texture); + } + alloc.free(mesh.primitives); + } + alloc.free(self.meshes); + alloc.free(self.nodes); +} + +pub fn loadMesh(position: []align(1) const [3]f32, uv: []align(1) const [2]f32, index: []align(1) const u16, alloc: std.mem.Allocator) !struct { *sdl.GPUBuffer, *sdl.GPUBuffer } { + if (position.len != uv.len) return error.ParsingError; + const vertices: u32 = @intCast(position.len); + const indices: u32 = @intCast(index.len); + + const BYTES_PER_VERTEX = 20; + const BYTES_PER_INDEX = 2; + + const vertex_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ + .size = vertices * BYTES_PER_VERTEX, + .usage = sdl.GPU_BUFFERUSAGE_VERTEX, + }) orelse return error.SdlError; + errdefer sdl.ReleaseGPUBuffer(Graphics.device, vertex_buffer); + + const index_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ + .size = indices * 2, + .usage = sdl.GPU_BUFFERUSAGE_INDEX, + }) orelse return error.SdlError; + errdefer sdl.ReleaseGPUBuffer(Graphics.device, index_buffer); + + const TRANSFER_CAPACITY = Graphics.TRANSFER_BUFFER_DEFAULT_CAPACITY; + + const transfer_buffer = sdl.CreateGPUTransferBuffer(Graphics.device, &.{ + .size = TRANSFER_CAPACITY, + .usage = sdl.GPU_TRANSFERBUFFERUSAGE_UPLOAD, + }) orelse return error.SdlError; + defer sdl.ReleaseGPUTransferBuffer(Graphics.device, transfer_buffer); + + const buffer = try alloc.alloc(u8, TRANSFER_CAPACITY); + defer alloc.free(buffer); + + var vertices_uploaded: u32 = 0; + while (vertices_uploaded < vertices) { + const vertices_to_upload = @min(vertices - vertices_uploaded, TRANSFER_CAPACITY / BYTES_PER_VERTEX); + if (vertices_to_upload == 0) return error.FileTooBig; + + for (0..vertices_to_upload) |i| { + const V = packed struct { x: f32, y: f32, z: f32, u: f32, v: f32 }; + std.mem.copyForwards( + u8, + buffer[BYTES_PER_VERTEX * i ..], + &@as([BYTES_PER_VERTEX]u8, @bitCast(V{ + .x = position[vertices_uploaded + i][0], + .y = position[vertices_uploaded + i][1], + .z = position[vertices_uploaded + i][2], + .u = uv[vertices_uploaded + i][0], + .v = uv[vertices_uploaded + i][1], + })), + ); + } + + const command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse return error.SdlError; + { + errdefer _ = sdl.CancelGPUCommandBuffer(command_buffer); + const copy_pass = sdl.BeginGPUCopyPass(command_buffer) orelse return error.SdlError; + defer sdl.EndGPUCopyPass(copy_pass); + + const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, transfer_buffer, false) orelse err.sdl()); + @memcpy(map, buffer[0 .. vertices_to_upload * BYTES_PER_VERTEX]); + sdl.UnmapGPUTransferBuffer(Graphics.device, transfer_buffer); + + sdl.UploadToGPUBuffer(copy_pass, &.{ + .transfer_buffer = transfer_buffer, + }, &.{ + .buffer = vertex_buffer, + .offset = vertices_uploaded * BYTES_PER_VERTEX, + .size = vertices_to_upload * BYTES_PER_VERTEX, + }, false); + } + vertices_uploaded += vertices_to_upload; + const fence = sdl.SubmitGPUCommandBufferAndAcquireFence(command_buffer) orelse return error.SdlError; + defer sdl.ReleaseGPUFence(Graphics.device, fence); + if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) return error.SdlError; + } + + var indices_uploaded: u32 = 0; + while (indices_uploaded < indices) { + const indices_to_upload = @min(indices - indices_uploaded, TRANSFER_CAPACITY / BYTES_PER_INDEX); + if (indices_to_upload == 0) return error.FileTooBig; + + for (0..indices_to_upload) |i| { + std.mem.copyForwards( + u8, + buffer[BYTES_PER_INDEX * i ..], + &@as([BYTES_PER_INDEX]u8, @bitCast(index[i])), + ); + } + + const command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse return error.SdlError; + { + errdefer _ = sdl.CancelGPUCommandBuffer(command_buffer); + const copy_pass = sdl.BeginGPUCopyPass(command_buffer) orelse return error.SdlError; + defer sdl.EndGPUCopyPass(copy_pass); + + const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, transfer_buffer, false) orelse err.sdl()); + @memcpy(map, buffer[0 .. indices_to_upload * BYTES_PER_INDEX]); + sdl.UnmapGPUTransferBuffer(Graphics.device, transfer_buffer); + + sdl.UploadToGPUBuffer(copy_pass, &.{ + .transfer_buffer = transfer_buffer, + }, &.{ + .buffer = index_buffer, + .offset = indices_uploaded * BYTES_PER_INDEX, + .size = indices_to_upload * BYTES_PER_INDEX, + }, false); + } + indices_uploaded += indices_to_upload; + const fence = sdl.SubmitGPUCommandBufferAndAcquireFence(command_buffer) orelse return error.SdlError; + defer sdl.ReleaseGPUFence(Graphics.device, fence); + if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) return error.SdlError; + } + + return .{ vertex_buffer, index_buffer }; +} diff --git a/src/graphics.zig b/src/graphics.zig index b377556..32966de 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -7,11 +7,6 @@ const Assets = @import("assets.zig"); pub const Transform = @import("graphics/transform.zig"); pub const Camera = @import("graphics/camera.zig"); -pub const Mesh = struct { - vertex_start: usize, - vertex_count: usize, -}; - pub var window: *sdl.Window = undefined; pub var device: *sdl.GPUDevice = undefined; /// Only available while drawing @@ -22,13 +17,6 @@ var render_target: ?*sdl.GPUTexture = null; var shader_vert: *sdl.GPUShader = undefined; var shader_frag: *sdl.GPUShader = undefined; -var vertex_buffer: *sdl.GPUBuffer = undefined; -var vertex_buffer_capacity: usize = undefined; -var vertex_buffer_used: usize = undefined; - -var transfer_buffer: *sdl.GPUTransferBuffer = undefined; -var transfer_buffer_capacity: usize = undefined; - var depth_texture: *sdl.GPUTexture = undefined; var fsaa_target: *sdl.GPUTexture = undefined; var pipeline: *sdl.GPUGraphicsPipeline = undefined; @@ -40,11 +28,9 @@ var fsaa_level: u32 = 3; pub var camera: Camera = undefined; -const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024; -const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2; const BYTES_PER_VERTEX = 5 * 4; const DEPTH_FORMAT = sdl.GPU_TEXTUREFORMAT_D32_FLOAT; -pub const TRANSFER_BUFFER_DEFAULT_CAPACITY = 256 * 1024; +pub const TRANSFER_BUFFER_DEFAULT_CAPACITY = 512 * 1024; pub const MIP_LEVEL = 4; const Graphics = @This(); @@ -92,19 +78,6 @@ pub fn create() void { }, ); - Graphics.vertex_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ - .usage = sdl.GPU_BUFFERUSAGE_VERTEX, - .size = VERTEX_BUFFER_DEFAULT_CAPACITY, - }) orelse err.sdl(); - Graphics.vertex_buffer_capacity = VERTEX_BUFFER_DEFAULT_CAPACITY; - Graphics.vertex_buffer_used = 0; - - Graphics.transfer_buffer = sdl.CreateGPUTransferBuffer(Graphics.device, &.{ - .size = TRANSFER_BUFFER_DEFAULT_CAPACITY, - .usage = sdl.GPU_TRANSFERBUFFERUSAGE_UPLOAD | sdl.GPU_TRANSFERBUFFERUSAGE_DOWNLOAD, - }) orelse err.sdl(); - Graphics.transfer_buffer_capacity = TRANSFER_BUFFER_DEFAULT_CAPACITY; - const target_format = sdl.GetGPUSwapchainTextureFormat(Graphics.device, Graphics.window); if (target_format == sdl.GPU_TEXTUREFORMAT_INVALID) err.sdl(); @@ -183,8 +156,6 @@ pub fn destroy() void { sdl.ReleaseGPUGraphicsPipeline(Graphics.device, Graphics.pipeline); sdl.ReleaseGPUTexture(Graphics.device, Graphics.fsaa_target); sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture); - sdl.ReleaseGPUBuffer(Graphics.device, Graphics.vertex_buffer); - sdl.ReleaseGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer); sdl.ReleaseGPUShader(Graphics.device, Graphics.shader_vert); sdl.ReleaseGPUShader(Graphics.device, Graphics.shader_frag); @@ -196,98 +167,6 @@ pub fn destroy() void { sdl.DestroyGPUDevice(Graphics.device); } -pub fn loadMesh(mesh_bytes: []const u8) Mesh { - std.debug.assert(mesh_bytes.len < Graphics.transfer_buffer_capacity); - - var size_mult: usize = 1; - while (Graphics.vertex_buffer_used + mesh_bytes.len > Graphics.vertex_buffer_capacity * size_mult) { - size_mult *= VERTEX_BUFFER_GROWTH_MULTIPLIER; - } - if (size_mult > 1) { - Graphics.growVertexBuffer(Graphics.vertex_buffer_capacity * size_mult); - } - - const map = sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, true) orelse err.sdl(); - @memcpy(@as([*]u8, @ptrCast(map)), mesh_bytes); - sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer); - - const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl(); - const fence = blk: { - const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer) orelse err.sdl(); - sdl.UploadToGPUBuffer(copy_pass, &.{ - .transfer_buffer = Graphics.transfer_buffer, - .offset = 0, - }, &.{ - .buffer = Graphics.vertex_buffer, - .offset = @intCast(Graphics.vertex_buffer_used), - .size = @intCast(mesh_bytes.len), - }, false); - sdl.EndGPUCopyPass(copy_pass); - - break :blk sdl.SubmitGPUCommandBufferAndAcquireFence(temp_command_buffer) orelse err.sdl(); - }; - defer sdl.ReleaseGPUFence(Graphics.device, fence); - - if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) err.sdl(); - - const vertex_start = Graphics.vertex_buffer_used; - Graphics.vertex_buffer_used += mesh_bytes.len; - - return Mesh{ - .vertex_start = vertex_start / BYTES_PER_VERTEX, - .vertex_count = mesh_bytes.len / BYTES_PER_VERTEX, - }; -} - -pub fn unloadMesh(mesh: Mesh) void { - // TODO: free some memory - _ = &mesh; -} - -fn growVertexBuffer(new_size: usize) void { - const new_buffer = sdl.CreateGPUBuffer(Graphics.device, &.{ - .size = @intCast(new_size), - .usage = sdl.GPU_BUFFERUSAGE_VERTEX, - }) orelse err.sdl(); - - const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl(); - - const fence = blk: { - const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer); - var copied: usize = 0; - while (copied < Graphics.vertex_buffer_used) { - const to_transer = @min(Graphics.vertex_buffer_used - copied, Graphics.transfer_buffer_capacity); - sdl.DownloadFromGPUBuffer(copy_pass, &.{ - .buffer = Graphics.vertex_buffer, - .offset = @intCast(copied), - .size = @intCast(to_transer), - }, &.{ - .transfer_buffer = Graphics.transfer_buffer, - .offset = 0, - }); - sdl.UploadToGPUBuffer(copy_pass, &.{ - .transfer_buffer = Graphics.transfer_buffer, - .offset = 0, - }, &.{ - .buffer = new_buffer, - .offset = @intCast(copied), - .size = @intCast(to_transer), - }, false); - copied += to_transer; - } - sdl.EndGPUCopyPass(copy_pass); - - break :blk sdl.SubmitGPUCommandBufferAndAcquireFence(temp_command_buffer) orelse err.sdl(); - }; - defer sdl.ReleaseGPUFence(Graphics.device, fence); - - if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) err.sdl(); - - sdl.ReleaseGPUBuffer(Graphics.device, Graphics.vertex_buffer); - Graphics.vertex_buffer = new_buffer; - Graphics.vertex_buffer_capacity = new_size; -} - /// If window is minimized returns `false`, `render_pass` remains null /// Otherwise `command_buffer` and `render_pass` are both set pub fn beginDraw() bool { @@ -323,7 +202,6 @@ pub fn beginDraw() bool { }) orelse err.sdl(); sdl.BindGPUGraphicsPipeline(Graphics.render_pass, Graphics.pipeline); - sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = Graphics.vertex_buffer }, 1); Graphics.camera.computeMatrix(); sdl.PushGPUVertexUniformData(Graphics.command_buffer, 0, &Graphics.camera.matrix, 16 * 4); @@ -350,20 +228,28 @@ pub fn clearDepth() void { }) orelse err.sdl(); sdl.BindGPUGraphicsPipeline(Graphics.render_pass, Graphics.pipeline); - sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = Graphics.vertex_buffer }, 1); sdl.PushGPUVertexUniformData(Graphics.command_buffer, 0, &Graphics.camera.matrix, 16 * 4); } -pub fn drawMesh(mesh: Mesh, texture: *Assets.Texture, transform: Transform) void { +pub fn drawObject(object: *Assets.Object, transform: Transform) void { if (Graphics.render_pass == null) return; - const asset_texture = texture.get() orelse return; + const asset_object = object.get() orelse return; sdl.PushGPUVertexUniformData(Graphics.command_buffer, 1, &transform.matrix(), 16 * 4); - sdl.BindGPUFragmentSamplers(Graphics.render_pass, 0, &sdl.GPUTextureSamplerBinding{ - .texture = asset_texture.texture, - .sampler = asset_texture.sampler, - }, 1); - sdl.DrawGPUPrimitives(Graphics.render_pass, @intCast(mesh.vertex_count), 1, @intCast(mesh.vertex_start), 0); + for (asset_object.nodes) |node| { + const mesh = &asset_object.meshes[node.mesh]; + + for (mesh.primitives) |*primitive| { + const asset_texture = primitive.texture.get() orelse continue; + sdl.BindGPUFragmentSamplers(Graphics.render_pass, 0, &sdl.GPUTextureSamplerBinding{ + .texture = asset_texture.texture, + .sampler = asset_texture.sampler, + }, 1); + sdl.BindGPUVertexBuffers(Graphics.render_pass, 0, &.{ .offset = 0, .buffer = primitive.vertex_buffer }, 1); + sdl.BindGPUIndexBuffer(Graphics.render_pass, &.{ .buffer = primitive.index_buffer }, sdl.GPU_INDEXELEMENTSIZE_16BIT); + sdl.DrawGPUIndexedPrimitives(Graphics.render_pass, primitive.indices, 1, 0, 0, 0); + } + } } pub fn endDraw() void { diff --git a/src/world.zig b/src/world.zig index fc564c3..950de95 100644 --- a/src/world.zig +++ b/src/world.zig @@ -12,13 +12,9 @@ const Order = i32; pub var object_map: std.AutoHashMapUnmanaged(Id, usize) = .{}; pub var objects: std.ArrayListUnmanaged(Object) = .{}; -pub var hand_mesh: Graphics.Mesh = undefined; -pub var cube_mesh: Graphics.Mesh = undefined; -pub var table_mesh: Graphics.Mesh = undefined; -pub var cubemap_mesh: Graphics.Mesh = undefined; -pub var table_texture: Assets.Texture = undefined; -pub var hand_texture: Assets.Texture = undefined; -pub var cubemap_texture: Assets.Texture = undefined; +pub var hand: Assets.Object = undefined; +pub var table: Assets.Object = undefined; +pub var cubemap: Assets.Object = undefined; pub var camera_position: @Vector(2, f32) = @splat(0); pub var hand_transform: Graphics.Transform = .{}; @@ -46,8 +42,7 @@ const Object = struct { target_transform: Graphics.Transform = .{}, width: f32, height: f32, - mesh: Graphics.Mesh, - texture: Assets.Texture, + object: Assets.Object, order: Order, id: Id, index: u32, @@ -99,15 +94,7 @@ pub fn initDebug() void { .type = .card, .width = 0.5, .height = 0.5, - .mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane( - @as(f32, @floatFromInt(9 + i / 10)) / 16.0, - @as(f32, @floatFromInt(0 + i % 10)) / 16.0, - @as(f32, @floatFromInt(10 + i / 10)) / 16.0, - @as(f32, @floatFromInt(1 + i % 10)) / 16.0, - 0.5, - 0.5, - ))), - .texture = Assets.load(.texture, "data/yakuza.png"), + .object = Assets.load(.gltf, "data/yakuza/card.gltf"), .order = @intCast(i), .id = @intCast(i), .index = @intCast(i), @@ -120,15 +107,7 @@ pub fn initDebug() void { .type = .deck, .width = 1, .height = 1, - .mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane( - 0.0 / 8.0, - 4.0 / 8.0, - 1.0 / 8.0, - 5.0 / 8.0, - 1, - 1, - ))), - .texture = Assets.load(.texture, "data/yakuza.png"), + .object = Assets.load(.gltf, "data/yakuza/pad.gltf"), .order = 70, .id = 70, .index = 70, @@ -139,27 +118,16 @@ pub fn initDebug() void { .type = .deck, .width = 1, .height = 1, - .mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane( - 0.0 / 8.0, - 4.0 / 8.0, - 1.0 / 8.0, - 5.0 / 8.0, - 1, - 1, - ))), - .texture = Assets.load(.texture, "data/yakuza.png"), + .object = Assets.load(.gltf, "data/yakuza/pad.gltf"), .order = 71, .id = 71, .index = 71, }) catch err.oom(); World.object_map.put(Game.alloc, 71, 71) catch err.oom(); - World.table_mesh = Graphics.loadMesh(@ptrCast(&Graphics.generatePlane(0, 0, 0.5, 0.5, 8, 8))); - World.hand_mesh = Graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA)); - World.cubemap_mesh = Graphics.loadMesh(@ptrCast(&CUBEMAP_MESH_DATA)); - World.table_texture = Assets.load(.texture, "data/yakuza.png"); - World.hand_texture = Assets.load(.texture, "data/hand.png"); - World.cubemap_texture = Assets.load(.texture, "data/cubemap.png"); + World.hand = Assets.load(.gltf, "data/hand.gltf"); + World.table = Assets.load(.gltf, "data/yakuza/table.gltf"); + World.cubemap = Assets.load(.gltf, "data/cubemap.gltf"); World.camera_position = @splat(0); World.hand_transform = .{}; @@ -177,15 +145,11 @@ pub fn initDebug() void { } pub fn deinit() void { - Graphics.unloadMesh(World.table_mesh); - Graphics.unloadMesh(World.hand_mesh); - Graphics.unloadMesh(World.cubemap_mesh); - Assets.free(World.table_texture); - Assets.free(World.hand_texture); - Assets.free(World.cubemap_texture); + Assets.free(World.hand); + Assets.free(World.table); + Assets.free(World.cubemap); for (World.objects.items) |*object| { - Assets.free(object.texture); - Graphics.unloadMesh(object.mesh); + Assets.free(object.object); } World.objects.clearAndFree(Game.alloc); World.object_map.clearAndFree(Game.alloc); @@ -424,8 +388,9 @@ pub fn updateObject(object: *Object, delta: f32) void { .dock => { var topleft_x = -World.dock_last_width * 0.5 * DOCK_TILT_COS + World.dock_spacing * (@as(f32, @floatFromInt(object.parent_index)) - @as(f32, @floatFromInt(World.dock_objects - 1)) * 0.5); const total_w = @as(f32, @floatFromInt(World.dock_objects - 1)) * World.dock_spacing + World.dock_last_width * DOCK_TILT_COS; + const mouse_x = if (World.dock_focused) Game.mouse.x_norm else 0.5; if (total_w > Graphics.camera.aspect * 2) { - topleft_x += math.lerp(0, Graphics.camera.aspect - total_w * 0.5, Game.mouse.x_norm); + topleft_x += math.lerp(0, Graphics.camera.aspect - total_w * 0.5, mouse_x); } const hit = World.hover == object.id; const topleft_y = if (World.dock_focused) if (hit) @as(f32, 0.5) else @as(f32, 0.3) else @as(f32, 0.2); @@ -465,12 +430,12 @@ pub fn updateObject(object: *Object, delta: f32) void { } pub fn draw() void { - Graphics.drawMesh(World.table_mesh, &World.table_texture, .{}); + Graphics.drawObject(&World.table, .{}); for (World.objects.items) |*object| { sw: switch (object.parent) { .none, .hand => { - Graphics.drawMesh(object.mesh, &object.texture, object.drawingTransform()); + Graphics.drawObject(&object.object, object.drawingTransform()); }, .dock => {}, .deck => |id| { @@ -482,9 +447,8 @@ pub fn draw() void { } } - Graphics.drawMesh( - World.hand_mesh, - &World.hand_texture, + Graphics.drawObject( + &World.hand, Graphics.Transform.combineTransforms( .{ .position = .{ World.hand_scale * 0.5, -World.hand_scale * 0.5, 0 }, @@ -494,14 +458,14 @@ pub fn draw() void { ), ); - Graphics.drawMesh(World.cubemap_mesh, &World.cubemap_texture, .{ + Graphics.drawObject(&World.cubemap, .{ .scale = Graphics.camera.far, .position = Graphics.camera.transform.position, }); Graphics.clearDepth(); for (World.objects.items) |*object| { if (object.parent == .dock) - Graphics.drawMesh(object.mesh, &object.texture, object.drawingTransform()); + Graphics.drawObject(&object.object, object.drawingTransform()); } } @@ -580,65 +544,3 @@ fn objectOrderLessThan(ctx: void, lhs: Object, rhs: Object) bool { _ = ctx; return lhs.order < rhs.order; } - -const T1 = 1.0 / 3.0; -const T2 = 2.0 / 3.0; -const CUBEMAP_MESH_DATA = [_]f32{ - -0.5, 0.5, -0.5, T2, 0, - -0.5, -0.5, -0.5, T2, 0.5, - 0.5, 0.5, -0.5, 1, 0, - 0.5, -0.5, -0.5, 1, 0.5, - 0.5, 0.5, -0.5, 1, 0, - -0.5, -0.5, -0.5, T2, 0.5, - - 0.5, 0.5, -0.5, 0, 1, - 0.5, -0.5, -0.5, T1, 1, - 0.5, 0.5, 0.5, 0, 0.5, - 0.5, -0.5, 0.5, T1, 0.5, - 0.5, 0.5, 0.5, 0, 0.5, - 0.5, -0.5, -0.5, T1, 1, - - 0.5, 0.5, 0.5, 1.0, 0.0, - 0.5, -0.5, 0.5, 1.0, 1.0, - -0.5, 0.5, 0.5, 0.0, 0.0, - -0.5, -0.5, 0.5, 0.0, 1.0, - -0.5, 0.5, 0.5, 0.0, 0.0, - 0.5, -0.5, 0.5, 1.0, 1.0, - - -0.5, 0.5, 0.5, T1, 0, - -0.5, -0.5, 0.5, 0, 0, - -0.5, 0.5, -0.5, T1, 0.5, - -0.5, -0.5, -0.5, 0, 0.5, - -0.5, 0.5, -0.5, T1, 0.5, - -0.5, -0.5, 0.5, 0, 0, - - -0.5, 0.5, 0.5, T1, 0.5, - -0.5, 0.5, -0.5, T1, 1, - 0.5, 0.5, 0.5, T2, 0.5, - 0.5, 0.5, -0.5, T2, 1, - 0.5, 0.5, 0.5, T2, 0.5, - -0.5, 0.5, -0.5, T1, 1, - - -0.5, -0.5, -0.5, T2, 0.5, - -0.5, -0.5, 0.5, T2, 0, - 0.5, -0.5, -0.5, T1, 0.5, - 0.5, -0.5, 0.5, T1, 0, - 0.5, -0.5, -0.5, T1, 0.5, - -0.5, -0.5, 0.5, T2, 0, -}; -const PLANE_MESH_DATA = [_]f32{ - -0.5, -0.5, 0, 0.0, 1.0, - 0.5, 0.5, 0, 1.0, 0.0, - -0.5, 0.5, 0, 0.0, 0.0, - 0.5, 0.5, 0, 1.0, 0.0, - -0.5, -0.5, 0, 0.0, 1.0, - 0.5, -0.5, 0, 1.0, 1.0, -}; -const PLANE_MESH_DATA_HALF = [_]f32{ - -0.25, -0.25, 0, 0.0, 1.0, - 0.25, 0.25, 0, 1.0, 0.0, - -0.25, 0.25, 0, 0.0, 0.0, - 0.25, 0.25, 0, 1.0, 0.0, - -0.25, -0.25, 0, 0.0, 1.0, - 0.25, -0.25, 0, 1.0, 1.0, -};