render-zig

A 3D rendering engine written in Zig
git clone git://git.christianermann.dev/render-zig
Log | Files | Refs

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 }