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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.37 static const char RCSid[] = "$Id: mesh.c,v 2.36 2024/12/12 19:55:57 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Mesh support routines
6     */
7    
8 schorsch 2.15 #include "rtio.h"
9     #include "rtmath.h"
10     #include "rterror.h"
11 greg 2.17 #include "paths.h"
12 greg 2.1 #include "octree.h"
13     #include "object.h"
14 greg 2.4 #include "otypes.h"
15 greg 2.1 #include "mesh.h"
16    
17     /* An encoded mesh vertex */
18     typedef struct {
19     int fl;
20 greg 2.8 uint32 xyz[3];
21     int32 norm;
22 greg 2.16 uint32 uv[2];
23 greg 2.1 } 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 greg 2.23 cvhash(const char *p) /* hash an encoded vertex */
34 greg 2.1 {
35 greg 2.20 const MCVERT *cvp = (const MCVERT *)p;
36 greg 2.1 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 greg 2.23 cvcmp(const char *vv1, const char *vv2) /* compare encoded vertices */
51 greg 2.1 {
52 greg 2.23 const MCVERT *v1 = (const MCVERT *)vv1, *v2 = (const MCVERT *)vv2;
53 greg 2.28
54 greg 2.1 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 greg 2.37 getmesh( /* get mesh data reference */
76 greg 2.24 char *mname,
77     int flags
78     )
79 greg 2.1 {
80     char *pathname;
81 greg 2.24 MESH *ms;
82 greg 2.1
83     flags &= IO_LEGAL;
84     for (ms = mlist; ms != NULL; ms = ms->next)
85 greg 2.37 if (!strcmp(mname, ms->name))
86 greg 2.10 break;
87 greg 2.36 if (ms == NULL) { /* new mesh entry? */
88 greg 2.1 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 greg 2.37 ms->nref++; /* bump reference count */
97 greg 2.36 if (!(flags &= ~ms->ldflags)) /* nothing to load? */
98     return(ms);
99 greg 2.7 if ((pathname = getpath(mname, getrlibpath(), R_OK)) == NULL) {
100 greg 2.1 sprintf(errmsg, "cannot find mesh file \"%s\"", mname);
101 greg 2.27 error(SYSTEM, errmsg);
102 greg 2.1 }
103 greg 2.36 readmesh(ms, pathname, flags);
104 greg 2.1 return(ms);
105     }
106    
107    
108     MESHINST *
109 greg 2.24 getmeshinst( /* create mesh instance */
110     OBJREC *o,
111     int flags
112     )
113 greg 2.1 {
114 greg 2.24 MESHINST *ins;
115 greg 2.1
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 greg 2.10 if (ins->msh == NULL)
133 greg 2.1 ins->msh = getmesh(o->oargs.sarg[0], flags);
134 greg 2.10 else if ((flags &= ~ins->msh->ldflags))
135     readmesh(ins->msh,
136     getpath(o->oargs.sarg[0], getrlibpath(), R_OK),
137     flags);
138 greg 2.1 return(ins);
139     }
140    
141    
142     int
143 greg 2.24 nextmeshtri( /* get next triangle ID */
144     OBJECT *tip,
145     MESH *mp
146     )
147     {
148     int pn;
149     MESHPATCH *pp;
150    
151 greg 2.26 pn = ++(*tip) >> 10; /* next triangle (OVOID init) */
152 greg 2.25 while (pn < mp->npatches) {
153     pp = &mp->patch[pn];
154     if (!(*tip & 0x200)) { /* local triangle? */
155 greg 2.26 if ((*tip & 0x1ff) < pp->ntris)
156 greg 2.25 return(1);
157     *tip &= ~0x1ff; /* move on to single-joiners */
158     *tip |= 0x200;
159     }
160     if (!(*tip & 0x100)) { /* single joiner? */
161 greg 2.26 if ((*tip & 0xff) < pp->nj1tris)
162 greg 2.25 return(1);
163     *tip &= ~0xff; /* move on to double-joiners */
164     *tip |= 0x100;
165 greg 2.24 }
166 greg 2.26 if ((*tip & 0xff) < pp->nj2tris)
167 greg 2.24 return(1);
168 greg 2.25 *tip = ++pn << 10; /* first in next patch */
169 greg 2.24 }
170 greg 2.25 return(0); /* out of patches */
171 greg 2.24 }
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 greg 2.1 {
181     int pn = ti >> 10;
182 greg 2.4 MESHPATCH *pp;
183 greg 2.1
184     if (pn >= mp->npatches)
185     return(0);
186 greg 2.4 pp = &mp->patch[pn];
187 greg 2.1 ti &= 0x3ff;
188     if (!(ti & 0x200)) { /* local triangle */
189     struct PTri *tp;
190 greg 2.4 if (ti >= pp->ntris)
191 greg 2.1 return(0);
192 greg 2.4 tp = &pp->tri[ti];
193 greg 2.1 tvid[0] = tvid[1] = tvid[2] = pn << 8;
194     tvid[0] |= tp->v1;
195     tvid[1] |= tp->v2;
196     tvid[2] |= tp->v3;
197 greg 2.4 if (pp->trimat != NULL)
198     *mo = pp->trimat[ti];
199     else
200     *mo = pp->solemat;
201     if (*mo != OVOID)
202     *mo += mp->mat0;
203 greg 2.1 return(1);
204     }
205     ti &= ~0x200;
206     if (!(ti & 0x100)) { /* single link vertex */
207     struct PJoin1 *tp1;
208 greg 2.4 if (ti >= pp->nj1tris)
209 greg 2.1 return(0);
210 greg 2.4 tp1 = &pp->j1tri[ti];
211 greg 2.1 tvid[0] = tp1->v1j;
212     tvid[1] = tvid[2] = pn << 8;
213     tvid[1] |= tp1->v2;
214     tvid[2] |= tp1->v3;
215 greg 2.4 if ((*mo = tp1->mat) != OVOID)
216     *mo += mp->mat0;
217 greg 2.1 return(1);
218     }
219     ti &= ~0x100;
220     { /* double link vertex */
221     struct PJoin2 *tp2;
222 greg 2.4 if (ti >= pp->nj2tris)
223 greg 2.1 return(0);
224 greg 2.4 tp2 = &pp->j2tri[ti];
225 greg 2.1 tvid[0] = tp2->v1j;
226     tvid[1] = tp2->v2j;
227     tvid[2] = pn << 8 | tp2->v3;
228 greg 2.4 if ((*mo = tp2->mat) != OVOID)
229     *mo += mp->mat0;
230 greg 2.1 }
231     return(1);
232     }
233    
234    
235     int
236 greg 2.24 getmeshvert( /* get triangle vertex from ID */
237     MESHVERT *vp,
238     MESH *mp,
239     int32 vid,
240     int what
241     )
242 greg 2.1 {
243     int pn = vid >> 8;
244     MESHPATCH *pp;
245     double vres;
246 greg 2.24 int i;
247 greg 2.1
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 greg 2.2 vp->uv[i] = mp->uvlim[0][i] +
272     (mp->uvlim[1][i] - mp->uvlim[0][i])*
273 greg 2.16 (pp->uv[vid][i] + .5)*(1./4294967296.);
274 greg 2.1 vp->fl |= MT_UV;
275     }
276     return(vp->fl);
277     }
278    
279    
280 greg 2.4 OBJREC *
281 greg 2.24 getmeshpseudo( /* get mesh pseudo object for material */
282     MESH *mp,
283     OBJECT mo
284     )
285 greg 2.4 {
286 greg 2.31 if ((mo < mp->mat0) | (mo >= mp->mat0 + mp->nmats))
287 greg 2.4 error(INTERNAL, "modifier out of range in getmeshpseudo");
288     if (mp->pseudo == NULL) {
289 greg 2.24 int i;
290 greg 2.4 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 greg 2.1 int
304 greg 2.24 getmeshtri( /* get triangle vertices */
305     MESHVERT tv[3],
306     OBJECT *mo,
307     MESH *mp,
308     OBJECT ti,
309     int wha
310     )
311 greg 2.1 {
312 greg 2.8 int32 tvid[3];
313 greg 2.1
314 greg 2.4 if (!getmeshtrivid(tvid, mo, mp, ti))
315 greg 2.1 return(0);
316    
317 greg 2.4 getmeshvert(&tv[0], mp, tvid[0], wha);
318     getmeshvert(&tv[1], mp, tvid[1], wha);
319     getmeshvert(&tv[2], mp, tvid[2], wha);
320 greg 2.1
321     return(tv[0].fl & tv[1].fl & tv[2].fl);
322     }
323    
324    
325 greg 2.8 int32
326 greg 2.24 addmeshvert( /* find/add a mesh vertex */
327 greg 2.28 MESH *mp,
328 greg 2.24 MESHVERT *vp
329     )
330 greg 2.1 {
331 greg 2.28 LUENT *lvp;
332     MCVERT cv;
333 greg 2.24 int i;
334 greg 2.1
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 greg 2.8 cv.xyz[i] = (uint32)(4294967296. *
344 greg 2.1 (vp->v[i] - mp->mcube.cuorg[i]) /
345     mp->mcube.cusize);
346     }
347 greg 2.16 if (vp->fl & MT_N) /* assumes normalized! */
348 greg 2.1 cv.norm = encodedir(vp->n);
349     if (vp->fl & MT_UV)
350     for (i = 0; i < 2; i++) {
351 greg 2.2 if (vp->uv[i] <= mp->uvlim[0][i])
352 greg 2.1 return(-1);
353 greg 2.2 if (vp->uv[i] >= mp->uvlim[1][i])
354 greg 2.1 return(-1);
355 greg 2.16 cv.uv[i] = (uint32)(4294967296. *
356 greg 2.2 (vp->uv[i] - mp->uvlim[0][i]) /
357     (mp->uvlim[1][i] - mp->uvlim[0][i]));
358 greg 2.1 }
359     cv.fl = vp->fl;
360 greg 2.4 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 greg 2.1 goto nomem;
366     }
367     /* find entry */
368 greg 2.4 lvp = lu_find(&mp->lut, (char *)&cv);
369 greg 2.1 if (lvp == NULL)
370     goto nomem;
371     if (lvp->key == NULL) {
372 greg 2.8 lvp->key = (char *)malloc(sizeof(MCVERT)+sizeof(int32));
373 greg 2.31 memcpy(lvp->key, &cv, sizeof(MCVERT));
374 greg 2.1 }
375     if (lvp->data == NULL) { /* new vertex */
376 greg 2.24 MESHPATCH *pp;
377 greg 2.1 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 greg 2.31 if (mp->npatches >= 1L<<22)
385     error(INTERNAL, "too many mesh patches");
386 greg 2.1 if (mp->npatches % MPATCHBLKSIZ == 0) {
387 greg 2.31 mp->patch = (MESHPATCH *)realloc(mp->patch,
388     (mp->npatches + MPATCHBLKSIZ)*
389     sizeof(MESHPATCH));
390     memset((mp->patch + mp->npatches), '\0',
391 greg 2.1 MPATCHBLKSIZ*sizeof(MESHPATCH));
392     }
393 greg 2.31 mp->npatches++;
394 greg 2.1 }
395     pp = &mp->patch[mp->npatches-1];
396     if (pp->xyz == NULL) {
397 greg 2.8 pp->xyz = (uint32 (*)[3])calloc(256, 3*sizeof(int32));
398 greg 2.1 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 greg 2.8 pp->norm = (int32 *)calloc(256, sizeof(int32));
406 greg 2.1 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 greg 2.16 pp->uv = (uint32 (*)[2])calloc(256,
414     2*sizeof(uint32));
415 greg 2.1 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 greg 2.8 *(int32 *)lvp->data = (mp->npatches-1) << 8 | (pp->nverts-1);
424 greg 2.1 }
425 greg 2.8 return(*(int32 *)lvp->data);
426 greg 2.1 nomem:
427     error(SYSTEM, "out of memory in addmeshvert");
428     return(-1);
429     }
430    
431    
432     OBJECT
433 greg 2.24 addmeshtri( /* add a new mesh triangle */
434     MESH *mp,
435     MESHVERT tv[3],
436     OBJECT mo
437     )
438 greg 2.1 {
439 greg 2.28 int32 vid[3], t;
440     int pn[3], i;
441 greg 2.24 MESHPATCH *pp;
442 greg 2.1
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 greg 2.4 /* normalize material index */
452 schorsch 2.13 if (mo != OVOID) {
453 greg 2.4 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 schorsch 2.13 }
458 greg 2.1 /* assign triangle */
459 greg 2.28 if ((pn[0] == pn[1]) & (pn[1] == pn[2])) { /* local case */
460 greg 2.1 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 greg 2.2 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 greg 2.4 if (pp->ntris == 0)
472     pp->solemat = mo;
473     else if (pp->trimat == NULL && mo != pp->solemat) {
474 greg 2.8 pp->trimat = (int16 *)malloc(
475     512*sizeof(int16));
476 greg 2.4 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 greg 2.2 return(pn[0] << 10 | pp->ntris++);
484     }
485 greg 2.28 } else if (pn[0] == pn[1]) {
486 greg 2.2 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 greg 2.1 } else if (pn[0] == pn[2]) {
489 greg 2.2 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 greg 2.1 }
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 greg 2.2 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 greg 2.4 pp->j1tri[pp->nj1tris].mat = mo;
505 greg 2.2 return(pn[1] << 10 | 0x200 | pp->nj1tris++);
506     }
507 greg 2.1 }
508     /* double link */
509 greg 2.29 pp = &mp->patch[pn[i=0]];
510 greg 2.34 if (mp->patch[pn[1]].nj2tris < pp->nj2tris)
511 greg 2.29 pp = &mp->patch[pn[i=1]];
512 greg 2.34 if (mp->patch[pn[2]].nj2tris < pp->nj2tris)
513 greg 2.29 pp = &mp->patch[pn[i=2]];
514     if (pp->nj2tris >= 256)
515     error(INTERNAL, "too many patch triangles in addmeshtri");
516 greg 2.1 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 greg 2.4 pp->j2tri[pp->nj2tris].mat = mo;
523 greg 2.29 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 greg 2.1 nomem:
541     error(SYSTEM, "out of memory in addmeshtri");
542     return(OVOID);
543     }
544    
545    
546 greg 2.4 char *
547 greg 2.24 checkmesh(MESH *mp) /* validate mesh data */
548 greg 2.4 {
549     static char embuf[128];
550     int nouvbounds = 1;
551 greg 2.35 int i, j;
552 greg 2.4 /* 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 greg 2.5 nouvbounds = (mp->uvlim[1][0] - mp->uvlim[0][0] <= FTINY ||
566     mp->uvlim[1][1] - mp->uvlim[0][1] <= FTINY);
567 greg 2.4 }
568     /* check octree */
569     if (mp->ldflags & IO_TREE) {
570     if (isempty(mp->mcube.cutree))
571     error(WARNING, "empty mesh octree");
572     }
573 greg 2.35 /* check patch data */
574 greg 2.4 if (mp->ldflags & IO_SCENE) {
575 greg 2.35 MESHVERT mv;
576 greg 2.4 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 greg 2.33 if (mp->nmats > 0) /* allocate during preload_objs()! */
581     getmeshpseudo(mp, mp->mat0);
582 greg 2.4 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 greg 2.24 MESHPATCH *pp = &mp->patch[i];
595 greg 2.4 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 greg 2.35 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 greg 2.4 }
653     }
654 greg 2.35 return(NULL); /* seems to be self-consistent */
655 greg 2.4 }
656    
657    
658 greg 2.1 static void
659 greg 2.24 tallyoctree( /* tally octree size */
660     OCTREE ot,
661     int *ecp,
662     int *lcp,
663     int *ocp
664     )
665 greg 2.1 {
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 greg 2.24 printmeshstats( /* print out mesh statistics */
686     MESH *ms,
687     FILE *fp
688     )
689 greg 2.1 {
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 greg 2.24 MESHPATCH *pp = &ms->patch[i];
699 greg 2.1 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 greg 2.22 fprintf(fp, "\t%ld materials\n", (long)ms->nmats);
718 greg 2.1 fprintf(fp, "\t%d patches (%.2f MBytes)\n", ms->npatches,
719     (ms->npatches*sizeof(MESHPATCH) +
720 greg 2.8 vcnt*3*sizeof(uint32) +
721     nscnt*sizeof(int32) +
722 greg 2.16 uvscnt*2*sizeof(uint32) +
723 greg 2.1 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 greg 2.24 freemesh(MESH *ms) /* free mesh data */
741 greg 2.1 {
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 greg 2.4 lu_done(&ms->lut);
767 greg 2.1 if (ms->npatches > 0) {
768 greg 2.24 MESHPATCH *pp = ms->patch + ms->npatches;
769 greg 2.1 while (pp-- > ms->patch) {
770     if (pp->j2tri != NULL)
771 greg 2.31 free(pp->j2tri);
772 greg 2.1 if (pp->j1tri != NULL)
773 greg 2.31 free(pp->j1tri);
774 greg 2.1 if (pp->tri != NULL)
775 greg 2.31 free(pp->tri);
776 greg 2.1 if (pp->uv != NULL)
777 greg 2.31 free(pp->uv);
778 greg 2.1 if (pp->norm != NULL)
779 greg 2.31 free(pp->norm);
780 greg 2.1 if (pp->xyz != NULL)
781 greg 2.31 free(pp->xyz);
782     if (pp->trimat != NULL)
783     free(pp->trimat);
784 greg 2.1 }
785 greg 2.31 free(ms->patch);
786 greg 2.1 }
787 greg 2.4 if (ms->pseudo != NULL)
788 greg 2.31 free(ms->pseudo);
789     free(ms);
790 greg 2.1 }
791    
792    
793     void
794 greg 2.24 freemeshinst(OBJREC *o) /* free mesh instance */
795 greg 2.1 {
796     if (o->os == NULL)
797     return;
798     freemesh((*(MESHINST *)o->os).msh);
799 greg 2.31 free(o->os);
800 greg 2.1 o->os = NULL;
801     }