load_obj.zig (6325B)
1 const std = @import("std"); 2 const gpu = @import("mach_gpu"); 3 4 const Mesh = @import("mesh.zig"); 5 6 const f32x2 = @Vector(2, f32); 7 const f32x3 = @Vector(3, f32); 8 const f32x4 = @Vector(4, f32); 9 10 fn parsePosition(line: []u8) !f32x4 { 11 var iter = std.mem.tokenize(u8, line[2..], " "); 12 var position = f32x4{ 0, 0, 0, 1 }; 13 var idx: u32 = 0; 14 while (iter.next()) |part| : (idx += 1) { 15 if (idx >= 4) { 16 return error.InvalidFormat; 17 } 18 position[idx] = try std.fmt.parseFloat(f32, part); 19 } 20 if (idx < 3) { 21 return error.InvalidFormat; 22 } 23 return position; 24 } 25 26 fn parseNormal(line: []u8) !f32x3 { 27 var iter = std.mem.tokenize(u8, line[3..], " "); 28 var normal = f32x3{ 0, 0, 0 }; 29 var idx: u32 = 0; 30 while (iter.next()) |part| : (idx += 1) { 31 if (idx >= 3) { 32 return error.InvalidFormat; 33 } 34 normal[idx] = try std.fmt.parseFloat(f32, part); 35 } 36 if (idx < 3) { 37 return error.InvalidFormat; 38 } 39 return normal; 40 } 41 42 fn parseTexCoord(line: []u8) !f32x3 { 43 var iter = std.mem.tokenize(u8, line[3..], " "); 44 var tex_coord = f32x3{ 0, 0, 0 }; 45 var idx: u32 = 0; 46 while (iter.next()) |part| : (idx += 1) { 47 if (idx >= 3) { 48 return error.InvalidFormat; 49 } 50 tex_coord[idx] = try std.fmt.parseFloat(f32, part); 51 } 52 if (idx < 1) { 53 return error.InvalidFormat; 54 } 55 return tex_coord; 56 } 57 58 const Vertex = struct { 59 position: u32 = 0, 60 tex_coord: ?u32 = null, 61 normal: ?u32 = null, 62 }; 63 64 fn parseFace(line: []u8) ![3]Vertex { 65 var iter = std.mem.tokenize(u8, line[2..], " "); 66 var vertices = [3]Vertex{ .{}, .{}, .{} }; 67 var idx: u32 = 0; 68 69 while (iter.next()) |part| : (idx += 1) { 70 if (idx >= 3) { 71 return error.InvalidFormat; 72 } 73 const vertex = try parseVertex(part); 74 vertices[idx] = vertex; 75 } 76 if (idx < 3) { 77 return error.InvalidFormat; 78 } 79 return vertices; 80 } 81 82 fn parseVertex(line: []const u8) !Vertex { 83 var iter = std.mem.split(u8, line, "/"); 84 var vertex = Vertex{ 85 .position = 0, 86 .tex_coord = null, 87 .normal = null, 88 }; 89 var idx: u32 = 0; 90 while (iter.next()) |part| : (idx += 1) { 91 switch (idx) { 92 0 => vertex.position = (try std.fmt.parseInt(u32, part, 10)) - 1, 93 1 => vertex.tex_coord = if (!std.mem.eql(u8, part, "")) (try std.fmt.parseInt(u32, part, 10)) - 1 else null, 94 2 => vertex.normal = if (!std.mem.eql(u8, part, "")) (try std.fmt.parseInt(u32, part, 10)) - 1 else null, 95 else => return error.InvalidFormat, 96 } 97 } 98 return vertex; 99 } 100 101 pub const LoadFileOptions = struct { 102 allocator: std.mem.Allocator, 103 path: []const u8, 104 }; 105 106 pub fn loadFile(options: LoadFileOptions) !Mesh { 107 const allocator = options.allocator; 108 const path = options.path; 109 110 var file = try std.fs.cwd().openFile(path, .{ .mode = .read_only }); 111 defer file.close(); 112 113 var reader = std.io.bufferedReader(file.reader()); 114 var stream = reader.reader(); 115 116 var positions = std.ArrayList(f32x4).init(allocator); 117 defer positions.deinit(); 118 var normals = std.ArrayList(f32x3).init(allocator); 119 defer normals.deinit(); 120 var tex_coords = std.ArrayList(f32x3).init(allocator); 121 defer tex_coords.deinit(); 122 var vertices = std.AutoArrayHashMap(Vertex, u32).init(allocator); 123 var indices = std.ArrayList(u32).init(allocator); 124 125 var buf: [256]u8 = undefined; 126 var vtx_idx: u32 = 0; 127 while (try stream.readUntilDelimiterOrEof(&buf, '\n')) |line| { 128 if (std.mem.startsWith(u8, line, "v ")) { 129 try positions.append(try parsePosition(line)); 130 } else if (std.mem.startsWith(u8, line, "vt ")) { 131 try tex_coords.append(try parseTexCoord(line)); 132 } else if (std.mem.startsWith(u8, line, "vn ")) { 133 try normals.append(try parseNormal(line)); 134 } else if (std.mem.startsWith(u8, line, "f ")) { 135 const face = try parseFace(line); 136 for (face) |vertex| { 137 const entry = try vertices.getOrPutValue(vertex, vtx_idx); 138 try indices.append(entry.value_ptr.*); 139 if (!entry.found_existing) { 140 vtx_idx += 1; 141 } 142 } 143 } else { 144 std.log.info("UNRECOGNIZED: {s}", .{line}); 145 } 146 } 147 148 std.log.info("NUM POSITIONS: {}", .{positions.items.len}); 149 std.log.info("NUM NORMALS: {}", .{normals.items.len}); 150 std.log.info("NUM TEXCOORDS: {}", .{tex_coords.items.len}); 151 std.log.info("NUM VERTICES: {}", .{vertices.count()}); 152 153 var positions_indexed = std.ArrayList(f32x4).init(allocator); 154 defer positions_indexed.deinit(); 155 var normals_indexed = std.ArrayList(f32x3).init(allocator); 156 defer normals_indexed.deinit(); 157 var tex_coords_indexed = std.ArrayList(f32x3).init(allocator); 158 defer tex_coords_indexed.deinit(); 159 160 var vtx_iter = vertices.iterator(); 161 while (vtx_iter.next()) |entry| { 162 const vtx = entry.key_ptr; 163 164 const position = positions.items[vtx.position]; 165 try positions_indexed.append(position); 166 167 if (vtx.normal != null) { 168 const normal = normals.items[vtx.normal.?]; 169 try normals_indexed.append(normal); 170 } 171 172 if (vtx.tex_coord != null) { 173 const tex_coord = tex_coords.items[vtx.tex_coord.?]; 174 try tex_coords_indexed.append(tex_coord); 175 } 176 } 177 178 std.log.info("NUM POSITIONS: {}", .{positions_indexed.items.len}); 179 std.log.info("NUM NORMALS: {}", .{normals_indexed.items.len}); 180 std.log.info("NUM TEXCOORDS: {}", .{tex_coords_indexed.items.len}); 181 182 const pslice = try positions_indexed.toOwnedSlice(); 183 const nslice = try normals_indexed.toOwnedSlice(); 184 const tslice = try tex_coords_indexed.toOwnedSlice(); 185 const islice = try indices.toOwnedSlice(); 186 var mesh = Mesh{ 187 .positions = pslice, 188 .normals = nslice, 189 .tex_coords = tslice, 190 .indices = islice, 191 .tangents = try allocator.alloc(f32x3, pslice.len), 192 .bitangents = try allocator.alloc(f32x3, pslice.len), 193 }; 194 mesh.bbox = mesh.axisAlignedBoundingBox(); 195 try mesh.tangentsAndBitangents(allocator); 196 197 return mesh; 198 }