Skip to content

Commit 6e7e91b

Browse files
committed
obj: fixed handling of the vertex color extension
previously if any mesh in an obj had vertex colors, every mesh after that would also be assumed to have them, and end up reading outside of the color array when assembling faces. Now we're keeping vertex position and color data together in the same array, and make sure to only create a color array if the current mesh has vertices with valid vertex colors.
1 parent 6ea1b0c commit 6e7e91b

File tree

1 file changed

+57
-45
lines changed

1 file changed

+57
-45
lines changed

src/fmtobj.c

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,19 @@ static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int num
3535
static int cmp_facevert(const void *ap, const void *bp);
3636
static void free_rbnode_key(struct rbnode *n, void *cls);
3737

38+
struct vertex {
39+
float x, y, z, w;
40+
float r, g, b, a;
41+
int rgba_valid;
42+
};
3843

3944
int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
4045
{
4146
char buf[128];
4247
int result = -1;
4348
int i, line_num = 0;
44-
mf_vec3 *varr = 0, *narr = 0;
45-
mf_vec4 *carr = 0;
49+
struct vertex *varr = 0;
50+
mf_vec3 *narr = 0;
4651
mf_vec2 *tarr = 0;
4752
struct rbtree *rbtree = 0;
4853
struct mf_mesh *mesh = 0;
@@ -58,24 +63,23 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
5863
}
5964

6065
if(!(rbtree = rb_create(cmp_facevert))) {
61-
fprintf(stderr, "mf_load: failed to create rbtree\n");
66+
fprintf(stderr, "load_obj: failed to create rbtree\n");
6267
goto end;
6368
}
6469
rb_set_delete_func(rbtree, free_rbnode_key, 0);
6570

6671
if(!(varr = mf_dynarr_alloc(0, sizeof *varr)) ||
6772
!(narr = mf_dynarr_alloc(0, sizeof *narr)) ||
68-
!(tarr = mf_dynarr_alloc(0, sizeof *tarr)) ||
69-
!(carr = mf_dynarr_alloc(0, sizeof *carr))) {
70-
fprintf(stderr, "mf_load: failed to allocate vertex attribute arrays\n");
73+
!(tarr = mf_dynarr_alloc(0, sizeof *tarr))) {
74+
fprintf(stderr, "load_obj: failed to allocate vertex attribute arrays\n");
7175
goto end;
7276
}
7377

7478
if(!(mesh = mf_alloc_mesh())) {
7579
goto end;
7680
}
7781
if(!(mesh->name = strdup(mf->name))) {
78-
fprintf(stderr, "mf_load: failed to allocate mesh name\n");
82+
fprintf(stderr, "load_obj: failed to allocate mesh name\n");
7983
goto end;
8084
}
8185

@@ -89,32 +93,30 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
8993
case 'v':
9094
if(isspace(line[1])) {
9195
/* vertex */
92-
mf_vec3 v;
93-
mf_vec4 col;
96+
struct vertex v;
9497
int num;
9598

9699
num = sscanf(line + 2, "%f %f %f %f %f %f %f", &v.x, &v.y, &v.z,
97-
&col.x, &col.y, &col.z, &col.w);
100+
&v.w, &v.g, &v.b, &v.a);
98101
if(num < 3) {
99102
fprintf(stderr, "%s:%d: invalid vertex definition: \"%s\"\n", mf->name, line_num, line);
100103
goto end;
101104
}
105+
switch(num) {
106+
case 6:
107+
v.a = 1.0f;
108+
case 7:
109+
v.r = v.w;
110+
v.rgba_valid = 1;
111+
break;
112+
default:
113+
v.rgba_valid = 0;
114+
v.r = v.g = v.b = v.a = 1.0f;
115+
}
102116
if(!(varr = mf_dynarr_push(varr, &v))) {
103-
fprintf(stderr, "mf_load: failed to resize vertex buffer\n");
117+
fprintf(stderr, "load_obj: failed to resize vertex buffer\n");
104118
goto end;
105119
}
106-
if(num > 3) {
107-
/* vertex color "extension" */
108-
switch(num) {
109-
case 4: col.y = col.x;
110-
case 5: col.z = col.x;
111-
case 6: col.w = 1.0f;
112-
}
113-
if(!(carr = mf_dynarr_push(carr, &col))) {
114-
fprintf(stderr, "mf_load: failed to resize buffer\n");
115-
goto end;
116-
}
117-
}
118120

119121
} else if(line[1] == 't' && isspace(line[2])) {
120122
/* texcoord */
@@ -125,7 +127,7 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
125127
}
126128
tc.y = 1.0f - tc.y;
127129
if(!(tarr = mf_dynarr_push(tarr, &tc))) {
128-
fprintf(stderr, "mf_load: failed to resize texcoord buffer\n");
130+
fprintf(stderr, "load_obj: failed to resize texcoord buffer\n");
129131
goto end;
130132
}
131133

@@ -137,7 +139,7 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
137139
goto end;
138140
}
139141
if(!(narr = mf_dynarr_push(narr, &norm))) {
140-
fprintf(stderr, "mf_load: failed to resize normal buffer\n");
142+
fprintf(stderr, "load_obj: failed to resize normal buffer\n");
141143
goto end;
142144
}
143145
}
@@ -175,29 +177,29 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
175177
} else {
176178
unsigned int newidx = mesh->num_verts;
177179
struct facevertex *newfv;
178-
mf_vec3 *vptr = varr + fv.vidx;
180+
struct vertex *vptr = varr + fv.vidx;
179181

180182
if(mf_add_vertex(mesh, vptr->x, vptr->y, vptr->z) == -1) {
181-
fprintf(stderr, "mf_load: failed to resize vertex array\n");
183+
fprintf(stderr, "load_obj: failed to resize vertex array\n");
182184
goto end;
183185
}
184-
if(!mf_dynarr_empty(carr)) {
186+
if(vptr->rgba_valid) {
185187
/* vertex color extension */
186-
if(mf_add_color(mesh, carr[fv.vidx].x, carr[fv.vidx].y,
187-
carr[fv.vidx].z, carr[fv.vidx].w) == -1) {
188-
fprintf(stderr, "mf_load: failed to resize color array\n");
188+
if(mf_add_color(mesh, varr[fv.vidx].r, varr[fv.vidx].g,
189+
varr[fv.vidx].b, varr[fv.vidx].a) == -1) {
190+
fprintf(stderr, "load_obj: failed to resize color array\n");
189191
goto end;
190192
}
191193
}
192194
if(fv.nidx >= 0) {
193195
if(mf_add_normal(mesh, narr[fv.nidx].x, narr[fv.nidx].y, narr[fv.nidx].z) == -1) {
194-
fprintf(stderr, "mf_load: failed to resize normal array\n");
196+
fprintf(stderr, "load_obj: failed to resize normal array\n");
195197
goto end;
196198
}
197199
}
198200
if(fv.tidx >= 0) {
199201
if(mf_add_texcoord(mesh, tarr[fv.tidx].x, tarr[fv.tidx].y) == -1) {
200-
fprintf(stderr, "mf_load: failed to resize texcoord array\n");
202+
fprintf(stderr, "load_obj: failed to resize texcoord array\n");
201203
goto end;
202204
}
203205
}
@@ -207,7 +209,7 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
207209
*newfv = fv;
208210
}
209211
if(!newfv || rb_insert(rbtree, newfv, (void*)(uintptr_t)newidx) == -1) {
210-
fprintf(stderr, "mf_load: failed to insert facevertex to the binary search tree\n");
212+
fprintf(stderr, "load_obj: failed to insert facevertex to the binary search tree\n");
211213
goto end;
212214
}
213215
}
@@ -219,7 +221,7 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
219221
res = mf_add_triangle(mesh, vidx[0], vidx[1], vidx[2]);
220222
}
221223
if(res == -1) {
222-
fprintf(stderr, "mf_load: failed to resize index array\n");
224+
fprintf(stderr, "load_obj: failed to resize index array\n");
223225
goto end;
224226
}
225227
}
@@ -228,12 +230,12 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
228230
case 'o':
229231
case 'g':
230232
if(mesh_done(mf, mesh) != -1 && !(mesh = mf_alloc_mesh())) {
231-
fprintf(stderr, "mf_load: failed to allocate mesh\n");
233+
fprintf(stderr, "load_obj: failed to allocate mesh\n");
232234
goto end;
233235
}
234236
mesh->name = clean_line(line + 1);
235237
if(!(mesh->name = strdup(mesh->name ? mesh->name : "unnamed mesh"))) {
236-
fprintf(stderr, "mf_load: failed to allocate mesh name\n");
238+
fprintf(stderr, "load_obj: failed to allocate mesh name\n");
237239
goto end;
238240
}
239241
break;
@@ -242,7 +244,7 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
242244
if(memcmp(line, "mtllib", 6) == 0) {
243245
const char *mtlfile = clean_line(line + 6);
244246
if(!mtlfile) {
245-
fprintf(stderr, "mf_load: ignoring invalid mtllib\n");
247+
fprintf(stderr, "load_obj: ignoring invalid mtllib\n");
246248
continue;
247249
}
248250
mtlfile = mf_find_asset(mf, mtlfile);
@@ -251,7 +253,7 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
251253
load_mtl(mf, &subio);
252254
io->close(subio.file);
253255
} else {
254-
fprintf(stderr, "mf_load: failed to open material library: %s, ignoring\n", mtlfile);
256+
fprintf(stderr, "load_obj: failed to open material library: %s, ignoring\n", mtlfile);
255257
}
256258

257259
} else if(memcmp(line, "usemtl", 6) == 0) {
@@ -273,7 +275,6 @@ int mf_load_obj(struct mf_meshfile *mf, const struct mf_userio *io)
273275
mf_dynarr_free(varr);
274276
mf_dynarr_free(narr);
275277
mf_dynarr_free(tarr);
276-
mf_dynarr_free(carr);
277278
mf_free_mesh(mesh);
278279
rb_free(rbtree);
279280
return result;
@@ -287,22 +288,33 @@ static int mesh_done(struct mf_meshfile *mf, struct mf_mesh *mesh)
287288
return -1;
288289
}
289290

291+
if(mesh->color && mf_dynarr_size(mesh->color) != mf_dynarr_size(mesh->vertex)) {
292+
/* we can end up with short color arrays in a mesh if some of its
293+
* vertices didn't have valid vertex colors. We don't support that, so
294+
* we'll just discard the color array altogether.
295+
*/
296+
mf_dynarr_free(mesh->color);
297+
mesh->color = 0;
298+
fprintf(stderr, "load_obj: ignoring partial vertex colors in mesh %s\n",
299+
mesh->name);
300+
}
301+
290302
if(mesh->normal) {
291303
if(mf_dynarr_size(mesh->normal) != mf_dynarr_size(mesh->vertex)) {
292-
fprintf(stderr, "mf_load: ignoring mesh with inconsistent attributes\n");
304+
fprintf(stderr, "load_obj: ignoring mesh with inconsistent attributes\n");
293305
goto reset_mesh;
294306
}
295307
}
296308
if(mesh->texcoord) {
297309
if(mf_dynarr_size(mesh->texcoord) != mf_dynarr_size(mesh->vertex)) {
298-
fprintf(stderr, "mf_load: ignoring mesh with inconsistent attributes\n");
310+
fprintf(stderr, "load_obj: ignoring mesh with inconsistent attributes\n");
299311
goto reset_mesh;
300312
}
301313
}
302314

303315
/* also allocate a node for it */
304316
if(!(node = mf_alloc_node())) {
305-
fprintf(stderr, "mf_load: failed to allocate mesh node\n");
317+
fprintf(stderr, "load_obj: failed to allocate mesh node\n");
306318
goto reset_mesh;
307319
}
308320
if(!(node->name = strdup(mesh->name))) {
@@ -311,12 +323,12 @@ static int mesh_done(struct mf_meshfile *mf, struct mf_mesh *mesh)
311323
}
312324

313325
if(mf_node_add_mesh(node, mesh) == -1) {
314-
fprintf(stderr, "mf_load: failed to add mesh to node\n");
326+
fprintf(stderr, "load_obj: failed to add mesh to node\n");
315327
goto reset_mesh;
316328
}
317329

318330
if(mf_add_mesh(mf, mesh) == -1) {
319-
fprintf(stderr, "mf_load: failed to add mesh\n");
331+
fprintf(stderr, "load_obj: failed to add mesh\n");
320332
goto reset_mesh;
321333
}
322334
mf_add_node(mf, node);

0 commit comments

Comments
 (0)