pub fn loadIndexedMeshForPoolingGltf(allocator: std.mem.Allocator, meshName: core.Name, skeletonName: ?core.Name, path: []const u8) !MeshUpdate { const file = try core.fs().loadFile(path); defer core.fs().unmap(file); const gcAllocator = allocator; // const allocator = gMeshPoolBuffer.allocator; var parser = zgltf.init(allocator); defer parser.deinit(); const ext = core.getFileExtension(path); if (std.mem.eql(u8, ".gltf", ext)) { try parser.parse(@alignCast(file.bytes[0 .. file.bytes.len - 1])); } else { try parser.parse(@alignCast(file.bytes)); } // std.debug.print("\n", .{}); // parser.debugPrint(); if (parser.data.meshes.items.len > 1) { return error.OnlyOneMeshPerGltfImplemented; } if (parser.data.skins.items.len > 1) { return error.TooManySkins; } var binaryFile: ?core.packer.PackerBytesMapping = null; var binaryBytes: []const u8 = undefined; if (std.mem.eql(u8, ext, ".glb")) { binaryBytes = parser.glb_binary.?; } else { const binaryPath = try std.fmt.allocPrint(allocator, "{s}bin", .{path[0 .. path.len - 4]}); defer allocator.free(binaryPath); core.engine_log("{s}", .{binaryPath}); binaryFile = try core.fs().loadFile(binaryPath); binaryBytes = binaryFile.?.bytes; } defer if (binaryFile) |f| core.fs().unmap(f); const m = parser.data.meshes.items[0]; core.engine_log("mesh name {s} number of primitives = {d}", .{ m.name, m.primitives.items.len }); var positions = std.ArrayList(f32).init(allocator); defer positions.deinit(); var texcoords = std.ArrayList(f32).init(allocator); defer texcoords.deinit(); var normals = std.ArrayList(f32).init(allocator); defer normals.deinit(); var joints = std.ArrayList(u16).init(allocator); defer joints.deinit(); // add a different joint format one, todo- i need to fix up zgltf var useJoints8: bool = false; var joints8 = std.ArrayList(u8).init(allocator); defer joints8.deinit(); var weights = std.ArrayList(f32).init(allocator); defer weights.deinit(); var weightCount: usize = 4; if (m.primitives.items.len > 1) { @panic("sorry, havent implemented support for multiple primitives yet, would require more work on the way i handle materials"); } var indexList = std.ArrayList(u32).init(allocator); for (m.primitives.items) |primitive| { if (primitive.indices) |indices| { const accessor = parser.data.accessors.items[indices]; // core.engine_log("index accessor info: {any}", .{accessor}); if (accessor.component_type == .unsigned_short) { var temp = std.ArrayList(u16).init(allocator); defer temp.deinit(); parser.getDataFromBufferView(u16, &temp, accessor, @alignCast(binaryBytes)); for (temp.items) |t| { try indexList.append(@intCast(t)); } } else if (accessor.component_type == .unsigned_integer) { parser.getDataFromBufferView(u32, &indexList, accessor, @alignCast(binaryBytes)); } } for (primitive.attributes.items) |attribute| { // core.engine_log("attribute: {any}", .{attribute}); switch (attribute) { .position => |x| { const accessor = parser.data.accessors.items[x]; // core.engine_log("accessor info: {any}", .{accessor}); parser.getDataFromBufferView(f32, &positions, accessor, @alignCast(binaryBytes)); // core.engine_log("positions loaded: {d}", .{positions.items.len}); }, .normal => |x| { const accessor = parser.data.accessors.items[x]; // core.engine_log("accessor info: {any}", .{accessor}); parser.getDataFromBufferView(f32, &normals, accessor, @alignCast(binaryBytes)); // core.engine_log("normals loaded: {d}", .{normals.items.len}); }, .texcoord => |x| { const accessor = parser.data.accessors.items[x]; // core.engine_log("accessor info: {any}", .{accessor}); parser.getDataFromBufferView(f32, &texcoords, accessor, @alignCast(binaryBytes)); // core.engine_log("texcoords loaded: {d}", .{texcoords.items.len}); }, .joints => |x| { const accessor = parser.data.accessors.items[x]; // core.engine_log("accessor info: {any} acecssor index {d}", .{ accessor, x }); if (accessor.component_type == .unsigned_byte) { useJoints8 = true; parser.getDataFromBufferView(u8, &joints8, accessor, @alignCast(binaryBytes)); // core.engine_log("joints8 loaded: {d} - {d} {d} {d} {d}", .{ joints8.items.len, joints8.items[0], joints8.items[1], joints8.items[2], joints8.items[3] }); } else { parser.getDataFromBufferView(u16, &joints, accessor, @alignCast(binaryBytes)); // core.engine_log("joints loaded: {d} - {d} {d} {d} {d}", .{ joints.items.len, joints.items[0], joints.items[1], joints.items[2], joints.items[3] }); } }, .weights => |x| { const accessor = parser.data.accessors.items[x]; // core.engine_log("accessor info: {any}", .{accessor}); parser.getDataFromBufferView(f32, &weights, accessor, @alignCast(binaryBytes)); if (accessor.type == .vec3) { weightCount = 3; } // core.engine_log("weights loaded: {d} - {d} {d} {d} {d}", .{ weights.items.len, weights.items[0], weights.items[1], weights.items[2], weights.items[3] }); }, .tangent => |x| { const accessor = parser.data.accessors.items[x]; core.engine_log("accessor info: {any} NOT PARSED", .{accessor}); }, .color => |x| { const accessor = parser.data.accessors.items[x]; core.engine_log("accessor info: {any} NOT PARSED", .{accessor}); }, } } } if (parser.data.skins.items.len > 1) { @panic("too many skins, not supported"); } var jointNameList: std.ArrayList(JointNameEntry) = std.ArrayList(JointNameEntry).init(allocator); if (weights.items.len > 0) { core.engine_log("skin found, building joint map", .{}); if (parser.data.skins.items[0].skeleton) |skeletonIndex| { for (parser.data.nodes.items[skeletonIndex..], 0..) |node, i| { // core.engine_log("gltf: {s} -> {d} (skeleton index)", .{ node.name, i }); // const gcAllocator = graphics.getContext().allocator; try jointNameList.append(.{ .index = @intCast(i), .name = try gcAllocator.dupe(u8, node.name) }); } } else { if (parser.data.skins.items[0].joints.items.len > 0) { for (parser.data.skins.items[0].joints.items, 0..) |i, j| { const node = parser.data.nodes.items[i]; // core.engine_log("gltf: {s} -> {d} (joints map)", .{ node.name, j }); // const gcAllocator = graphics.getContext().allocator; try jointNameList.append(.{ .index = @intCast(j), .name = try gcAllocator.dupe(u8, node.name) }); } } else { for (parser.data.nodes.items, 0..) |node, i| { // core.engine_log("gltf: {s} -> {d} (fallback)", .{ node.name, i }); // const gcAllocator = graphics.getContext().allocator; try jointNameList.append(.{ .index = @intCast(i), .name = try gcAllocator.dupe(u8, node.name) }); } } } } var vertexList = std.ArrayList(MeshVertex).init(allocator); var i: usize = 0; const vertexCount = positions.items.len / 3; while (i < vertexCount) : (i += 1) { const normalIndex = i * 3; const positionIndex = i * 3; const uvIndex = i * 2; const uv: core.Vector2f = if (uvIndex < texcoords.items.len) .{ .x = texcoords.items[uvIndex], .y = texcoords.items[uvIndex + 1], } else core.Vector2f{}; const normal = if (normalIndex < normals.items.len) core.Vectorf{ .x = normals.items[i], .y = normals.items[i + 1], .z = normals.items[i + 2], } else core.Vectorf{}; try vertexList.append(.{ .position = .{ .x = positions.items[positionIndex], .y = positions.items[positionIndex + 1], .z = positions.items[positionIndex + 2], }, .normal = normal, .color = .{}, .uv = uv, }); const jointsIndex = weightCount * i; if (weightCount == 4) { if (useJoints8) { if (jointsIndex < joints8.items.len) { vertexList.items[vertexList.items.len - 1].bones = .{ @intCast(joints8.items[jointsIndex + 0]), @intCast(joints8.items[jointsIndex + 1]), @intCast(joints8.items[jointsIndex + 2]), @intCast(joints8.items[jointsIndex + 3]), }; vertexList.items[vertexList.items.len - 1].weights = .{ @intFromFloat(weights.items[jointsIndex + 0] * 255), @intFromFloat(weights.items[jointsIndex + 1] * 255), @intFromFloat(weights.items[jointsIndex + 2] * 255), @intFromFloat(weights.items[jointsIndex + 3] * 255), }; } } else { if (jointsIndex < joints.items.len) { vertexList.items[vertexList.items.len - 1].bones = .{ @intCast(joints.items[jointsIndex + 0]), @intCast(joints.items[jointsIndex + 1]), @intCast(joints.items[jointsIndex + 2]), @intCast(joints.items[jointsIndex + 3]), }; vertexList.items[vertexList.items.len - 1].weights = .{ @intFromFloat(weights.items[jointsIndex + 0] * 255), @intFromFloat(weights.items[jointsIndex + 1] * 255), @intFromFloat(weights.items[jointsIndex + 2] * 255), @intFromFloat(weights.items[jointsIndex + 3] * 255), }; } } } else { return error.NotImplementedYet; } } if (indexList.items.len == 0) { for (0..vertexList.items.len) |x| { try indexList.append(@intCast(x)); } } const rv: MeshUpdate = .{ .new = .{ .vertices = try vertexList.toOwnedSlice(), .indices = try indexList.toOwnedSlice(), .jointNames = try jointNameList.toOwnedSlice(), .skeletonName = skeletonName, .name = meshName, }, }; core.graphics_log("[{s}] gltf loaded vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len }); // try gMeshPoolBuffer.updateRequests.pushLocked(rv); return rv; } const meshes = @import("meshes.zig"); const MeshUpdate = meshes.MeshUpdate; const JointNameEntry = meshes.JointNameEntry; const MeshVertex = meshes.MeshVertex; const core = @import("core"); const std = @import("std"); const zgltf = @import("zgltf");