ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/objutil.c
Revision: 2.1
Committed: Mon Mar 30 18:28:35 2020 UTC (4 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Created robjutil tool to manipulate Wavefront .OBJ files

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
3 #endif
4 /*
5 * Basic .OBJ scene handling routines.
6 *
7 * Created by Greg Ward on Wed Feb 11 2004.
8 */
9
10 #include <stdlib.h>
11 #include "rtio.h"
12 #include "rtmath.h"
13 #include "rterror.h"
14 #include "lookup.h"
15 #include "objutil.h"
16
17 /* Find an existing name in a list of names */
18 int
19 findName(const char *nm, const char **nmlist, int n)
20 {
21 register int i;
22
23 for (i = n; i-- > 0; )
24 if (!strcmp(nmlist[i], nm))
25 break;
26 return(i);
27 }
28
29 /* Clear face selection */
30 void
31 clearSelection(Scene *sc, int set)
32 {
33 Face *f;
34
35 for (f = sc->flist; f != NULL; f = f->next)
36 if (set)
37 f->flags |= FACE_SELECTED;
38 else
39 f->flags &= ~FACE_SELECTED;
40 }
41
42 /* Select faces by object name (modifies current) */
43 void
44 selectGroup(Scene *sc, const char *gname, int invert)
45 {
46 int gid = findName(gname, (const char **)sc->grpname, sc->ngrps);
47 Face *f;
48
49 if (gid < 0)
50 return;
51 for (f = sc->flist; f != NULL; f = f->next)
52 if (f->grp == gid) {
53 if (invert)
54 f->flags &= ~FACE_SELECTED;
55 else
56 f->flags |= FACE_SELECTED;
57 }
58 }
59
60 /* Select faces by material name (modifies current) */
61 void
62 selectMaterial(Scene *sc, const char *mname, int invert)
63 {
64 int mid = findName(mname, (const char **)sc->matname, sc->nmats);
65 Face *f;
66
67 if (mid < 0)
68 return;
69 for (f = sc->flist; f != NULL; f = f->next)
70 if (f->mat == mid) {
71 if (invert)
72 f->flags &= ~FACE_SELECTED;
73 else
74 f->flags |= FACE_SELECTED;
75 }
76 }
77
78 /* Invert face selection */
79 void
80 invertSelection(Scene *sc)
81 {
82 Face *f;
83
84 for (f = sc->flist; f != NULL; f = f->next)
85 f->flags ^= FACE_SELECTED;
86 }
87
88 /* Count selected faces */
89 int
90 numberSelected(Scene *sc)
91 {
92 int nsel = 0;
93 Face *f;
94
95 for (f = sc->flist; f != NULL; f = f->next)
96 nsel += ((f->flags & FACE_SELECTED) != 0);
97 return(nsel);
98 }
99
100 /* Execute callback on indicated faces */
101 int
102 foreachFace(Scene *sc, int (*cb)(Scene *, Face *, void *),
103 int flreq, int flexc, void *c_data)
104 {
105 int fltest = flreq | flexc;
106 int sum = 0;
107 Face *f;
108
109 if ((sc == NULL) | (cb == NULL))
110 return(0);
111 for (f = sc->flist; f != NULL; f = f->next)
112 if ((f->flags & fltest) == flreq) {
113 int res = (*cb)(sc, f, c_data);
114 if (res < 0)
115 return(res);
116 sum += res;
117 }
118 return(sum);
119 }
120
121 /* Callback for removeTexture() */
122 static int
123 remFaceTexture(Scene *sc, Face *f, void *dummy)
124 {
125 int hadTexture = 0;
126 int i;
127
128 for (i = f->nv; i-- > 0; ) {
129 if (f->v[i].tid < 0)
130 continue;
131 f->v[i].tid = -1;
132 hadTexture = 1;
133 }
134 return(hadTexture);
135 }
136
137 /* Remove texture coordinates from the indicated faces */
138 int
139 removeTexture(Scene *sc, int flreq, int flexc)
140 {
141 return(foreachFace(sc, remFaceTexture, flreq, flexc, NULL));
142 }
143
144 /* Callback for removeNormals() */
145 static int
146 remFaceNormal(Scene *sc, Face *f, void *dummy)
147 {
148 int hadNormal = 0;
149 int i;
150
151 for (i = f->nv; i-- > 0; ) {
152 if (f->v[i].nid < 0)
153 continue;
154 f->v[i].nid = -1;
155 hadNormal = 1;
156 }
157 return(hadNormal);
158 }
159
160 /* Remove surface normals from the indicated faces */
161 int
162 removeNormals(Scene *sc, int flreq, int flexc)
163 {
164 return(foreachFace(sc, remFaceNormal, flreq, flexc, NULL));
165 }
166
167 /* Callback for changeGroup() */
168 static int
169 chngFaceGroup(Scene *sc, Face *f, void *ptr)
170 {
171 int grp = *(int *)ptr;
172 if (f->grp == grp)
173 return(0);
174 f->grp = grp;
175 return(1);
176 }
177
178 /* Change group for the indicated faces */
179 int
180 changeGroup(Scene *sc, const char *gname, int flreq, int flexc)
181 {
182 int grp = findName(gname, (const char **)sc->grpname, sc->ngrps);
183 if (grp < 0) {
184 sc->grpname = chunk_alloc(char *, sc->grpname, sc->ngrps);
185 sc->grpname[grp=sc->ngrps++] = savqstr((char *)gname);
186 }
187 return(foreachFace(sc, chngFaceGroup, flreq, flexc, (void *)&grp));
188 }
189
190 /* Callback for changeMaterial() */
191 static int
192 chngFaceMaterial(Scene *sc, Face *f, void *ptr)
193 {
194 int mat = *(int *)ptr;
195 if (f->mat == mat)
196 return(0);
197 f->mat = mat;
198 return(1);
199 }
200
201 /* Change material for the indicated faces */
202 int
203 changeMaterial(Scene *sc, const char *mname, int flreq, int flexc)
204 {
205 int mat = findName(mname, (const char **)sc->matname, sc->nmats);
206 if (mat < 0) {
207 sc->matname = chunk_alloc(char *, sc->matname, sc->nmats);
208 sc->matname[mat=sc->nmats++] = savqstr((char *)mname);
209 }
210 return(foreachFace(sc, chngFaceMaterial, flreq, flexc, (void *)&mat));
211 }
212
213 /* Compare floating point values for (near) equality */
214 static int
215 fdiffer(double f1, double f2, double eps)
216 {
217 if (f2 != .0)
218 f1 = f1/f2 - 1.;
219 return((f1 > eps) | (f1 < -eps));
220 }
221
222 /* Compare two texture coordinates for (near) equality */
223 static int
224 tex_diff(const TexCoord *t1, const TexCoord *t2, double eps)
225 {
226 if (fdiffer(t1->u, t2->u, eps))
227 return(1);
228 if (fdiffer(t1->v, t2->v, eps))
229 return(1);
230 return(0);
231 }
232
233 /* Compare two surface normals for (near) equality */
234 static int
235 norm_diff(const Normal n1, const Normal n2, double eps)
236 {
237 if (fabs(n1[0]-n2[0]) > eps)
238 return(1);
239 if (fabs(n1[1]-n2[1]) > eps)
240 return(1);
241 if (fabs(n1[2]-n2[2]) > eps)
242 return(1);
243 return(0);
244 }
245
246 /* Replace the given vertex with an equivalent one */
247 static int
248 replace_vertex(Scene *sc, int prev, int repl, double eps)
249 {
250 int repl_tex[10];
251 int repl_norm[10];
252 Face *f, *flast=NULL;
253 int i, j=0;
254 /* check to see if it's even used */
255 if (sc->vert[prev].vflist == NULL)
256 return(0);
257 /* get replacement textures */
258 repl_tex[0] = -1;
259 for (f = sc->vert[repl].vflist; f != NULL; f = f->v[j].fnext) {
260 /* make sure prev isn't in there */
261 for (j = 0; j < f->nv; j++)
262 if (f->v[j].vid == prev)
263 return(0);
264 for (j = 0; j < f->nv; j++)
265 if (f->v[j].vid == repl)
266 break;
267 if (j >= f->nv)
268 goto linkerr;
269 if (f->v[j].tid < 0)
270 continue;
271 /* see if it's new */
272 for (i = 0; repl_tex[i] >= 0; i++) {
273 if (repl_tex[i] == f->v[j].tid)
274 break;
275 if (!tex_diff(&sc->tex[repl_tex[i]],
276 &sc->tex[f->v[j].tid], eps)) {
277 f->v[j].tid = repl_tex[i];
278 break; /* consolidate */
279 }
280 }
281 if (repl_tex[i] >= 0)
282 continue; /* have this one already */
283 /* else add it */
284 repl_tex[i++] = f->v[j].tid;
285 repl_tex[i] = -1;
286 if (i >= 9)
287 break; /* that's all we have room for */
288 }
289 /* get replacement normals */
290 repl_norm[0] = -1;
291 for (f = sc->vert[repl].vflist; f != NULL; f = f->v[j].fnext) {
292 for (j = 0; j < f->nv; j++)
293 if (f->v[j].vid == repl)
294 break;
295 if (f->v[j].nid < 0)
296 continue;
297 /* see if it's new */
298 for (i = 0; repl_norm[i] >= 0; i++) {
299 if (repl_norm[i] == f->v[j].nid)
300 break;
301 if (!norm_diff(sc->norm[repl_norm[i]],
302 sc->norm[f->v[j].nid], eps)) {
303 f->v[j].nid = repl_norm[i];
304 break; /* consolidate */
305 }
306 }
307 if (repl_norm[i] >= 0)
308 continue; /* have this one already */
309 /* else add it */
310 repl_norm[i++] = f->v[j].nid;
311 repl_norm[i] = -1;
312 if (i >= 9)
313 break; /* that's all we have room for */
314 }
315 /* replace occurrences of vertex */
316 for (f = sc->vert[prev].vflist; f != NULL; f = f->v[j].fnext) {
317 for (j = 0; j < f->nv; j++)
318 if (f->v[j].vid == prev)
319 break;
320 if (j >= f->nv)
321 goto linkerr;
322 /* XXX doesn't allow for multiple references to prev in face */
323 f->v[j].vid = repl; /* replace vertex itself */
324 if (faceArea(sc, f, NULL) <= FTINY)
325 f->flags |= FACE_DEGENERATE;
326 if (f->v[j].tid >= 0) /* replace texture if appropriate */
327 for (i = 0; repl_tex[i] >= 0; i++) {
328 if (repl_tex[i] == f->v[j].tid)
329 break;
330 if (!tex_diff(&sc->tex[repl_tex[i]],
331 &sc->tex[f->v[j].tid], eps)) {
332 f->v[j].tid = repl_tex[i];
333 break;
334 }
335 }
336 if (f->v[j].nid >= 0) /* replace normal if appropriate */
337 for (i = 0; repl_norm[i] >= 0; i++) {
338 if (repl_norm[i] == f->v[j].nid)
339 break;
340 if (!norm_diff(sc->norm[repl_norm[i]],
341 sc->norm[f->v[j].nid], eps)) {
342 f->v[j].nid = repl_norm[i];
343 break;
344 }
345 }
346 flast = f;
347 }
348 /* transfer face list */
349 flast->v[j].fnext = sc->vert[repl].vflist;
350 sc->vert[repl].vflist = sc->vert[prev].vflist;
351 sc->vert[prev].vflist = NULL;
352 return(1);
353 linkerr:
354 error(CONSISTENCY, "Link error in replace_vertex()");
355 return(0); /* shouldn't return */
356 }
357
358 /* Eliminate duplicate vertices, return # joined */
359 int
360 coalesceVertices(Scene *sc, double eps)
361 {
362 int nelim = 0;
363 LUTAB myLookup;
364 LUENT *le;
365 char vertfmt[32], vertbuf[64];
366 double d;
367 int i;
368
369 if (eps >= 1.)
370 return(0);
371 if (sc->nverts <= 3)
372 return(0);
373 /* create hash table */
374 myLookup.hashf = lu_shash;
375 myLookup.keycmp = (lut_keycmpf_t *)strcmp;
376 myLookup.freek = (lut_free_t *)freeqstr;
377 myLookup.freed = NULL;
378 if (!lu_init(&myLookup, sc->nverts))
379 return(0);
380 if (eps <= 5e-15)
381 strcpy(vertfmt, "%.15e %.15e %.15e");
382 else {
383 int nsigdig = 0;
384 for (d = eps; d < 0.5; d *= 10.)
385 ++nsigdig;
386 sprintf(vertfmt, "%%.%de %%.%de %%.%de",
387 nsigdig, nsigdig, nsigdig);
388 }
389 /* find coicident vertices */
390 for (i = 0; i < sc->nverts; i++) {
391 if (verbose && !((i+1) & 0x3fff))
392 fprintf(stderr, "%3d%% complete\r", 100*i/sc->nverts);
393 /* check for match */
394 sprintf(vertbuf, vertfmt, sc->vert[i].p[0],
395 sc->vert[i].p[1], sc->vert[i].p[2]);
396 le = lu_find(&myLookup, vertbuf);
397 if (le->data != NULL) { /* coincident vertex */
398 nelim += replace_vertex(sc, i,
399 (Vertex *)le->data - sc->vert, eps);
400 continue;
401 }
402 if (le->key == NULL) /* else create new entry */
403 le->key = savqstr(vertbuf);
404 le->data = (char *)&sc->vert[i];
405 }
406 lu_done(&myLookup); /* clean up */
407 return(nelim);
408 }
409
410 /* Identify duplicate faces */
411 int
412 findDuplicateFaces(Scene *sc)
413 {
414 int nchecked = 0;
415 int nfound = 0;
416 Face *f, *f1;
417 int vid;
418 int i, j;
419 /* start fresh */
420 for (f = sc->flist; f != NULL; f = f->next)
421 f->flags &= ~FACE_DUPLICATE;
422 /* check each face */
423 for (f = sc->flist; f != NULL; f = f->next) {
424 nchecked++;
425 if (verbose && !(nchecked & 0x3fff))
426 fprintf(stderr, "%3d%% complete\r",
427 100*nchecked/sc->nfaces);
428 if (f->flags & FACE_DUPLICATE)
429 continue; /* already identified */
430 vid = f->v[0].vid;
431 /* look for duplicates */
432 for (f1 = sc->vert[vid].vflist; f1 != NULL;
433 f1 = f1->v[j].fnext) {
434 for (j = 0; j < f1->nv; j++)
435 if (f1->v[j].vid == vid)
436 break;
437 if (j >= f1->nv)
438 break; /* missing link! */
439 if (f1 == f)
440 continue; /* shouldn't happen */
441 if (f1->flags & FACE_DUPLICATE)
442 continue; /* already marked */
443 if (f1->nv != f->nv)
444 continue; /* couldn't be dup. */
445 for (i = f->nv; --i > 0; )
446 if (f->v[i].vid != f1->v[(j+i)%f1->nv].vid)
447 break; /* vertex mismatch */
448 if (i) {
449 #if DUP_CHECK_REVERSE /* check reverse direction */
450 for (i = f->nv; --i > 0; )
451 if (f1->v[(j+f1->nv-i)%f1->nv].vid
452 != f->v[i].vid)
453 break;
454 if (i) /* no match */
455 #endif
456 continue;
457 }
458 f1->flags |= FACE_DUPLICATE;
459 ++nfound;
460 }
461 }
462 return(nfound);
463 }
464
465 /* Delete indicated faces */
466 int
467 deleteFaces(Scene *sc, int flreq, int flexc)
468 {
469 int fltest = flreq | flexc;
470 int orig_nfaces = sc->nfaces;
471 Face fhead;
472 Face *f, *ftst;
473
474 fhead.next = sc->flist;
475 f = &fhead;
476 while ((ftst = f->next) != NULL)
477 if ((ftst->flags & fltest) == flreq) {
478 Face *vf; /* remove from vertex lists */
479 int vid, i, j;
480 for (i = 0; i < ftst->nv; i++) {
481 vid = ftst->v[i].vid;
482 vf = sc->vert[vid].vflist;
483 if (vf == ftst) {
484 sc->vert[vid].vflist = ftst->v[i].fnext;
485 continue;
486 }
487 while (vf != NULL) {
488 for (j = 0; j < vf->nv; j++)
489 if (vf->v[j].vid == vid)
490 break;
491 if (j >= vf->nv)
492 break; /* error */
493 if (vf->v[j].fnext == ftst) {
494 vf->v[j].fnext =
495 ftst->v[i].fnext;
496 break;
497 }
498 vf = vf->v[j].fnext;
499 }
500 }
501 f->next = ftst->next; /* remove from scene list */
502 efree((char *)ftst);
503 sc->nfaces--;
504 } else
505 f = f->next;
506 sc->flist = fhead.next;
507 return(orig_nfaces - sc->nfaces);
508 }
509
510 /* Compute face area (and normal) */
511 double
512 faceArea(const Scene *sc, const Face *f, Normal nrm)
513 {
514 FVECT fnrm;
515 double area;
516 FVECT v1, v2, v3;
517 double *p0;
518 int i;
519
520 if (f->flags & FACE_DEGENERATE)
521 return(.0); /* should we check this? */
522 fnrm[0] = fnrm[1] = fnrm[2] = .0;
523 p0 = sc->vert[f->v[0].vid].p;
524 VSUB(v1, sc->vert[f->v[1].vid].p, p0);
525 for (i = 2; i < f->nv; i++) {
526 VSUB(v2, sc->vert[f->v[i].vid].p, p0);
527 fcross(v3, v1, v2);
528 VADD(fnrm, fnrm, v3);
529 VCOPY(v1, v2);
530 }
531 area = 0.5*normalize(fnrm);
532 if (nrm != NULL) {
533 if (area != 0.)
534 VCOPY(nrm, fnrm);
535 else
536 nrm[0] = nrm[1] = nrm[2] = 0.;
537 }
538 return(area);
539 }
540
541 /* Add a descriptive comment */
542 void
543 addComment(Scene *sc, const char *comment)
544 {
545 sc->descr = chunk_alloc(char *, sc->descr, sc->ndescr);
546 sc->descr[sc->ndescr++] = savqstr((char *)comment);
547 }
548
549 /* Clear comments */
550 void
551 clearComments(Scene *sc)
552 {
553 while (sc->ndescr > 0)
554 freeqstr(sc->descr[--sc->ndescr]);
555 efree((char *)sc->descr);
556 sc->ndescr = 0;
557 }
558
559 /* Add a face to a vertex face list */
560 static void
561 add2facelist(Scene *sc, Face *f, int i)
562 {
563 int vid = f->v[i].vid;
564 Face *fp = sc->vert[vid].vflist;
565 int j;
566
567 f->v[i].fnext = NULL; /* will put at end */
568 if (fp == NULL) { /* new list */
569 sc->vert[vid].vflist = f;
570 return;
571 }
572 for ( ; ; ) { /* else find position */
573 if (fp == f)
574 return; /* already in list */
575 for (j = 0; j < fp->nv; j++)
576 if (fp->v[j].vid == vid)
577 break;
578 if (j >= fp->nv)
579 error(CONSISTENCY, "Link error in add2facelist()");
580 if (fp->v[j].fnext == NULL)
581 break; /* reached the end */
582 fp = fp->v[j].fnext;
583 }
584 fp->v[j].fnext = f; /* append new face */
585 }
586
587 /* Allocate an empty scene */
588 Scene *
589 newScene(void)
590 {
591 Scene *sc = (Scene *)ecalloc(1, sizeof(Scene));
592 /* default group & material */
593 sc->grpname = chunk_alloc(char *, sc->grpname, sc->ngrps);
594 sc->grpname[sc->ngrps++] = savqstr("DEFAULT_GROUP");
595 sc->matname = chunk_alloc(char *, sc->matname, sc->nmats);
596 sc->matname[sc->nmats++] = savqstr("DEFAULT_MATERIAL");
597
598 return(sc);
599 }
600
601 /* Duplicate a scene */
602 Scene *
603 dupScene(const Scene *osc)
604 {
605 Scene *sc;
606 const Face *fo;
607 Face *f;
608 int i;
609
610 if (osc == NULL)
611 return(NULL);
612 sc = newScene();
613 for (i = 0; i < osc->ndescr; i++)
614 addComment(sc, osc->descr[i]);
615 if (osc->ngrps) {
616 sc->grpname = (char **)emalloc(sizeof(char *) *
617 (osc->ngrps+(CHUNKSIZ-1)));
618 for (i = 0; i < osc->ngrps; i++)
619 sc->grpname[i] = savqstr(osc->grpname[i]);
620 sc->ngrps = osc->ngrps;
621 }
622 if (osc->nmats) {
623 sc->matname = (char **)emalloc(sizeof(char *) *
624 (osc->nmats+(CHUNKSIZ-1)));
625 for (i = 0; i < osc->nmats; i++)
626 sc->matname[i] = savqstr(osc->matname[i]);
627 sc->nmats = osc->nmats;
628 }
629 if (osc->nverts) {
630 sc->vert = (Vertex *)emalloc(sizeof(Vertex) *
631 (osc->nverts+(CHUNKSIZ-1)));
632 memcpy((void *)sc->vert, (const void *)osc->vert,
633 sizeof(Vertex)*osc->nverts);
634 sc->nverts = osc->nverts;
635 for (i = 0; i < sc->nverts; i++)
636 sc->vert[i].vflist = NULL;
637 }
638 if (osc->ntex) {
639 sc->tex = (TexCoord *)emalloc(sizeof(TexCoord) *
640 (osc->ntex+(CHUNKSIZ-1)));
641 memcpy((void *)sc->tex, (const void *)osc->tex,
642 sizeof(TexCoord)*osc->ntex);
643 sc->ntex = osc->ntex;
644 }
645 if (osc->nnorms) {
646 sc->norm = (Normal *)emalloc(sizeof(Normal) *
647 (osc->nnorms+CHUNKSIZ));
648 memcpy((void *)sc->norm, (const void *)osc->norm,
649 sizeof(Normal)*osc->nnorms);
650 sc->nnorms = osc->nnorms;
651 }
652 for (fo = osc->flist; fo != NULL; fo = fo->next) {
653 f = (Face *)emalloc(sizeof(Face) +
654 sizeof(VertEnt)*(fo->nv-3));
655 memcpy((void *)f, (const void *)fo, sizeof(Face) +
656 sizeof(VertEnt)*(fo->nv-3));
657 for (i = 0; i < f->nv; i++)
658 add2facelist(sc, f, i);
659 f->next = sc->flist;
660 sc->flist = f;
661 sc->nfaces++;
662 }
663 return(sc);
664 }
665
666 /* Free a scene */
667 void
668 freeScene(Scene *sc)
669 {
670 int i;
671 Face *f;
672
673 if (sc == NULL)
674 return;
675 clearComments(sc);
676 for (i = sc->ngrps; i-- > 0; )
677 freeqstr(sc->grpname[i]);
678 efree((char *)sc->grpname);
679 for (i = sc->nmats; i-- > 0; )
680 freeqstr(sc->matname[i]);
681 efree((char *)sc->matname);
682 efree((char *)sc->vert);
683 efree((char *)sc->tex);
684 efree((char *)sc->norm);
685 while ((f = sc->flist) != NULL) {
686 sc->flist = f->next;
687 efree((char *)f);
688 }
689 efree((char *)sc);
690 }