1
1
const std = @import ("std" );
2
- const tokenize = std .mem .tokenize ;
3
- const split = std .mem .split ;
4
- const ArrayList = std .ArrayList ;
2
+ const tokenizeAny = std .mem .tokenizeAny ;
3
+ const splitAny = std .mem .splitAny ;
4
+ const ArrayListUnmanaged = std .ArrayListUnmanaged ;
5
5
const Allocator = std .mem .Allocator ;
6
6
const assert = std .debug .assert ;
7
7
const parseFloat = std .fmt .parseFloat ;
@@ -17,14 +17,149 @@ pub const ObjData = struct {
17
17
meshes : []const Mesh ,
18
18
19
19
pub fn deinit (self : * @This (), allocator : Allocator ) void {
20
+ for (self .material_libs ) | mlib | allocator .free (mlib );
20
21
allocator .free (self .material_libs );
22
+
21
23
allocator .free (self .vertices );
22
24
allocator .free (self .tex_coords );
23
25
allocator .free (self .normals );
24
26
25
27
for (self .meshes ) | mesh | mesh .deinit (allocator );
26
28
allocator .free (self .meshes );
27
29
}
30
+
31
+ const Builder = struct {
32
+ allocator : std.mem.Allocator ,
33
+ material_libs : ArrayListUnmanaged ([]const u8 ) = .{},
34
+ vertices : ArrayListUnmanaged (f32 ) = .{},
35
+ tex_coords : ArrayListUnmanaged (f32 ) = .{},
36
+ normals : ArrayListUnmanaged (f32 ) = .{},
37
+ meshes : ArrayListUnmanaged (Mesh ) = .{},
38
+
39
+ // current mesh
40
+ name : ? []const u8 = null ,
41
+ num_verts : ArrayListUnmanaged (u32 ) = .{},
42
+ indices : ArrayListUnmanaged (Mesh .Index ) = .{},
43
+ index_i : u32 = 0 ,
44
+
45
+ // current mesh material
46
+ current_material : ? MeshMaterial = null ,
47
+ mesh_materials : ArrayListUnmanaged (MeshMaterial ) = .{},
48
+ num_processed_verts : usize = 0 ,
49
+
50
+ fn onError (self : * Builder ) void {
51
+ for (self .material_libs .items ) | mlib | self .allocator .free (mlib );
52
+ for (self .meshes .items ) | mesh | mesh .deinit (self .allocator );
53
+ self .material_libs .deinit (self .allocator );
54
+ self .vertices .deinit (self .allocator );
55
+ self .tex_coords .deinit (self .allocator );
56
+ self .normals .deinit (self .allocator );
57
+ self .meshes .deinit (self .allocator );
58
+ if (self .name ) | n | self .allocator .free (n );
59
+ self .num_verts .deinit (self .allocator );
60
+ self .indices .deinit (self .allocator );
61
+ if (self .current_material ) | mat | self .allocator .free (mat .material );
62
+ self .mesh_materials .deinit (self .allocator );
63
+ }
64
+
65
+ fn finish (self : * Builder ) ! ObjData {
66
+ defer self .* = undefined ;
67
+ try self .use_material (null ); // add last material if any
68
+ try self .object (null ); // add last mesh (as long as it is not empty)
69
+ return ObjData {
70
+ .material_libs = try self .material_libs .toOwnedSlice (self .allocator ),
71
+ .vertices = try self .vertices .toOwnedSlice (self .allocator ),
72
+ .tex_coords = try self .tex_coords .toOwnedSlice (self .allocator ),
73
+ .normals = try self .normals .toOwnedSlice (self .allocator ),
74
+ .meshes = try self .meshes .toOwnedSlice (self .allocator ),
75
+ };
76
+ }
77
+
78
+ fn vertex (self : * Builder , x : f32 , y : f32 , z : f32 , w : ? f32 ) ! void {
79
+ _ = w ;
80
+ try self .vertices .appendSlice (self .allocator , &.{ x , y , z });
81
+ }
82
+
83
+ fn tex_coord (self : * Builder , u : f32 , v : ? f32 , w : ? f32 ) ! void {
84
+ _ = w ;
85
+ try self .tex_coords .appendSlice (self .allocator , &.{ u , v .? });
86
+ }
87
+
88
+ fn normal (self : * Builder , i : f32 , j : f32 , k : f32 ) ! void {
89
+ try self .normals .appendSlice (self .allocator , &.{ i , j , k });
90
+ }
91
+
92
+ fn face_index (self : * Builder , vert : u32 , tex : ? u32 , norm : ? u32 ) ! void {
93
+ try self .indices .append (
94
+ self .allocator ,
95
+ .{ .vertex = vert , .tex_coord = tex , .normal = norm },
96
+ );
97
+ self .index_i += 1 ;
98
+ }
99
+
100
+ fn face_end (self : * Builder ) ! void {
101
+ try self .num_verts .append (self .allocator , self .index_i );
102
+ self .num_processed_verts += self .index_i ;
103
+ self .index_i = 0 ;
104
+ }
105
+
106
+ fn object (self : * Builder , name : ? []const u8 ) ! void {
107
+ if (0 < self .num_verts .items .len ) {
108
+ if (self .current_material ) | * m | {
109
+ m .end_index = self .num_processed_verts ;
110
+ try self .mesh_materials .append (self .allocator , m .* );
111
+ }
112
+ try self .meshes .append (self .allocator , .{
113
+ .name = self .name ,
114
+ .num_vertices = try self .num_verts .toOwnedSlice (self .allocator ),
115
+ .indices = try self .indices .toOwnedSlice (self .allocator ),
116
+ .materials = try self .mesh_materials .toOwnedSlice (self .allocator ),
117
+ });
118
+ }
119
+ if (name ) | n | {
120
+ self .name = try self .allocator .dupe (u8 , n );
121
+ self .num_verts = .{};
122
+ self .indices = .{};
123
+ self .num_processed_verts = 0 ;
124
+ self .current_material = null ;
125
+ }
126
+ }
127
+
128
+ fn use_material (self : * Builder , name : ? []const u8 ) ! void {
129
+ if (self .current_material ) | * m | {
130
+ m .end_index = self .num_processed_verts ;
131
+ try self .mesh_materials .append (self .allocator , m .* );
132
+ }
133
+ if (name ) | n | {
134
+ self .current_material = MeshMaterial {
135
+ .material = try self .allocator .dupe (u8 , n ),
136
+ .start_index = self .num_processed_verts ,
137
+ .end_index = self .num_processed_verts + 1 ,
138
+ };
139
+ } else {
140
+ self .current_material = null ;
141
+ }
142
+ }
143
+
144
+ fn material_lib (self : * Builder , name : []const u8 ) ! void {
145
+ try self .material_libs .append (
146
+ self .allocator ,
147
+ try self .allocator .dupe (u8 , name ),
148
+ );
149
+ }
150
+
151
+ fn vertexCount (self : Builder ) usize {
152
+ return self .vertices .items .len ;
153
+ }
154
+
155
+ fn texCoordCount (self : Builder ) usize {
156
+ return self .tex_coords .items .len ;
157
+ }
158
+
159
+ fn normalCount (self : Builder ) usize {
160
+ return self .normals .items .len ;
161
+ }
162
+ };
28
163
};
29
164
30
165
fn compareOpt (a : ? u32 , b : ? u32 ) bool {
@@ -117,146 +252,93 @@ const DefType = enum {
117
252
};
118
253
119
254
pub fn parse (allocator : Allocator , data : []const u8 ) ! ObjData {
120
- var material_libs = ArrayList ([]const u8 ).init (allocator );
121
- errdefer material_libs .deinit ();
122
-
123
- var vertices = ArrayList (f32 ).init (allocator );
124
- errdefer vertices .deinit ();
125
-
126
- var tex_coords = ArrayList (f32 ).init (allocator );
127
- errdefer tex_coords .deinit ();
128
-
129
- var normals = ArrayList (f32 ).init (allocator );
130
- errdefer normals .deinit ();
131
-
132
- var meshes = ArrayList (Mesh ).init (allocator );
133
- errdefer meshes .deinit ();
134
-
135
- // current mesh
136
- var name : ? []const u8 = null ;
137
- var num_verts = ArrayList (u32 ).init (allocator );
138
- errdefer num_verts .deinit ();
139
- var indices = ArrayList (Mesh .Index ).init (allocator );
140
- errdefer indices .deinit ();
141
-
142
- // current mesh material
143
- var current_material : ? MeshMaterial = null ;
144
- var mesh_materials = ArrayList (MeshMaterial ).init (allocator );
145
- errdefer mesh_materials .deinit ();
146
- var num_processed_verts : usize = 0 ;
147
-
148
- var lines = tokenize (u8 , data , "\r \n " );
149
- while (lines .next ()) | line | {
150
- var iter = tokenize (u8 , line , " " );
151
- const def_type = try parseType (iter .next ().? );
255
+ var b = ObjData.Builder { .allocator = allocator };
256
+ errdefer b .onError ();
257
+ var fbs = std .io .fixedBufferStream (data );
258
+ return try parseCustom (ObjData , & b , fbs .reader ());
259
+ }
260
+
261
+ pub fn parseCustom (comptime T : type , b : * T.Builder , rdr : anytype ) ! T {
262
+ var buffer : [128 ]u8 = undefined ;
263
+ var lines = lineIterator (rdr , & buffer );
264
+ while (try lines .next ()) | line | {
265
+ var iter = tokenizeAny (u8 , line , " " );
266
+ const def_type =
267
+ if (iter .next ()) | tok | try parseType (tok ) else continue ;
152
268
switch (def_type ) {
153
- .vertex = > {
154
- try vertices .append (try parseFloat (f32 , iter .next ().? ));
155
- try vertices .append (try parseFloat (f32 , iter .next ().? ));
156
- try vertices .append (try parseFloat (f32 , iter .next ().? ));
157
- },
158
- .tex_coord = > {
159
- try tex_coords .append (try parseFloat (f32 , iter .next ().? ));
160
- try tex_coords .append (try parseFloat (f32 , iter .next ().? ));
161
- },
162
- .normal = > {
163
- try normals .append (try parseFloat (f32 , iter .next ().? ));
164
- try normals .append (try parseFloat (f32 , iter .next ().? ));
165
- try normals .append (try parseFloat (f32 , iter .next ().? ));
166
- },
269
+ .vertex = > try b .vertex (
270
+ try parseFloat (f32 , iter .next ().? ),
271
+ try parseFloat (f32 , iter .next ().? ),
272
+ try parseFloat (f32 , iter .next ().? ),
273
+ if (iter .next ()) | w | (try parseFloat (f32 , w )) else null ,
274
+ ),
275
+ .tex_coord = > try b .tex_coord (
276
+ try parseFloat (f32 , iter .next ().? ),
277
+ if (iter .next ()) | v | (try parseFloat (f32 , v )) else null ,
278
+ if (iter .next ()) | w | (try parseFloat (f32 , w )) else null ,
279
+ ),
280
+ .normal = > try b .normal (
281
+ try parseFloat (f32 , iter .next ().? ),
282
+ try parseFloat (f32 , iter .next ().? ),
283
+ try parseFloat (f32 , iter .next ().? ),
284
+ ),
167
285
.face = > {
168
- var i : u32 = 0 ;
169
286
while (iter .next ()) | entry | {
170
- var entry_iter = split (u8 , entry , "/" );
171
- // TODO support x//y and similar
172
- // NOTE obj is one-indexed - let's make it zero-indexed
173
- try indices .append (.{
174
- .vertex = if (entry_iter .next ()) | e | (try parseOptionalIndex (e , vertices .items )) else null ,
175
- .tex_coord = if (entry_iter .next ()) | e | (try parseOptionalIndex (e , tex_coords .items )) else null ,
176
- .normal = if (entry_iter .next ()) | e | (try parseOptionalIndex (e , normals .items )) else null ,
177
- });
178
-
179
- i += 1 ;
180
- }
181
- try num_verts .append (i );
182
- num_processed_verts += i ;
183
- },
184
- .object = > {
185
- if (num_verts .items .len > 0 ) {
186
- // add last material if any
187
- if (current_material ) | * m | {
188
- m .end_index = num_processed_verts ;
189
- try mesh_materials .append (m .* );
190
- }
191
-
192
- try meshes .append (.{
193
- .name = if (name ) | n | try allocator .dupe (u8 , n ) else null ,
194
- .num_vertices = try num_verts .toOwnedSlice (),
195
- .indices = try indices .toOwnedSlice (),
196
- .materials = try mesh_materials .toOwnedSlice (),
197
- });
287
+ var entry_iter = splitAny (u8 , entry , "/" );
288
+ try b .face_index (
289
+ (try parseOptionalIndex (entry_iter .next ().? , b .vertexCount ())).? ,
290
+ if (entry_iter .next ()) | e | (try parseOptionalIndex (e , b .texCoordCount ())) else null ,
291
+ if (entry_iter .next ()) | e | (try parseOptionalIndex (e , b .normalCount ())) else null ,
292
+ );
198
293
}
199
-
200
- name = iter .next ().? ;
201
- num_verts = ArrayList (u32 ).init (allocator );
202
- errdefer num_verts .deinit ();
203
- indices = ArrayList (Mesh .Index ).init (allocator );
204
- errdefer indices .deinit ();
205
- num_processed_verts = 0 ;
206
- current_material = null ;
207
- },
208
- .use_material = > {
209
- if (current_material ) | * m | {
210
- m .end_index = num_processed_verts ;
211
- try mesh_materials .append (m .* );
212
- }
213
- const material = try allocator .dupe (u8 , iter .next ().? );
214
- current_material = MeshMaterial {
215
- .material = material ,
216
- .start_index = num_processed_verts ,
217
- .end_index = num_processed_verts + 1 ,
218
- };
219
- },
220
- .material_lib = > {
221
- try material_libs .append (iter .next ().? );
294
+ try b .face_end ();
222
295
},
296
+ .object = > try b .object (iter .next ().? ),
297
+ .use_material = > try b .use_material (iter .next ().? ),
298
+ .material_lib = > while (iter .next ()) | lib | try b .material_lib (lib ),
223
299
else = > {},
224
300
}
225
301
}
226
302
227
- // add last material if any
228
- if (current_material ) | * m | {
229
- m .end_index = num_processed_verts ;
230
- try mesh_materials .append (m .* );
231
- }
232
-
233
- // add last mesh (as long as it is not empty)
234
- if (num_verts .items .len > 0 ) {
235
- try meshes .append (Mesh {
236
- .name = if (name ) | n | try allocator .dupe (u8 , n ) else null ,
237
- .num_vertices = try num_verts .toOwnedSlice (),
238
- .indices = try indices .toOwnedSlice (),
239
- .materials = try mesh_materials .toOwnedSlice (),
240
- });
241
- }
303
+ return try b .finish ();
304
+ }
242
305
243
- return ObjData {
244
- .material_libs = try material_libs .toOwnedSlice (),
245
- .vertices = try vertices .toOwnedSlice (),
246
- .tex_coords = try tex_coords .toOwnedSlice (),
247
- .normals = try normals .toOwnedSlice (),
248
- .meshes = try meshes .toOwnedSlice (),
306
+ fn LineIterator (comptime Reader : type ) type {
307
+ return struct {
308
+ buffer : []u8 ,
309
+ reader : Reader ,
310
+
311
+ fn next (self : * @This ()) ! ? []const u8 {
312
+ var fbs = std .io .fixedBufferStream (self .buffer );
313
+ self .reader .streamUntilDelimiter (
314
+ fbs .writer (),
315
+ '\n ' ,
316
+ fbs .buffer .len ,
317
+ ) catch | err | switch (err ) {
318
+ error .EndOfStream = > if (fbs .getWritten ().len == 0 ) return null ,
319
+ else = > | e | return e ,
320
+ };
321
+ var line = fbs .getWritten ();
322
+ if (0 < line .len and line [line .len - 1 ] == '\r ' )
323
+ line = line [0 .. line .len - 1 ];
324
+ return line ;
325
+ }
249
326
};
250
327
}
251
328
252
- fn parseOptionalIndex (v : []const u8 , indices : []f32 ) ! ? u32 {
329
+ fn lineIterator (rdr : anytype , buffer : []u8 ) LineIterator (@TypeOf (rdr )) {
330
+ return .{ .buffer = buffer , .reader = rdr };
331
+ }
332
+
333
+ fn parseOptionalIndex (v : []const u8 , n_items : usize ) ! ? u32 {
253
334
if (std .mem .eql (u8 , v , "" )) return null ;
254
335
const i = try parseInt (i32 , v , 10 );
255
336
256
337
if (i < 0 ) {
257
338
// index is relative to end of indices list, -1 meaning the last element
258
- return @as (u32 , @intCast (@as (i32 , @intCast (indices . len )) + i ));
339
+ return @as (u32 , @intCast (@as (i32 , @intCast (n_items )) + i ));
259
340
} else {
341
+ // obj is one-indexed - let's make it zero-indexed
260
342
return @as (u32 , @intCast (i )) - 1 ;
261
343
}
262
344
}
0 commit comments