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 (5 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Created robjutil tool to manipulate Wavefront .OBJ files

File Contents

# User Rev Content
1 greg 2.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     }