ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/mesh.c
Revision: 2.37
Committed: Thu Dec 12 23:04:31 2024 UTC (4 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.36: +4 -6 lines
Log Message:
perf: Minor optimization

File Contents

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