ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/mesh.c
Revision: 2.24
Committed: Tue Nov 6 01:04:23 2012 UTC (11 years, 5 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.23: +110 -56 lines
Log Message:
Added routine for cycling through all triangles

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: mesh.c,v 2.23 2011/02/18 00:40:25 greg Exp $";
3 #endif
4 /*
5 * Mesh support routines
6 */
7
8 #include <string.h>
9
10 #include "rtio.h"
11 #include "rtmath.h"
12 #include "rterror.h"
13 #include "paths.h"
14 #include "octree.h"
15 #include "object.h"
16 #include "otypes.h"
17 #include "mesh.h"
18
19 /* An encoded mesh vertex */
20 typedef struct {
21 int fl;
22 uint32 xyz[3];
23 int32 norm;
24 uint32 uv[2];
25 } MCVERT;
26
27 #define MPATCHBLKSIZ 128 /* patch allocation block size */
28
29 #define IO_LEGAL (IO_BOUNDS|IO_TREE|IO_SCENE)
30
31 static MESH *mlist = NULL; /* list of loaded meshes */
32
33
34 static unsigned long
35 cvhash(const char *p) /* hash an encoded vertex */
36 {
37 const MCVERT *cvp = (const MCVERT *)p;
38 unsigned long hval;
39
40 if (!(cvp->fl & MT_V))
41 return(0);
42 hval = cvp->xyz[0] ^ cvp->xyz[1] << 11 ^ cvp->xyz[2] << 22;
43 if (cvp->fl & MT_N)
44 hval ^= cvp->norm;
45 if (cvp->fl & MT_UV)
46 hval ^= cvp->uv[0] ^ cvp->uv[1] << 16;
47 return(hval);
48 }
49
50
51 static int
52 cvcmp(const char *vv1, const char *vv2) /* compare encoded vertices */
53 {
54 const MCVERT *v1 = (const MCVERT *)vv1, *v2 = (const MCVERT *)vv2;
55 if (v1->fl != v2->fl)
56 return(1);
57 if (v1->xyz[0] != v2->xyz[0])
58 return(1);
59 if (v1->xyz[1] != v2->xyz[1])
60 return(1);
61 if (v1->xyz[2] != v2->xyz[2])
62 return(1);
63 if (v1->fl & MT_N && v1->norm != v2->norm)
64 return(1);
65 if (v1->fl & MT_UV) {
66 if (v1->uv[0] != v2->uv[0])
67 return(1);
68 if (v1->uv[1] != v2->uv[1])
69 return(1);
70 }
71 return(0);
72 }
73
74
75 MESH *
76 getmesh( /* get new mesh data reference */
77 char *mname,
78 int flags
79 )
80 {
81 char *pathname;
82 MESH *ms;
83
84 flags &= IO_LEGAL;
85 for (ms = mlist; ms != NULL; ms = ms->next)
86 if (!strcmp(mname, ms->name)) {
87 ms->nref++; /* increase reference count */
88 break;
89 }
90 if (ms == NULL) { /* load first time */
91 ms = (MESH *)calloc(1, sizeof(MESH));
92 if (ms == NULL)
93 error(SYSTEM, "out of memory in getmesh");
94 ms->name = savestr(mname);
95 ms->nref = 1;
96 ms->mcube.cutree = EMPTY;
97 ms->next = mlist;
98 mlist = ms;
99 }
100 if ((pathname = getpath(mname, getrlibpath(), R_OK)) == NULL) {
101 sprintf(errmsg, "cannot find mesh file \"%s\"", mname);
102 error(USER, errmsg);
103 }
104 flags &= ~ms->ldflags;
105 if (flags)
106 readmesh(ms, pathname, flags);
107 return(ms);
108 }
109
110
111 MESHINST *
112 getmeshinst( /* create mesh instance */
113 OBJREC *o,
114 int flags
115 )
116 {
117 MESHINST *ins;
118
119 flags &= IO_LEGAL;
120 if ((ins = (MESHINST *)o->os) == NULL) {
121 if ((ins = (MESHINST *)malloc(sizeof(MESHINST))) == NULL)
122 error(SYSTEM, "out of memory in getmeshinst");
123 if (o->oargs.nsargs < 1)
124 objerror(o, USER, "bad # of arguments");
125 if (fullxf(&ins->x, o->oargs.nsargs-1,
126 o->oargs.sarg+1) != o->oargs.nsargs-1)
127 objerror(o, USER, "bad transform");
128 if (ins->x.f.sca < 0.0) {
129 ins->x.f.sca = -ins->x.f.sca;
130 ins->x.b.sca = -ins->x.b.sca;
131 }
132 ins->msh = NULL;
133 o->os = (char *)ins;
134 }
135 if (ins->msh == NULL)
136 ins->msh = getmesh(o->oargs.sarg[0], flags);
137 else if ((flags &= ~ins->msh->ldflags))
138 readmesh(ins->msh,
139 getpath(o->oargs.sarg[0], getrlibpath(), R_OK),
140 flags);
141 return(ins);
142 }
143
144
145 int
146 nextmeshtri( /* get next triangle ID */
147 OBJECT *tip,
148 MESH *mp
149 )
150 {
151 int pn;
152 MESHPATCH *pp;
153
154 if (*tip == OVOID) { /* check for first index */
155 *tip = 0;
156 return(mp->npatches > 0); /* assumes 1 local triangle */
157 }
158 pn = *tip >> 10;
159 if (pn >= mp->npatches) /* past end? */
160 return(0);
161 pp = &mp->patch[pn];
162 if (!(*tip & 0x200)) { /* local triangle? */
163 if ((*tip & 0x1ff) < pp->ntris-1) {
164 ++*tip;
165 return(1);
166 }
167 *tip &= ~0x1ff; /* move on to single-joiners */
168 *tip |= 0x200;
169 if (pp->nj1tris) /* is there at least one? */
170 return(1);
171 }
172 if (!(*tip & 0x100)) { /* single joiner? */
173 if ((*tip & 0xff) < pp->nj1tris-1) {
174 ++*tip;
175 return(1);
176 }
177 *tip &= ~0xff; /* move on to double-joiners */
178 *tip |= 0x100;
179 if (pp->nj2tris) /* is there one? */
180 return(1);
181 }
182 if ((*tip & 0xff) < pp->nj2tris-1) { /* double-joiner? */
183 ++*tip;
184 return(1);
185 }
186 *tip = ++pn << 10; /* first in next patch */
187 return(pn < mp->npatches);
188 }
189
190 int
191 getmeshtrivid( /* get triangle vertex ID's */
192 int32 tvid[3],
193 OBJECT *mo,
194 MESH *mp,
195 OBJECT ti
196 )
197 {
198 int pn = ti >> 10;
199 MESHPATCH *pp;
200
201 if (pn >= mp->npatches)
202 return(0);
203 pp = &mp->patch[pn];
204 ti &= 0x3ff;
205 if (!(ti & 0x200)) { /* local triangle */
206 struct PTri *tp;
207 if (ti >= pp->ntris)
208 return(0);
209 tp = &pp->tri[ti];
210 tvid[0] = tvid[1] = tvid[2] = pn << 8;
211 tvid[0] |= tp->v1;
212 tvid[1] |= tp->v2;
213 tvid[2] |= tp->v3;
214 if (pp->trimat != NULL)
215 *mo = pp->trimat[ti];
216 else
217 *mo = pp->solemat;
218 if (*mo != OVOID)
219 *mo += mp->mat0;
220 return(1);
221 }
222 ti &= ~0x200;
223 if (!(ti & 0x100)) { /* single link vertex */
224 struct PJoin1 *tp1;
225 if (ti >= pp->nj1tris)
226 return(0);
227 tp1 = &pp->j1tri[ti];
228 tvid[0] = tp1->v1j;
229 tvid[1] = tvid[2] = pn << 8;
230 tvid[1] |= tp1->v2;
231 tvid[2] |= tp1->v3;
232 if ((*mo = tp1->mat) != OVOID)
233 *mo += mp->mat0;
234 return(1);
235 }
236 ti &= ~0x100;
237 { /* double link vertex */
238 struct PJoin2 *tp2;
239 if (ti >= pp->nj2tris)
240 return(0);
241 tp2 = &pp->j2tri[ti];
242 tvid[0] = tp2->v1j;
243 tvid[1] = tp2->v2j;
244 tvid[2] = pn << 8 | tp2->v3;
245 if ((*mo = tp2->mat) != OVOID)
246 *mo += mp->mat0;
247 }
248 return(1);
249 }
250
251
252 int
253 getmeshvert( /* get triangle vertex from ID */
254 MESHVERT *vp,
255 MESH *mp,
256 int32 vid,
257 int what
258 )
259 {
260 int pn = vid >> 8;
261 MESHPATCH *pp;
262 double vres;
263 int i;
264
265 vp->fl = 0;
266 if (pn >= mp->npatches)
267 return(0);
268 pp = &mp->patch[pn];
269 vid &= 0xff;
270 if (vid >= pp->nverts)
271 return(0);
272 /* get location */
273 if (what & MT_V) {
274 vres = (1./4294967296.)*mp->mcube.cusize;
275 for (i = 0; i < 3; i++)
276 vp->v[i] = mp->mcube.cuorg[i] +
277 (pp->xyz[vid][i] + .5)*vres;
278 vp->fl |= MT_V;
279 }
280 /* get normal */
281 if (what & MT_N && pp->norm != NULL && pp->norm[vid]) {
282 decodedir(vp->n, pp->norm[vid]);
283 vp->fl |= MT_N;
284 }
285 /* get (u,v) */
286 if (what & MT_UV && pp->uv != NULL && pp->uv[vid][0]) {
287 for (i = 0; i < 2; i++)
288 vp->uv[i] = mp->uvlim[0][i] +
289 (mp->uvlim[1][i] - mp->uvlim[0][i])*
290 (pp->uv[vid][i] + .5)*(1./4294967296.);
291 vp->fl |= MT_UV;
292 }
293 return(vp->fl);
294 }
295
296
297 OBJREC *
298 getmeshpseudo( /* get mesh pseudo object for material */
299 MESH *mp,
300 OBJECT mo
301 )
302 {
303 if (mo < mp->mat0 || mo >= mp->mat0 + mp->nmats)
304 error(INTERNAL, "modifier out of range in getmeshpseudo");
305 if (mp->pseudo == NULL) {
306 int i;
307 mp->pseudo = (OBJREC *)calloc(mp->nmats, sizeof(OBJREC));
308 if (mp->pseudo == NULL)
309 error(SYSTEM, "out of memory in getmeshpseudo");
310 for (i = mp->nmats; i--; ) {
311 mp->pseudo[i].omod = mp->mat0 + i;
312 mp->pseudo[i].otype = OBJ_FACE;
313 mp->pseudo[i].oname = "M-Tri";
314 }
315 }
316 return(&mp->pseudo[mo - mp->mat0]);
317 }
318
319
320 int
321 getmeshtri( /* get triangle vertices */
322 MESHVERT tv[3],
323 OBJECT *mo,
324 MESH *mp,
325 OBJECT ti,
326 int wha
327 )
328 {
329 int32 tvid[3];
330
331 if (!getmeshtrivid(tvid, mo, mp, ti))
332 return(0);
333
334 getmeshvert(&tv[0], mp, tvid[0], wha);
335 getmeshvert(&tv[1], mp, tvid[1], wha);
336 getmeshvert(&tv[2], mp, tvid[2], wha);
337
338 return(tv[0].fl & tv[1].fl & tv[2].fl);
339 }
340
341
342 int32
343 addmeshvert( /* find/add a mesh vertex */
344 MESH *mp,
345 MESHVERT *vp
346 )
347 {
348 LUENT *lvp;
349 MCVERT cv;
350 int i;
351
352 if (!(vp->fl & MT_V))
353 return(-1);
354 /* encode vertex */
355 for (i = 0; i < 3; i++) {
356 if (vp->v[i] < mp->mcube.cuorg[i])
357 return(-1);
358 if (vp->v[i] >= mp->mcube.cuorg[i] + mp->mcube.cusize)
359 return(-1);
360 cv.xyz[i] = (uint32)(4294967296. *
361 (vp->v[i] - mp->mcube.cuorg[i]) /
362 mp->mcube.cusize);
363 }
364 if (vp->fl & MT_N) /* assumes normalized! */
365 cv.norm = encodedir(vp->n);
366 if (vp->fl & MT_UV)
367 for (i = 0; i < 2; i++) {
368 if (vp->uv[i] <= mp->uvlim[0][i])
369 return(-1);
370 if (vp->uv[i] >= mp->uvlim[1][i])
371 return(-1);
372 cv.uv[i] = (uint32)(4294967296. *
373 (vp->uv[i] - mp->uvlim[0][i]) /
374 (mp->uvlim[1][i] - mp->uvlim[0][i]));
375 }
376 cv.fl = vp->fl;
377 if (mp->lut.tsiz == 0) {
378 mp->lut.hashf = cvhash;
379 mp->lut.keycmp = cvcmp;
380 mp->lut.freek = free;
381 if (!lu_init(&mp->lut, 50000))
382 goto nomem;
383 }
384 /* find entry */
385 lvp = lu_find(&mp->lut, (char *)&cv);
386 if (lvp == NULL)
387 goto nomem;
388 if (lvp->key == NULL) {
389 lvp->key = (char *)malloc(sizeof(MCVERT)+sizeof(int32));
390 memcpy((void *)lvp->key, (void *)&cv, sizeof(MCVERT));
391 }
392 if (lvp->data == NULL) { /* new vertex */
393 MESHPATCH *pp;
394 if (mp->npatches <= 0) {
395 mp->patch = (MESHPATCH *)calloc(MPATCHBLKSIZ,
396 sizeof(MESHPATCH));
397 if (mp->patch == NULL)
398 goto nomem;
399 mp->npatches = 1;
400 } else if (mp->patch[mp->npatches-1].nverts >= 256) {
401 if (mp->npatches % MPATCHBLKSIZ == 0) {
402 mp->patch = (MESHPATCH *)realloc(
403 (void *)mp->patch,
404 (mp->npatches + MPATCHBLKSIZ)*
405 sizeof(MESHPATCH));
406 memset((void *)(mp->patch + mp->npatches), '\0',
407 MPATCHBLKSIZ*sizeof(MESHPATCH));
408 }
409 if (mp->npatches++ >= 1L<<22)
410 error(INTERNAL, "too many mesh patches");
411 }
412 pp = &mp->patch[mp->npatches-1];
413 if (pp->xyz == NULL) {
414 pp->xyz = (uint32 (*)[3])calloc(256, 3*sizeof(int32));
415 if (pp->xyz == NULL)
416 goto nomem;
417 }
418 for (i = 0; i < 3; i++)
419 pp->xyz[pp->nverts][i] = cv.xyz[i];
420 if (cv.fl & MT_N) {
421 if (pp->norm == NULL) {
422 pp->norm = (int32 *)calloc(256, sizeof(int32));
423 if (pp->norm == NULL)
424 goto nomem;
425 }
426 pp->norm[pp->nverts] = cv.norm;
427 }
428 if (cv.fl & MT_UV) {
429 if (pp->uv == NULL) {
430 pp->uv = (uint32 (*)[2])calloc(256,
431 2*sizeof(uint32));
432 if (pp->uv == NULL)
433 goto nomem;
434 }
435 for (i = 0; i < 2; i++)
436 pp->uv[pp->nverts][i] = cv.uv[i];
437 }
438 pp->nverts++;
439 lvp->data = lvp->key + sizeof(MCVERT);
440 *(int32 *)lvp->data = (mp->npatches-1) << 8 | (pp->nverts-1);
441 }
442 return(*(int32 *)lvp->data);
443 nomem:
444 error(SYSTEM, "out of memory in addmeshvert");
445 return(-1);
446 }
447
448
449 OBJECT
450 addmeshtri( /* add a new mesh triangle */
451 MESH *mp,
452 MESHVERT tv[3],
453 OBJECT mo
454 )
455 {
456 int32 vid[3], t;
457 int pn[3], i;
458 MESHPATCH *pp;
459
460 if (!(tv[0].fl & tv[1].fl & tv[2].fl & MT_V))
461 return(OVOID);
462 /* find/allocate patch vertices */
463 for (i = 0; i < 3; i++) {
464 if ((vid[i] = addmeshvert(mp, &tv[i])) < 0)
465 return(OVOID);
466 pn[i] = vid[i] >> 8;
467 }
468 /* normalize material index */
469 if (mo != OVOID) {
470 if ((mo -= mp->mat0) >= mp->nmats)
471 mp->nmats = mo+1;
472 else if (mo < 0)
473 error(INTERNAL, "modifier range error in addmeshtri");
474 }
475 /* assign triangle */
476 if (pn[0] == pn[1] && pn[1] == pn[2]) { /* local case */
477 pp = &mp->patch[pn[0]];
478 if (pp->tri == NULL) {
479 pp->tri = (struct PTri *)malloc(
480 512*sizeof(struct PTri));
481 if (pp->tri == NULL)
482 goto nomem;
483 }
484 if (pp->ntris < 512) {
485 pp->tri[pp->ntris].v1 = vid[0] & 0xff;
486 pp->tri[pp->ntris].v2 = vid[1] & 0xff;
487 pp->tri[pp->ntris].v3 = vid[2] & 0xff;
488 if (pp->ntris == 0)
489 pp->solemat = mo;
490 else if (pp->trimat == NULL && mo != pp->solemat) {
491 pp->trimat = (int16 *)malloc(
492 512*sizeof(int16));
493 if (pp->trimat == NULL)
494 goto nomem;
495 for (i = pp->ntris; i--; )
496 pp->trimat[i] = pp->solemat;
497 }
498 if (pp->trimat != NULL)
499 pp->trimat[pp->ntris] = mo;
500 return(pn[0] << 10 | pp->ntris++);
501 }
502 }
503 if (pn[0] == pn[1]) {
504 t = vid[2]; vid[2] = vid[1]; vid[1] = vid[0]; vid[0] = t;
505 i = pn[2]; pn[2] = pn[1]; pn[1] = pn[0]; pn[0] = i;
506 } else if (pn[0] == pn[2]) {
507 t = vid[0]; vid[0] = vid[1]; vid[1] = vid[2]; vid[2] = t;
508 i = pn[0]; pn[0] = pn[1]; pn[1] = pn[2]; pn[2] = i;
509 }
510 if (pn[1] == pn[2]) { /* single link */
511 pp = &mp->patch[pn[1]];
512 if (pp->j1tri == NULL) {
513 pp->j1tri = (struct PJoin1 *)malloc(
514 256*sizeof(struct PJoin1));
515 if (pp->j1tri == NULL)
516 goto nomem;
517 }
518 if (pp->nj1tris < 256) {
519 pp->j1tri[pp->nj1tris].v1j = vid[0];
520 pp->j1tri[pp->nj1tris].v2 = vid[1] & 0xff;
521 pp->j1tri[pp->nj1tris].v3 = vid[2] & 0xff;
522 pp->j1tri[pp->nj1tris].mat = mo;
523 return(pn[1] << 10 | 0x200 | pp->nj1tris++);
524 }
525 }
526 /* double link */
527 pp = &mp->patch[pn[2]];
528 if (pp->j2tri == NULL) {
529 pp->j2tri = (struct PJoin2 *)malloc(
530 256*sizeof(struct PJoin2));
531 if (pp->j2tri == NULL)
532 goto nomem;
533 }
534 if (pp->nj2tris >= 256)
535 error(INTERNAL, "too many patch triangles in addmeshtri");
536 pp->j2tri[pp->nj2tris].v1j = vid[0];
537 pp->j2tri[pp->nj2tris].v2j = vid[1];
538 pp->j2tri[pp->nj2tris].v3 = vid[2] & 0xff;
539 pp->j2tri[pp->nj2tris].mat = mo;
540 return(pn[2] << 10 | 0x300 | pp->nj2tris++);
541 nomem:
542 error(SYSTEM, "out of memory in addmeshtri");
543 return(OVOID);
544 }
545
546
547 char *
548 checkmesh(MESH *mp) /* validate mesh data */
549 {
550 static char embuf[128];
551 int nouvbounds = 1;
552 int i;
553 /* basic checks */
554 if (mp == NULL)
555 return("NULL mesh pointer");
556 if (!mp->ldflags)
557 return("unassigned mesh");
558 if (mp->name == NULL)
559 return("missing mesh name");
560 if (mp->nref <= 0)
561 return("unreferenced mesh");
562 /* check boundaries */
563 if (mp->ldflags & IO_BOUNDS) {
564 if (mp->mcube.cusize <= FTINY)
565 return("illegal octree bounds in mesh");
566 nouvbounds = (mp->uvlim[1][0] - mp->uvlim[0][0] <= FTINY ||
567 mp->uvlim[1][1] - mp->uvlim[0][1] <= FTINY);
568 }
569 /* check octree */
570 if (mp->ldflags & IO_TREE) {
571 if (isempty(mp->mcube.cutree))
572 error(WARNING, "empty mesh octree");
573 }
574 /* check scene data */
575 if (mp->ldflags & IO_SCENE) {
576 if (!(mp->ldflags & IO_BOUNDS))
577 return("unbounded scene in mesh");
578 if (mp->mat0 < 0 || mp->mat0+mp->nmats > nobjects)
579 return("bad mesh modifier range");
580 for (i = mp->mat0+mp->nmats; i-- > mp->mat0; ) {
581 int otyp = objptr(i)->otype;
582 if (!ismodifier(otyp)) {
583 sprintf(embuf,
584 "non-modifier in mesh (%s \"%s\")",
585 ofun[otyp].funame, objptr(i)->oname);
586 return(embuf);
587 }
588 }
589 if (mp->npatches <= 0)
590 error(WARNING, "no patches in mesh");
591 for (i = 0; i < mp->npatches; i++) {
592 MESHPATCH *pp = &mp->patch[i];
593 if (pp->nverts <= 0)
594 error(WARNING, "no vertices in patch");
595 else {
596 if (pp->xyz == NULL)
597 return("missing patch vertex list");
598 if (nouvbounds && pp->uv != NULL)
599 return("unreferenced uv coordinates");
600 }
601 if (pp->ntris > 0 && pp->tri == NULL)
602 return("missing patch triangle list");
603 if (pp->nj1tris > 0 && pp->j1tri == NULL)
604 return("missing patch joiner triangle list");
605 if (pp->nj2tris > 0 && pp->j2tri == NULL)
606 return("missing patch double-joiner list");
607 }
608 }
609 return(NULL); /* seems OK */
610 }
611
612
613 static void
614 tallyoctree( /* tally octree size */
615 OCTREE ot,
616 int *ecp,
617 int *lcp,
618 int *ocp
619 )
620 {
621 int i;
622
623 if (isempty(ot)) {
624 (*ecp)++;
625 return;
626 }
627 if (isfull(ot)) {
628 OBJECT oset[MAXSET+1];
629 (*lcp)++;
630 objset(oset, ot);
631 *ocp += oset[0];
632 return;
633 }
634 for (i = 0; i < 8; i++)
635 tallyoctree(octkid(ot, i), ecp, lcp, ocp);
636 }
637
638
639 void
640 printmeshstats( /* print out mesh statistics */
641 MESH *ms,
642 FILE *fp
643 )
644 {
645 int lfcnt=0, lecnt=0, locnt=0;
646 int vcnt=0, ncnt=0, uvcnt=0;
647 int nscnt=0, uvscnt=0;
648 int tcnt=0, t1cnt=0, t2cnt=0;
649 int i, j;
650
651 tallyoctree(ms->mcube.cutree, &lecnt, &lfcnt, &locnt);
652 for (i = 0; i < ms->npatches; i++) {
653 MESHPATCH *pp = &ms->patch[i];
654 vcnt += pp->nverts;
655 if (pp->norm != NULL) {
656 for (j = pp->nverts; j--; )
657 if (pp->norm[j])
658 ncnt++;
659 nscnt += pp->nverts;
660 }
661 if (pp->uv != NULL) {
662 for (j = pp->nverts; j--; )
663 if (pp->uv[j][0])
664 uvcnt++;
665 uvscnt += pp->nverts;
666 }
667 tcnt += pp->ntris;
668 t1cnt += pp->nj1tris;
669 t2cnt += pp->nj2tris;
670 }
671 fprintf(fp, "Mesh statistics:\n");
672 fprintf(fp, "\t%ld materials\n", (long)ms->nmats);
673 fprintf(fp, "\t%d patches (%.2f MBytes)\n", ms->npatches,
674 (ms->npatches*sizeof(MESHPATCH) +
675 vcnt*3*sizeof(uint32) +
676 nscnt*sizeof(int32) +
677 uvscnt*2*sizeof(uint32) +
678 tcnt*sizeof(struct PTri) +
679 t1cnt*sizeof(struct PJoin1) +
680 t2cnt*sizeof(struct PJoin2))/(1024.*1024.));
681 fprintf(fp, "\t%d vertices (%.1f%% w/ normals, %.1f%% w/ uv)\n",
682 vcnt, 100.*ncnt/vcnt, 100.*uvcnt/vcnt);
683 fprintf(fp, "\t%d triangles (%.1f%% local, %.1f%% joiner)\n",
684 tcnt+t1cnt+t2cnt,
685 100.*tcnt/(tcnt+t1cnt+t2cnt),
686 100.*t1cnt/(tcnt+t1cnt+t2cnt));
687 fprintf(fp,
688 "\t%d leaves in octree (%.1f%% empty, %.2f avg. set size)\n",
689 lfcnt+lecnt, 100.*lecnt/(lfcnt+lecnt),
690 (double)locnt/lfcnt);
691 }
692
693
694 void
695 freemesh(MESH *ms) /* free mesh data */
696 {
697 MESH mhead;
698 MESH *msp;
699
700 if (ms == NULL)
701 return;
702 if (ms->nref <= 0)
703 error(CONSISTENCY, "unreferenced mesh in freemesh");
704 ms->nref--;
705 if (ms->nref) /* still in use */
706 return;
707 /* else remove from list */
708 mhead.next = mlist;
709 for (msp = &mhead; msp->next != NULL; msp = msp->next)
710 if (msp->next == ms) {
711 msp->next = ms->next;
712 ms->next = NULL;
713 break;
714 }
715 if (ms->next != NULL) /* can't be in list anymore */
716 error(CONSISTENCY, "unlisted mesh in freemesh");
717 mlist = mhead.next;
718 /* free mesh data */
719 freestr(ms->name);
720 octfree(ms->mcube.cutree);
721 lu_done(&ms->lut);
722 if (ms->npatches > 0) {
723 MESHPATCH *pp = ms->patch + ms->npatches;
724 while (pp-- > ms->patch) {
725 if (pp->j2tri != NULL)
726 free((void *)pp->j2tri);
727 if (pp->j1tri != NULL)
728 free((void *)pp->j1tri);
729 if (pp->tri != NULL)
730 free((void *)pp->tri);
731 if (pp->uv != NULL)
732 free((void *)pp->uv);
733 if (pp->norm != NULL)
734 free((void *)pp->norm);
735 if (pp->xyz != NULL)
736 free((void *)pp->xyz);
737 }
738 free((void *)ms->patch);
739 }
740 if (ms->pseudo != NULL)
741 free((void *)ms->pseudo);
742 free((void *)ms);
743 }
744
745
746 void
747 freemeshinst(OBJREC *o) /* free mesh instance */
748 {
749 if (o->os == NULL)
750 return;
751 freemesh((*(MESHINST *)o->os).msh);
752 free((void *)o->os);
753 o->os = NULL;
754 }