ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/mesh.c
Revision: 2.35
Committed: Thu Jun 10 00:27:25 2021 UTC (2 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, HEAD
Changes since 2.34: +54 -10 lines
Log Message:
perf: made mesh self-consistency tests much more thorough

File Contents

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