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 |
|
|
} |