| 1 | 
#ifndef lint | 
| 2 | 
static const char RCSid[] = "$Id: objutil.c,v 2.21 2022/01/15 02:00:21 greg Exp $"; | 
| 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 <ctype.h> | 
| 12 | 
#include "rtio.h" | 
| 13 | 
#include "rtmath.h" | 
| 14 | 
#include "rterror.h" | 
| 15 | 
#include "lookup.h" | 
| 16 | 
#include "objutil.h" | 
| 17 | 
 | 
| 18 | 
/* Find an existing name in a list of names */ | 
| 19 | 
int | 
| 20 | 
findName(const char *nm, const char **nmlist, int n) | 
| 21 | 
{ | 
| 22 | 
        int     i; | 
| 23 | 
         | 
| 24 | 
        for (i = n; i-- > 0; ) | 
| 25 | 
                if (!strcmp(nmlist[i], nm)) | 
| 26 | 
                        break; | 
| 27 | 
        return(i); | 
| 28 | 
} | 
| 29 | 
 | 
| 30 | 
/* Clear face selection */ | 
| 31 | 
void | 
| 32 | 
clearSelection(Scene *sc, int set) | 
| 33 | 
{ | 
| 34 | 
        Face    *f; | 
| 35 | 
         | 
| 36 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 37 | 
                if (set) | 
| 38 | 
                        f->flags |= FACE_SELECTED; | 
| 39 | 
                else | 
| 40 | 
                        f->flags &= ~FACE_SELECTED; | 
| 41 | 
} | 
| 42 | 
 | 
| 43 | 
/* Select faces by object name (modifies current) */ | 
| 44 | 
void | 
| 45 | 
selectGroup(Scene *sc, const char *gname, int invert) | 
| 46 | 
{ | 
| 47 | 
        int     gid = getGroupID(sc, gname); | 
| 48 | 
        Face    *f; | 
| 49 | 
 | 
| 50 | 
        if (gid < 0) | 
| 51 | 
                return; | 
| 52 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 53 | 
                if (f->grp == gid) { | 
| 54 | 
                        if (invert) | 
| 55 | 
                                f->flags &= ~FACE_SELECTED; | 
| 56 | 
                        else | 
| 57 | 
                                f->flags |= FACE_SELECTED; | 
| 58 | 
                } | 
| 59 | 
} | 
| 60 | 
 | 
| 61 | 
/* Select faces by material name (modifies current) */ | 
| 62 | 
void | 
| 63 | 
selectMaterial(Scene *sc, const char *mname, int invert) | 
| 64 | 
{ | 
| 65 | 
        int     mid = getMaterialID(sc, mname); | 
| 66 | 
        Face    *f; | 
| 67 | 
         | 
| 68 | 
        if (mid < 0) | 
| 69 | 
                return; | 
| 70 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 71 | 
                if (f->mat == mid) { | 
| 72 | 
                        if (invert) | 
| 73 | 
                                f->flags &= ~FACE_SELECTED; | 
| 74 | 
                        else | 
| 75 | 
                                f->flags |= FACE_SELECTED; | 
| 76 | 
                } | 
| 77 | 
} | 
| 78 | 
 | 
| 79 | 
/* Invert face selection */ | 
| 80 | 
void | 
| 81 | 
invertSelection(Scene *sc) | 
| 82 | 
{ | 
| 83 | 
        Face    *f; | 
| 84 | 
         | 
| 85 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 86 | 
                f->flags ^= FACE_SELECTED; | 
| 87 | 
} | 
| 88 | 
 | 
| 89 | 
/* Count selected faces */ | 
| 90 | 
int | 
| 91 | 
numberSelected(Scene *sc) | 
| 92 | 
{ | 
| 93 | 
        int     nsel = 0; | 
| 94 | 
        Face    *f; | 
| 95 | 
         | 
| 96 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 97 | 
                nsel += ((f->flags & FACE_SELECTED) != 0); | 
| 98 | 
        return(nsel); | 
| 99 | 
} | 
| 100 | 
 | 
| 101 | 
/* Execute callback on indicated faces */ | 
| 102 | 
int | 
| 103 | 
foreachFace(Scene *sc, int (*cb)(Scene *, Face *, void *), | 
| 104 | 
                        int flreq, int flexc, void *c_data) | 
| 105 | 
{ | 
| 106 | 
        const int       fltest = flreq | flexc; | 
| 107 | 
        int             sum = 0; | 
| 108 | 
        Face            *f; | 
| 109 | 
         | 
| 110 | 
        if ((sc == NULL) | (cb == NULL)) | 
| 111 | 
                return(0); | 
| 112 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 113 | 
                if ((f->flags & fltest) == flreq) { | 
| 114 | 
                        int     res = (*cb)(sc, f, c_data); | 
| 115 | 
                        if (res < 0) | 
| 116 | 
                                return(res); | 
| 117 | 
                        sum += res; | 
| 118 | 
                } | 
| 119 | 
        return(sum); | 
| 120 | 
} | 
| 121 | 
 | 
| 122 | 
/* Callback for removeTexture() */ | 
| 123 | 
static int | 
| 124 | 
remFaceTexture(Scene *sc, Face *f, void *dummy) | 
| 125 | 
{ | 
| 126 | 
        int     hadTexture = 0; | 
| 127 | 
        int     i; | 
| 128 | 
         | 
| 129 | 
        for (i = f->nv; i-- > 0; ) { | 
| 130 | 
                if (f->v[i].tid < 0) | 
| 131 | 
                        continue; | 
| 132 | 
                f->v[i].tid = -1; | 
| 133 | 
                hadTexture = 1; | 
| 134 | 
        } | 
| 135 | 
        return(hadTexture); | 
| 136 | 
} | 
| 137 | 
 | 
| 138 | 
/* Remove texture coordinates from the indicated faces */ | 
| 139 | 
int | 
| 140 | 
removeTexture(Scene *sc, int flreq, int flexc) | 
| 141 | 
{ | 
| 142 | 
        return(foreachFace(sc, remFaceTexture, flreq, flexc, NULL)); | 
| 143 | 
} | 
| 144 | 
 | 
| 145 | 
/* Callback for removeNormals() */ | 
| 146 | 
static int | 
| 147 | 
remFaceNormal(Scene *sc, Face *f, void *dummy) | 
| 148 | 
{ | 
| 149 | 
        int     hadNormal = 0; | 
| 150 | 
        int     i; | 
| 151 | 
         | 
| 152 | 
        for (i = f->nv; i-- > 0; ) { | 
| 153 | 
                if (f->v[i].nid < 0) | 
| 154 | 
                        continue; | 
| 155 | 
                f->v[i].nid = -1; | 
| 156 | 
                hadNormal = 1; | 
| 157 | 
        } | 
| 158 | 
        return(hadNormal); | 
| 159 | 
} | 
| 160 | 
 | 
| 161 | 
/* Remove surface normals from the indicated faces */ | 
| 162 | 
int | 
| 163 | 
removeNormals(Scene *sc, int flreq, int flexc) | 
| 164 | 
{ | 
| 165 | 
        return(foreachFace(sc, remFaceNormal, flreq, flexc, NULL)); | 
| 166 | 
} | 
| 167 | 
 | 
| 168 | 
/* Callback for changeGroup() */ | 
| 169 | 
static int | 
| 170 | 
chngFaceGroup(Scene *sc, Face *f, void *ptr) | 
| 171 | 
{ | 
| 172 | 
        int     grp = *(int *)ptr; | 
| 173 | 
 | 
| 174 | 
        if (f->grp == grp) | 
| 175 | 
                return(0); | 
| 176 | 
        f->grp = grp; | 
| 177 | 
        return(1); | 
| 178 | 
} | 
| 179 | 
 | 
| 180 | 
/* Change group for the indicated faces */ | 
| 181 | 
int | 
| 182 | 
changeGroup(Scene *sc, const char *gname, int flreq, int flexc) | 
| 183 | 
{ | 
| 184 | 
        int     grp = getGroupID(sc, gname); | 
| 185 | 
 | 
| 186 | 
        if (grp < 0) { | 
| 187 | 
                sc->grpname = chunk_alloc(char *, sc->grpname, sc->ngrps); | 
| 188 | 
                sc->grpname[grp=sc->ngrps++] = savqstr((char *)gname); | 
| 189 | 
        } | 
| 190 | 
        return(foreachFace(sc, chngFaceGroup, flreq, flexc, (void *)&grp)); | 
| 191 | 
} | 
| 192 | 
 | 
| 193 | 
/* Callback for changeMaterial() */ | 
| 194 | 
static int | 
| 195 | 
chngFaceMaterial(Scene *sc, Face *f, void *ptr) | 
| 196 | 
{ | 
| 197 | 
        int     mat = *(int *)ptr; | 
| 198 | 
 | 
| 199 | 
        if (f->mat == mat) | 
| 200 | 
                return(0); | 
| 201 | 
        f->mat = mat; | 
| 202 | 
        return(1); | 
| 203 | 
} | 
| 204 | 
 | 
| 205 | 
/* Change material for the indicated faces */ | 
| 206 | 
int | 
| 207 | 
changeMaterial(Scene *sc, const char *mname, int flreq, int flexc) | 
| 208 | 
{ | 
| 209 | 
        int     mat = getMaterialID(sc, mname); | 
| 210 | 
 | 
| 211 | 
        if (mat < 0) { | 
| 212 | 
                sc->matname = chunk_alloc(char *, sc->matname, sc->nmats); | 
| 213 | 
                sc->matname[mat=sc->nmats++] = savqstr((char *)mname); | 
| 214 | 
        } | 
| 215 | 
        return(foreachFace(sc, chngFaceMaterial, flreq, flexc, (void *)&mat)); | 
| 216 | 
} | 
| 217 | 
 | 
| 218 | 
/* Compare floating point values for (near) equality */ | 
| 219 | 
static int | 
| 220 | 
fdiffer(double f1, double f2, double eps) | 
| 221 | 
{ | 
| 222 | 
        if (f2 != .0) | 
| 223 | 
                f1 = f1/f2 - 1.; | 
| 224 | 
        return((f1 > eps) | (f1 < -eps)); | 
| 225 | 
} | 
| 226 | 
 | 
| 227 | 
/* Compare two texture coordinates for (near) equality */ | 
| 228 | 
static int | 
| 229 | 
tex_diff(const TexCoord *t1, const TexCoord *t2, double eps) | 
| 230 | 
{ | 
| 231 | 
        if (fdiffer(t1->u, t2->u, eps)) | 
| 232 | 
                return(1); | 
| 233 | 
        if (fdiffer(t1->v, t2->v, eps)) | 
| 234 | 
                return(1); | 
| 235 | 
        return(0); | 
| 236 | 
} | 
| 237 | 
 | 
| 238 | 
/* Compare two surface normals for (near) equality */ | 
| 239 | 
static int | 
| 240 | 
norm_diff(const Normal n1, const Normal n2, double eps) | 
| 241 | 
{ | 
| 242 | 
        if (fabs(n1[0]-n2[0]) > eps) | 
| 243 | 
                return(1); | 
| 244 | 
        if (fabs(n1[1]-n2[1]) > eps) | 
| 245 | 
                return(1); | 
| 246 | 
        if (fabs(n1[2]-n2[2]) > eps) | 
| 247 | 
                return(1); | 
| 248 | 
        return(0); | 
| 249 | 
} | 
| 250 | 
 | 
| 251 | 
/* Replace the given vertex with an equivalent one */ | 
| 252 | 
static int | 
| 253 | 
replace_vertex(Scene *sc, int prev, int repl, double eps) | 
| 254 | 
{ | 
| 255 | 
        int     repl_tex[10]; | 
| 256 | 
        int     repl_norm[10]; | 
| 257 | 
        Face    *f, *flast=NULL; | 
| 258 | 
        int     i, j=0; | 
| 259 | 
                                        /* check to see if it's even used */ | 
| 260 | 
        if (sc->vert[prev].vflist == NULL) | 
| 261 | 
                return(0); | 
| 262 | 
                                        /* get replacement textures */ | 
| 263 | 
        repl_tex[0] = -1; | 
| 264 | 
        for (f = sc->vert[repl].vflist; f != NULL; f = f->v[j].fnext) { | 
| 265 | 
                                        /* make sure prev isn't in there */ | 
| 266 | 
                for (j = f->nv; j-- > 0; ) | 
| 267 | 
                        if (f->v[j].vid == prev) | 
| 268 | 
                                return(0); | 
| 269 | 
                for (j = f->nv; j-- > 0; ) | 
| 270 | 
                        if (f->v[j].vid == repl) | 
| 271 | 
                                break; | 
| 272 | 
                if (j < 0) | 
| 273 | 
                        goto linkerr; | 
| 274 | 
                if (f->v[j].tid < 0) | 
| 275 | 
                        continue; | 
| 276 | 
                                        /* see if it's new */ | 
| 277 | 
                for (i = 0; repl_tex[i] >= 0; i++) { | 
| 278 | 
                        if (repl_tex[i] == f->v[j].tid) | 
| 279 | 
                                break; | 
| 280 | 
                        if (!tex_diff(&sc->tex[repl_tex[i]], | 
| 281 | 
                                        &sc->tex[f->v[j].tid], eps)) { | 
| 282 | 
                                f->v[j].tid = repl_tex[i]; | 
| 283 | 
                                break;  /* consolidate */ | 
| 284 | 
                        } | 
| 285 | 
                } | 
| 286 | 
                if (repl_tex[i] >= 0) | 
| 287 | 
                        continue;       /* have this one already */ | 
| 288 | 
                                        /* else add it */ | 
| 289 | 
                repl_tex[i++] = f->v[j].tid; | 
| 290 | 
                repl_tex[i] = -1; | 
| 291 | 
                if (i >= 9) | 
| 292 | 
                        break;          /* that's all we have room for */ | 
| 293 | 
        } | 
| 294 | 
                                        /* get replacement normals */ | 
| 295 | 
        repl_norm[0] = -1; | 
| 296 | 
        for (f = sc->vert[repl].vflist; f != NULL; f = f->v[j].fnext) { | 
| 297 | 
                for (j = f->nv; j-- > 0; ) | 
| 298 | 
                        if (f->v[j].vid == repl) | 
| 299 | 
                                break; | 
| 300 | 
                if (f->v[j].nid < 0) | 
| 301 | 
                        continue; | 
| 302 | 
                                        /* see if it's new */ | 
| 303 | 
                for (i = 0; repl_norm[i] >= 0; i++) { | 
| 304 | 
                        if (repl_norm[i] == f->v[j].nid) | 
| 305 | 
                                break; | 
| 306 | 
                        if (!norm_diff(sc->norm[repl_norm[i]], | 
| 307 | 
                                        sc->norm[f->v[j].nid], eps)) { | 
| 308 | 
                                f->v[j].nid = repl_norm[i]; | 
| 309 | 
                                break;  /* consolidate */ | 
| 310 | 
                        } | 
| 311 | 
                } | 
| 312 | 
                if (repl_norm[i] >= 0) | 
| 313 | 
                        continue;       /* have this one already */ | 
| 314 | 
                                        /* else add it */ | 
| 315 | 
                repl_norm[i++] = f->v[j].nid; | 
| 316 | 
                repl_norm[i] = -1; | 
| 317 | 
                if (i >= 9) | 
| 318 | 
                        break;          /* that's all we have room for */ | 
| 319 | 
        } | 
| 320 | 
                                        /* replace occurrences of vertex */ | 
| 321 | 
        for (f = sc->vert[prev].vflist; f != NULL; f = f->v[j].fnext) { | 
| 322 | 
                for (j = f->nv; j-- > 0; ) | 
| 323 | 
                        if (f->v[j].vid == prev) | 
| 324 | 
                                break; | 
| 325 | 
                if (j < 0) | 
| 326 | 
                        goto linkerr; | 
| 327 | 
                /* XXX doesn't allow for multiple references to prev in face */ | 
| 328 | 
                f->v[j].vid = repl;     /* replace vertex itself */ | 
| 329 | 
                if (faceArea(sc, f, NULL) <= FTINY*FTINY) | 
| 330 | 
                        f->flags |= FACE_DEGENERATE; | 
| 331 | 
                if (f->v[j].tid >= 0)   /* replace texture if appropriate */ | 
| 332 | 
                        for (i = 0; repl_tex[i] >= 0; i++) { | 
| 333 | 
                                if (repl_tex[i] == f->v[j].tid) | 
| 334 | 
                                        break; | 
| 335 | 
                                if (!tex_diff(&sc->tex[repl_tex[i]], | 
| 336 | 
                                                &sc->tex[f->v[j].tid], eps)) { | 
| 337 | 
                                        f->v[j].tid = repl_tex[i]; | 
| 338 | 
                                        break; | 
| 339 | 
                                } | 
| 340 | 
                        } | 
| 341 | 
                if (f->v[j].nid >= 0)   /* replace normal if appropriate */ | 
| 342 | 
                        for (i = 0; repl_norm[i] >= 0; i++) { | 
| 343 | 
                                if (repl_norm[i] == f->v[j].nid) | 
| 344 | 
                                        break; | 
| 345 | 
                                if (!norm_diff(sc->norm[repl_norm[i]], | 
| 346 | 
                                                sc->norm[f->v[j].nid], eps)) { | 
| 347 | 
                                        f->v[j].nid = repl_norm[i]; | 
| 348 | 
                                        break; | 
| 349 | 
                                } | 
| 350 | 
                        } | 
| 351 | 
                flast = f; | 
| 352 | 
        } | 
| 353 | 
                                        /* transfer face list */ | 
| 354 | 
        flast->v[j].fnext = sc->vert[repl].vflist; | 
| 355 | 
        sc->vert[repl].vflist = sc->vert[prev].vflist; | 
| 356 | 
        sc->vert[prev].vflist = NULL; | 
| 357 | 
        return(1); | 
| 358 | 
linkerr: | 
| 359 | 
        error(CONSISTENCY, "Link error in replace_vertex()"); | 
| 360 | 
        return(0);                      /* shouldn't return */ | 
| 361 | 
} | 
| 362 | 
 | 
| 363 | 
/* Eliminate duplicate vertices, return # joined */ | 
| 364 | 
int | 
| 365 | 
coalesceVertices(Scene *sc, double eps) | 
| 366 | 
{ | 
| 367 | 
        int     nelim = 0; | 
| 368 | 
        LUTAB   myLookup; | 
| 369 | 
        LUENT   *le; | 
| 370 | 
        char    vertfmt[32], vertbuf[64]; | 
| 371 | 
        double  d; | 
| 372 | 
        int     i; | 
| 373 | 
         | 
| 374 | 
        if (eps >= 1.) | 
| 375 | 
                return(0); | 
| 376 | 
        if (sc->nverts <= 3) | 
| 377 | 
                return(0); | 
| 378 | 
                                        /* create hash table */ | 
| 379 | 
        myLookup.hashf = lu_shash; | 
| 380 | 
        myLookup.keycmp = (lut_keycmpf_t *)strcmp; | 
| 381 | 
        myLookup.freek = (lut_free_t *)freeqstr; | 
| 382 | 
        myLookup.freed = NULL; | 
| 383 | 
        if (!lu_init(&myLookup, sc->nverts)) | 
| 384 | 
                return(0); | 
| 385 | 
        if (eps <= 5e-15) | 
| 386 | 
                strcpy(vertfmt, "%.15e %.15e %.15e"); | 
| 387 | 
        else { | 
| 388 | 
                int     nsigdig = 0; | 
| 389 | 
                for (d = eps; d < 0.5; d *= 10.) | 
| 390 | 
                        ++nsigdig; | 
| 391 | 
                sprintf(vertfmt, "%%.%de %%.%de %%.%de", | 
| 392 | 
                                nsigdig, nsigdig, nsigdig); | 
| 393 | 
        } | 
| 394 | 
                                        /* find coicident vertices */ | 
| 395 | 
        for (i = 0; i < sc->nverts; i++) { | 
| 396 | 
                if (verbose && !((i+1) & 0x3fff)) | 
| 397 | 
                        fprintf(stderr, "%3d%% complete\r", 100*i/sc->nverts); | 
| 398 | 
                                        /* check for match */ | 
| 399 | 
                sprintf(vertbuf, vertfmt, sc->vert[i].p[0], | 
| 400 | 
                                sc->vert[i].p[1], sc->vert[i].p[2]); | 
| 401 | 
                le = lu_find(&myLookup, vertbuf); | 
| 402 | 
                if (le->data != NULL) { /* coincident vertex */ | 
| 403 | 
                        nelim += replace_vertex(sc, i, | 
| 404 | 
                                        (Vertex *)le->data - sc->vert, eps); | 
| 405 | 
                        continue; | 
| 406 | 
                } | 
| 407 | 
                if (le->key == NULL)    /* else create new entry */ | 
| 408 | 
                        le->key = savqstr(vertbuf); | 
| 409 | 
                le->data = (char *)&sc->vert[i]; | 
| 410 | 
        } | 
| 411 | 
        lu_done(&myLookup);             /* clean up */ | 
| 412 | 
        return(nelim); | 
| 413 | 
} | 
| 414 | 
 | 
| 415 | 
/* Identify duplicate faces */ | 
| 416 | 
int | 
| 417 | 
findDuplicateFaces(Scene *sc) | 
| 418 | 
{ | 
| 419 | 
        int     nchecked = 0; | 
| 420 | 
        int     nfound = 0; | 
| 421 | 
        Face    *f, *f1; | 
| 422 | 
        int     vid; | 
| 423 | 
        int     i, j; | 
| 424 | 
                                                /* start fresh */ | 
| 425 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 426 | 
                f->flags &= ~FACE_DUPLICATE; | 
| 427 | 
                                                /* check each face */ | 
| 428 | 
        for (f = sc->flist; f != NULL; f = f->next) { | 
| 429 | 
                nchecked++; | 
| 430 | 
                if (verbose && !(nchecked & 0x3fff)) | 
| 431 | 
                        fprintf(stderr, "%3d%% complete\r", | 
| 432 | 
                                        100*nchecked/sc->nfaces); | 
| 433 | 
                if (f->flags & FACE_DUPLICATE) | 
| 434 | 
                        continue;               /* already identified */ | 
| 435 | 
                vid = f->v[0].vid; | 
| 436 | 
                                                /* look for duplicates */ | 
| 437 | 
                for (f1 = sc->vert[vid].vflist; f1 != NULL; | 
| 438 | 
                                        f1 = f1->v[j].fnext) { | 
| 439 | 
                        for (j = f1->nv; j-- > 0; ) | 
| 440 | 
                                if (f1->v[j].vid == vid) | 
| 441 | 
                                        break; | 
| 442 | 
                        if (j < 0) | 
| 443 | 
                                break;          /* missing link! */ | 
| 444 | 
                        if (f1 == f) | 
| 445 | 
                                continue;       /* shouldn't happen */ | 
| 446 | 
                        if (f1->flags & FACE_DUPLICATE) | 
| 447 | 
                                continue;       /* already marked */ | 
| 448 | 
                        if (f1->nv != f->nv) | 
| 449 | 
                                continue;       /* couldn't be dup. */ | 
| 450 | 
                        for (i = f->nv; --i > 0; ) | 
| 451 | 
                                if (f->v[i].vid != f1->v[(j+i)%f1->nv].vid) | 
| 452 | 
                                        break;  /* vertex mismatch */ | 
| 453 | 
                        if (i) { | 
| 454 | 
#if DUP_CHECK_REVERSE                           /* check reverse direction */ | 
| 455 | 
                                for (i = f->nv; --i > 0; ) | 
| 456 | 
                                        if (f1->v[(j+f1->nv-i)%f1->nv].vid | 
| 457 | 
                                                        != f->v[i].vid) | 
| 458 | 
                                                break; | 
| 459 | 
                                if (i)          /* no match */ | 
| 460 | 
#endif | 
| 461 | 
                                        continue; | 
| 462 | 
                        } | 
| 463 | 
                        f1->flags |= FACE_DUPLICATE; | 
| 464 | 
                        ++nfound; | 
| 465 | 
                } | 
| 466 | 
        } | 
| 467 | 
        if (verbose) | 
| 468 | 
                fprintf(stderr, "Found %d duplicate faces\n", nfound); | 
| 469 | 
        return(nfound); | 
| 470 | 
} | 
| 471 | 
 | 
| 472 | 
/* Delete indicated faces */ | 
| 473 | 
int | 
| 474 | 
deleteFaces(Scene *sc, int flreq, int flexc) | 
| 475 | 
{ | 
| 476 | 
        const int       fltest = flreq | flexc; | 
| 477 | 
        int             orig_nfaces = sc->nfaces; | 
| 478 | 
        Face            fhead; | 
| 479 | 
        Face            *f, *ftst; | 
| 480 | 
         | 
| 481 | 
        fhead.next = sc->flist; | 
| 482 | 
        f = &fhead; | 
| 483 | 
        while ((ftst = f->next) != NULL) | 
| 484 | 
                if ((ftst->flags & fltest) == flreq) { | 
| 485 | 
                        Face    *vf;            /* remove from vertex lists */ | 
| 486 | 
                        int     vid, i, j; | 
| 487 | 
                        for (i = 0; i < ftst->nv; i++) { | 
| 488 | 
                                vid = ftst->v[i].vid; | 
| 489 | 
                                vf = sc->vert[vid].vflist; | 
| 490 | 
                                if (vf == ftst) { | 
| 491 | 
                                        sc->vert[vid].vflist = ftst->v[i].fnext; | 
| 492 | 
                                        continue; | 
| 493 | 
                                } | 
| 494 | 
                                while (vf != NULL) { | 
| 495 | 
                                        for (j = vf->nv; j-- > 0; ) | 
| 496 | 
                                                if (vf->v[j].vid == vid) | 
| 497 | 
                                                        break; | 
| 498 | 
                                        if (j < 0) | 
| 499 | 
                                                break;  /* error */ | 
| 500 | 
                                        if (vf->v[j].fnext == ftst) { | 
| 501 | 
                                                vf->v[j].fnext = | 
| 502 | 
                                                        ftst->v[i].fnext; | 
| 503 | 
                                                break; | 
| 504 | 
                                        } | 
| 505 | 
                                        vf = vf->v[j].fnext; | 
| 506 | 
                                } | 
| 507 | 
                        } | 
| 508 | 
                        f->next = ftst->next;   /* remove from scene list */ | 
| 509 | 
                        efree(ftst); | 
| 510 | 
                        sc->nfaces--; | 
| 511 | 
                } else | 
| 512 | 
                        f = f->next; | 
| 513 | 
        sc->flist = fhead.next; | 
| 514 | 
        return(orig_nfaces - sc->nfaces); | 
| 515 | 
} | 
| 516 | 
 | 
| 517 | 
/* Compute face area (and normal) */ | 
| 518 | 
double | 
| 519 | 
faceArea(const Scene *sc, const Face *f, Normal nrm) | 
| 520 | 
{ | 
| 521 | 
        FVECT   fnrm; | 
| 522 | 
        double  area; | 
| 523 | 
        FVECT   v1, v2, v3; | 
| 524 | 
        double  *p0; | 
| 525 | 
        int     i; | 
| 526 | 
         | 
| 527 | 
        if (f->flags & FACE_DEGENERATE) | 
| 528 | 
                return(.0);                     /* should we check this? */ | 
| 529 | 
        fnrm[0] = fnrm[1] = fnrm[2] = .0; | 
| 530 | 
        p0 = sc->vert[f->v[0].vid].p; | 
| 531 | 
        VSUB(v1, sc->vert[f->v[1].vid].p, p0); | 
| 532 | 
        for (i = 2; i < f->nv; i++) { | 
| 533 | 
                VSUB(v2, sc->vert[f->v[i].vid].p, p0); | 
| 534 | 
                fcross(v3, v1, v2); | 
| 535 | 
                VADD(fnrm, fnrm, v3); | 
| 536 | 
                VCOPY(v1, v2); | 
| 537 | 
        } | 
| 538 | 
        area = 0.5*normalize(fnrm); | 
| 539 | 
        if (nrm != NULL) { | 
| 540 | 
                if (area != 0.) | 
| 541 | 
                        VCOPY(nrm, fnrm); | 
| 542 | 
                else | 
| 543 | 
                        nrm[0] = nrm[1] = nrm[2] = 0.; | 
| 544 | 
        } | 
| 545 | 
        return(area); | 
| 546 | 
} | 
| 547 | 
 | 
| 548 | 
/* Add a descriptive comment */ | 
| 549 | 
void | 
| 550 | 
addComment(Scene *sc, const char *comment) | 
| 551 | 
{ | 
| 552 | 
        sc->descr = chunk_alloc(char *, sc->descr, sc->ndescr); | 
| 553 | 
        sc->descr[sc->ndescr++] = savqstr((char *)comment); | 
| 554 | 
} | 
| 555 | 
 | 
| 556 | 
/* Find index for comment containing the given string (starting from n) */ | 
| 557 | 
int | 
| 558 | 
findComment(Scene *sc, const char *match, int n) | 
| 559 | 
{ | 
| 560 | 
        if (n >= sc->ndescr) | 
| 561 | 
                return(-1); | 
| 562 | 
        n *= (n > 0); | 
| 563 | 
        while (n < sc->ndescr) | 
| 564 | 
                if (strstr(sc->descr[n], match) != NULL) | 
| 565 | 
                        return(n); | 
| 566 | 
        return(-1); | 
| 567 | 
} | 
| 568 | 
 | 
| 569 | 
/* Clear comments */ | 
| 570 | 
void | 
| 571 | 
clearComments(Scene *sc) | 
| 572 | 
{ | 
| 573 | 
        while (sc->ndescr > 0) | 
| 574 | 
                freeqstr(sc->descr[--sc->ndescr]); | 
| 575 | 
        efree(sc->descr); | 
| 576 | 
        sc->descr = NULL; | 
| 577 | 
        sc->ndescr = 0; | 
| 578 | 
} | 
| 579 | 
 | 
| 580 | 
/* Add a face to a vertex face list */ | 
| 581 | 
static void | 
| 582 | 
add2facelist(Scene *sc, Face *f, int i) | 
| 583 | 
{ | 
| 584 | 
        int     vid = f->v[i].vid; | 
| 585 | 
        Face    *fp = sc->vert[vid].vflist; | 
| 586 | 
        int     j; | 
| 587 | 
         | 
| 588 | 
        f->v[i].fnext = NULL;                   /* will put at end */ | 
| 589 | 
        if (fp == NULL) {                       /* new list */ | 
| 590 | 
                sc->vert[vid].vflist = f; | 
| 591 | 
                return; | 
| 592 | 
        } | 
| 593 | 
        for ( ; ; ) {                           /* else find position */ | 
| 594 | 
                if (fp == f) | 
| 595 | 
                        return;                 /* already in list */ | 
| 596 | 
                for (j = fp->nv; j-- > 0; ) | 
| 597 | 
                        if (fp->v[j].vid == vid) | 
| 598 | 
                                break; | 
| 599 | 
                if (j < 0) | 
| 600 | 
                        error(CONSISTENCY, "Link error in add2facelist()"); | 
| 601 | 
                if (fp->v[j].fnext == NULL) | 
| 602 | 
                        break;                  /* reached the end */ | 
| 603 | 
                fp = fp->v[j].fnext; | 
| 604 | 
        } | 
| 605 | 
        fp->v[j].fnext = f;                     /* append new face */ | 
| 606 | 
} | 
| 607 | 
 | 
| 608 | 
/* Add a vertex to a scene */ | 
| 609 | 
int | 
| 610 | 
addVertex(Scene *sc, double x, double y, double z) | 
| 611 | 
{ | 
| 612 | 
        sc->vert = chunk_alloc(Vertex, sc->vert, sc->nverts); | 
| 613 | 
        sc->vert[sc->nverts].p[0] = x; | 
| 614 | 
        sc->vert[sc->nverts].p[1] = y; | 
| 615 | 
        sc->vert[sc->nverts].p[2] = z; | 
| 616 | 
        sc->vert[sc->nverts].vflist = NULL; | 
| 617 | 
        return(sc->nverts++); | 
| 618 | 
} | 
| 619 | 
 | 
| 620 | 
/* Add a texture coordinate to a scene */ | 
| 621 | 
int | 
| 622 | 
addTexture(Scene *sc, double u, double v) | 
| 623 | 
{ | 
| 624 | 
        sc->tex = chunk_alloc(TexCoord, sc->tex, sc->ntex); | 
| 625 | 
        sc->tex[sc->ntex].u = u; | 
| 626 | 
        sc->tex[sc->ntex].v = v; | 
| 627 | 
        return(sc->ntex++); | 
| 628 | 
} | 
| 629 | 
 | 
| 630 | 
/* Add a surface normal to a scene */ | 
| 631 | 
int | 
| 632 | 
addNormal(Scene *sc, double xn, double yn, double zn) | 
| 633 | 
{ | 
| 634 | 
        FVECT   nrm; | 
| 635 | 
 | 
| 636 | 
        nrm[0] = xn; nrm[1] = yn; nrm[2] = zn; | 
| 637 | 
        if (normalize(nrm) == .0) | 
| 638 | 
                return(-1); | 
| 639 | 
        sc->norm = chunk_alloc(Normal, sc->norm, sc->nnorms); | 
| 640 | 
        VCOPY(sc->norm[sc->nnorms], nrm); | 
| 641 | 
        return(sc->nnorms++); | 
| 642 | 
} | 
| 643 | 
 | 
| 644 | 
/* Set current (last) group */ | 
| 645 | 
void | 
| 646 | 
setGroup(Scene *sc, const char *nm) | 
| 647 | 
{ | 
| 648 | 
        sc->lastgrp = getGroupID(sc, nm); | 
| 649 | 
        if (sc->lastgrp >= 0) | 
| 650 | 
                return; | 
| 651 | 
        sc->grpname = chunk_alloc(char *, sc->grpname, sc->ngrps); | 
| 652 | 
        sc->grpname[sc->lastgrp=sc->ngrps++] = savqstr((char *)nm); | 
| 653 | 
} | 
| 654 | 
 | 
| 655 | 
/* Set current (last) material */ | 
| 656 | 
void | 
| 657 | 
setMaterial(Scene *sc, const char *nm) | 
| 658 | 
{ | 
| 659 | 
        sc->lastmat = getMaterialID(sc, nm); | 
| 660 | 
        if (sc->lastmat >= 0) | 
| 661 | 
                return; | 
| 662 | 
        sc->matname = chunk_alloc(char *, sc->matname, sc->nmats); | 
| 663 | 
        sc->matname[sc->lastmat=sc->nmats++] = savqstr((char *)nm); | 
| 664 | 
} | 
| 665 | 
 | 
| 666 | 
/* Add new face to a scene */ | 
| 667 | 
Face * | 
| 668 | 
addFace(Scene *sc, VNDX vid[], int nv) | 
| 669 | 
{ | 
| 670 | 
        Face    *f; | 
| 671 | 
        int     i; | 
| 672 | 
         | 
| 673 | 
        if (nv < 3) | 
| 674 | 
                return(NULL); | 
| 675 | 
        f = (Face *)emalloc(sizeof(Face)+sizeof(VertEnt)*(nv-3)); | 
| 676 | 
        f->flags = 0; | 
| 677 | 
        f->nv = nv; | 
| 678 | 
        f->grp = sc->lastgrp; | 
| 679 | 
        f->mat = sc->lastmat; | 
| 680 | 
        for (i = 0; i < nv; i++) {              /* add each vertex */ | 
| 681 | 
                int     j; | 
| 682 | 
                f->v[i].vid = vid[i][0]; | 
| 683 | 
                f->v[i].tid = vid[i][1]; | 
| 684 | 
                f->v[i].nid = vid[i][2]; | 
| 685 | 
                f->v[i].fnext = NULL; | 
| 686 | 
                for (j = i; j-- > 0; ) | 
| 687 | 
                        if (f->v[j].vid == vid[i][0]) | 
| 688 | 
                                break; | 
| 689 | 
                if (j < 0) {                    /* first occurrence? */ | 
| 690 | 
                        f->v[i].fnext = sc->vert[vid[i][0]].vflist; | 
| 691 | 
                        sc->vert[vid[i][0]].vflist = f; | 
| 692 | 
                } else if (nv == 3)             /* degenerate triangle? */ | 
| 693 | 
                        f->flags |= FACE_DEGENERATE; | 
| 694 | 
        } | 
| 695 | 
        f->next = sc->flist;                    /* push onto face list */ | 
| 696 | 
        sc->flist = f; | 
| 697 | 
        sc->nfaces++; | 
| 698 | 
                                                /* check face area */ | 
| 699 | 
        if (!(f->flags & FACE_DEGENERATE) && faceArea(sc, f, NULL) <= FTINY*FTINY) | 
| 700 | 
                f->flags |= FACE_DEGENERATE; | 
| 701 | 
        return(f); | 
| 702 | 
} | 
| 703 | 
 | 
| 704 | 
/* Get neighbor vertices: malloc array with valence in index[0] */ | 
| 705 | 
int * | 
| 706 | 
getVertexNeighbors(Scene *sc, int vid) | 
| 707 | 
{ | 
| 708 | 
        int     *varr; | 
| 709 | 
        Face    *f; | 
| 710 | 
        int     j=0; | 
| 711 | 
 | 
| 712 | 
        if (sc == NULL || (vid < 0) | (vid >= sc->nverts) || | 
| 713 | 
                        (f = sc->vert[vid].vflist) == NULL) | 
| 714 | 
                return(NULL); | 
| 715 | 
 | 
| 716 | 
        varr = (int *)emalloc(sizeof(int)*CHUNKSIZ); | 
| 717 | 
        varr[0] = 0;                            /* add unique neighbors */ | 
| 718 | 
        for ( ; f != NULL; f = f->v[j].fnext) { | 
| 719 | 
                int     i, cvi; | 
| 720 | 
                for (j = f->nv; j--; )          /* find ourself in poly */ | 
| 721 | 
                        if (f->v[j].vid == vid) | 
| 722 | 
                                break; | 
| 723 | 
                if (j < 0)                      /* this is an error */ | 
| 724 | 
                        break; | 
| 725 | 
                if (f->nv < 3)                  /* also never happens */ | 
| 726 | 
                        continue; | 
| 727 | 
                                                /* check previous neighbor */ | 
| 728 | 
                cvi = f->v[ (j > 0) ? j-1 : f->nv-1 ].vid; | 
| 729 | 
                for (i = varr[0]+1; --i; )      /* making sure not in list */ | 
| 730 | 
                        if (varr[i] == cvi) | 
| 731 | 
                                break; | 
| 732 | 
                if (!i) {                       /* new neighbor? */ | 
| 733 | 
                        varr = chunk_alloc(int, varr, varr[0]+1); | 
| 734 | 
                        varr[++varr[0]] = cvi; | 
| 735 | 
                } | 
| 736 | 
                                                /* check next neighbor */ | 
| 737 | 
                cvi = f->v[ (j < f->nv-1) ? j+1 : 0 ].vid; | 
| 738 | 
                for (i = varr[0]+1; --i; ) | 
| 739 | 
                        if (varr[i] == cvi) | 
| 740 | 
                                break; | 
| 741 | 
                if (!i) { | 
| 742 | 
                        varr = chunk_alloc(int, varr, varr[0]+1); | 
| 743 | 
                        varr[++varr[0]] = cvi; | 
| 744 | 
                } | 
| 745 | 
        } | 
| 746 | 
        if (!varr[0]) { | 
| 747 | 
                efree(varr);                    /* something went awry */ | 
| 748 | 
                return(NULL); | 
| 749 | 
        } | 
| 750 | 
                                                /* tighten array & return */ | 
| 751 | 
        return((int *)erealloc(varr, sizeof(int)*(varr[0]+1))); | 
| 752 | 
} | 
| 753 | 
 | 
| 754 | 
/* Callback for growBoundingBox() */ | 
| 755 | 
static int | 
| 756 | 
addBBox(Scene *sc, Face *f, void *p) | 
| 757 | 
{ | 
| 758 | 
        double  (*bbox)[3] = (double (*)[3])p; | 
| 759 | 
        int     i, j; | 
| 760 | 
 | 
| 761 | 
        for (i = f->nv; i-- > 0; ) { | 
| 762 | 
                double  *p3 = sc->vert[f->v[i].vid].p; | 
| 763 | 
                for (j = 3; j--; ) { | 
| 764 | 
                        if (p3[j] < bbox[0][j]) | 
| 765 | 
                                bbox[0][j] = p3[j]; | 
| 766 | 
                        if (p3[j] > bbox[1][j]) | 
| 767 | 
                                bbox[1][j] = p3[j]; | 
| 768 | 
                } | 
| 769 | 
        } | 
| 770 | 
        return(1); | 
| 771 | 
} | 
| 772 | 
 | 
| 773 | 
/* Expand bounding box min & max (initialize bbox to all zeroes) */ | 
| 774 | 
int | 
| 775 | 
growBoundingBox(Scene *sc, double bbox[2][3], int flreq, int flexc) | 
| 776 | 
{ | 
| 777 | 
        if (sc == NULL || sc->nfaces <= 0 || bbox == NULL) | 
| 778 | 
                return(0); | 
| 779 | 
 | 
| 780 | 
        if (VABSEQ(bbox[0], bbox[1])) {         /* first run */ | 
| 781 | 
                bbox[0][0] = bbox[0][1] = bbox[0][2] = FHUGE; | 
| 782 | 
                bbox[1][0] = bbox[1][1] = bbox[1][2] = -FHUGE; | 
| 783 | 
        } | 
| 784 | 
        return(foreachFace(sc, addBBox, flreq, flexc, bbox)); | 
| 785 | 
} | 
| 786 | 
 | 
| 787 | 
/* Allocate an empty scene */ | 
| 788 | 
Scene * | 
| 789 | 
newScene(void) | 
| 790 | 
{ | 
| 791 | 
        Scene   *sc = (Scene *)ecalloc(1, sizeof(Scene)); | 
| 792 | 
                                                /* default group & material */ | 
| 793 | 
        sc->grpname = chunk_alloc(char *, sc->grpname, sc->ngrps); | 
| 794 | 
        sc->grpname[sc->ngrps++] = savqstr("DEFAULT_GROUP"); | 
| 795 | 
        sc->matname = chunk_alloc(char *, sc->matname, sc->nmats); | 
| 796 | 
        sc->matname[sc->nmats++] = savqstr("DEFAULT_MATERIAL"); | 
| 797 | 
 | 
| 798 | 
        return(sc); | 
| 799 | 
} | 
| 800 | 
 | 
| 801 | 
/* Duplicate a scene, optionally selecting faces */ | 
| 802 | 
Scene * | 
| 803 | 
dupScene(const Scene *osc, int flreq, int flexc) | 
| 804 | 
{ | 
| 805 | 
        int             fltest = flreq | flexc; | 
| 806 | 
        Scene           *sc; | 
| 807 | 
        const Face      *fo; | 
| 808 | 
        Face            *f; | 
| 809 | 
        int             i; | 
| 810 | 
 | 
| 811 | 
        if (osc == NULL) | 
| 812 | 
                return(NULL); | 
| 813 | 
        sc = newScene(); | 
| 814 | 
        for (i = 0; i < osc->ndescr; i++) | 
| 815 | 
                addComment(sc, osc->descr[i]); | 
| 816 | 
        if (osc->ngrps > 1) { | 
| 817 | 
                sc->grpname = (char **)erealloc(sc->grpname, | 
| 818 | 
                                sizeof(char *) * (osc->ngrps+(CHUNKSIZ-1))); | 
| 819 | 
                for (i = 1; i < osc->ngrps; i++) | 
| 820 | 
                        sc->grpname[i] = savqstr(osc->grpname[i]); | 
| 821 | 
                sc->ngrps = osc->ngrps; | 
| 822 | 
        } | 
| 823 | 
        if (osc->nmats > 1) { | 
| 824 | 
                sc->matname = (char **)erealloc(sc->matname, | 
| 825 | 
                                sizeof(char *) * (osc->nmats+(CHUNKSIZ-1))); | 
| 826 | 
                for (i = 1; i < osc->nmats; i++) | 
| 827 | 
                        sc->matname[i] = savqstr(osc->matname[i]); | 
| 828 | 
                sc->nmats = osc->nmats; | 
| 829 | 
        } | 
| 830 | 
        if (osc->nverts) { | 
| 831 | 
                sc->vert = (Vertex *)emalloc(sizeof(Vertex) * | 
| 832 | 
                                                (osc->nverts+(CHUNKSIZ-1))); | 
| 833 | 
                memcpy(sc->vert, osc->vert, sizeof(Vertex)*osc->nverts); | 
| 834 | 
                sc->nverts = osc->nverts; | 
| 835 | 
                for (i = 0; i < sc->nverts; i++) | 
| 836 | 
                        sc->vert[i].vflist = NULL; | 
| 837 | 
        } | 
| 838 | 
        if (osc->ntex) { | 
| 839 | 
                sc->tex = (TexCoord *)emalloc(sizeof(TexCoord) * | 
| 840 | 
                                                (osc->ntex+(CHUNKSIZ-1))); | 
| 841 | 
                memcpy(sc->tex, osc->tex, sizeof(TexCoord)*osc->ntex); | 
| 842 | 
                sc->ntex = osc->ntex; | 
| 843 | 
        } | 
| 844 | 
        if (osc->nnorms) { | 
| 845 | 
                sc->norm = (Normal *)emalloc(sizeof(Normal) * | 
| 846 | 
                                                (osc->nnorms+CHUNKSIZ)); | 
| 847 | 
                memcpy(sc->norm, osc->norm, sizeof(Normal)*osc->nnorms); | 
| 848 | 
                sc->nnorms = osc->nnorms; | 
| 849 | 
        } | 
| 850 | 
        for (fo = osc->flist; fo != NULL; fo = fo->next) { | 
| 851 | 
                if ((fo->flags & fltest) != flreq) | 
| 852 | 
                        continue; | 
| 853 | 
                f = (Face *)emalloc(sizeof(Face) + sizeof(VertEnt)*(fo->nv-3)); | 
| 854 | 
                memcpy(f, fo, sizeof(Face) + sizeof(VertEnt)*(fo->nv-3)); | 
| 855 | 
                for (i = 0; i < f->nv; i++) | 
| 856 | 
                        add2facelist(sc, f, i); | 
| 857 | 
                f->next = sc->flist; | 
| 858 | 
                sc->flist = f; | 
| 859 | 
                sc->nfaces++; | 
| 860 | 
        } | 
| 861 | 
        deleteUnreferenced(sc);         /* jetsam */ | 
| 862 | 
        return(sc); | 
| 863 | 
} | 
| 864 | 
 | 
| 865 | 
/* Add one scene to another, not checking for redundancies */ | 
| 866 | 
int | 
| 867 | 
addScene(Scene *scdst, const Scene *scsrc) | 
| 868 | 
{ | 
| 869 | 
        VNDX            my_vlist[4]; | 
| 870 | 
        int             *vert_map = NULL; | 
| 871 | 
        int             tex_off = 0; | 
| 872 | 
        int             norm_off = 0; | 
| 873 | 
        VNDX            *vlist = my_vlist; | 
| 874 | 
        int             vllen = sizeof(my_vlist)/sizeof(VNDX); | 
| 875 | 
        int             cur_mat = 0; | 
| 876 | 
        int             cur_grp = 0; | 
| 877 | 
        int             fcnt = 0; | 
| 878 | 
        const Face      *f; | 
| 879 | 
        int             i; | 
| 880 | 
 | 
| 881 | 
        if ((scdst == NULL) | (scsrc == NULL)) | 
| 882 | 
                return(-1); | 
| 883 | 
        if (scsrc->nfaces <= 0) | 
| 884 | 
                return(0); | 
| 885 | 
                                        /* map vertices */ | 
| 886 | 
        vert_map = (int *)emalloc(sizeof(int)*scsrc->nverts); | 
| 887 | 
        for (i = 0; i < scsrc->nverts; i++) { | 
| 888 | 
                const Vertex    *v = scsrc->vert + i; | 
| 889 | 
                if (v->vflist == NULL) { | 
| 890 | 
                        vert_map[i] = -1; | 
| 891 | 
                        continue; | 
| 892 | 
                } | 
| 893 | 
                vert_map[i] = addVertex(scdst, v->p[0], v->p[1], v->p[2]); | 
| 894 | 
        } | 
| 895 | 
        tex_off = scdst->ntex;          /* append texture coords */ | 
| 896 | 
        if (scsrc->ntex > 0) { | 
| 897 | 
                scdst->tex = (TexCoord *)erealloc(scdst->tex, | 
| 898 | 
                        sizeof(TexCoord)*(tex_off+scsrc->ntex+(CHUNKSIZ-1))); | 
| 899 | 
                memcpy(scdst->tex+tex_off, scsrc->tex, | 
| 900 | 
                                sizeof(TexCoord)*scsrc->ntex); | 
| 901 | 
        } | 
| 902 | 
        norm_off = scdst->nnorms;       /* append normals */ | 
| 903 | 
        if (scsrc->nnorms > 0) { | 
| 904 | 
                scdst->norm = (Normal *)erealloc(scdst->norm, | 
| 905 | 
                        sizeof(Normal)*(norm_off+scsrc->nnorms+(CHUNKSIZ-1))); | 
| 906 | 
                memcpy(scdst->norm+norm_off, scsrc->norm, | 
| 907 | 
                                sizeof(Normal)*scsrc->nnorms); | 
| 908 | 
        } | 
| 909 | 
                                        /* add faces */ | 
| 910 | 
        scdst->lastgrp = scdst->lastmat = 0; | 
| 911 | 
        for (f = scsrc->flist; f != NULL; f = f->next) { | 
| 912 | 
                if (f->grp != cur_grp) | 
| 913 | 
                        setGroup(scdst, scsrc->grpname[cur_grp = f->grp]); | 
| 914 | 
                if (f->mat != cur_mat) | 
| 915 | 
                        setMaterial(scdst, scsrc->matname[cur_mat = f->mat]); | 
| 916 | 
                if (f->nv > vllen) { | 
| 917 | 
                        vlist = (VNDX *)( vlist == my_vlist ? | 
| 918 | 
                                emalloc(sizeof(VNDX)*f->nv) : | 
| 919 | 
                                erealloc(vlist, sizeof(VNDX)*f->nv) ); | 
| 920 | 
                        vllen = f->nv; | 
| 921 | 
                } | 
| 922 | 
                memset(vlist, 0xff, sizeof(VNDX)*f->nv); | 
| 923 | 
                for (i = f->nv; i-- > 0; ) { | 
| 924 | 
                        if (f->v[i].vid >= 0) | 
| 925 | 
                                vlist[i][0] = vert_map[f->v[i].vid]; | 
| 926 | 
                        if (f->v[i].tid >= 0) | 
| 927 | 
                                vlist[i][1] = f->v[i].tid + tex_off; | 
| 928 | 
                        if (f->v[i].nid >= 0) | 
| 929 | 
                                vlist[i][2] = f->v[i].nid + norm_off; | 
| 930 | 
                } | 
| 931 | 
                fcnt += (addFace(scdst, vlist, f->nv) != NULL); | 
| 932 | 
        } | 
| 933 | 
                                        /* clean up */ | 
| 934 | 
        if (vlist != my_vlist) efree(vlist); | 
| 935 | 
        efree(vert_map); | 
| 936 | 
        return(fcnt); | 
| 937 | 
} | 
| 938 | 
 | 
| 939 | 
#define MAXAC   100 | 
| 940 | 
 | 
| 941 | 
/* Transform entire scene */ | 
| 942 | 
int | 
| 943 | 
xfScene(Scene *sc, int xac, char *xav[]) | 
| 944 | 
{ | 
| 945 | 
        char    comm[24+MAXAC*8]; | 
| 946 | 
        char    *cp; | 
| 947 | 
        XF      myxf; | 
| 948 | 
        FVECT   vec; | 
| 949 | 
        int     i; | 
| 950 | 
 | 
| 951 | 
        if ((sc == NULL) | (xac <= 0) | (xav == NULL)) | 
| 952 | 
                return(0); | 
| 953 | 
                                        /* compute matrix */ | 
| 954 | 
        if (xf(&myxf, xac, xav) < xac) | 
| 955 | 
                return(0); | 
| 956 | 
                                        /* transform vertices */ | 
| 957 | 
        for (i = 0; i < sc->nverts; i++) { | 
| 958 | 
                VCOPY(vec, sc->vert[i].p); | 
| 959 | 
                multp3(vec, vec, myxf.xfm); | 
| 960 | 
                VCOPY(sc->vert[i].p, vec); | 
| 961 | 
        } | 
| 962 | 
                                        /* transform normals */ | 
| 963 | 
        for (i = 0; i < sc->nnorms; i++) { | 
| 964 | 
                VCOPY(vec, sc->norm[i]); | 
| 965 | 
                multv3(vec, vec, myxf.xfm); | 
| 966 | 
                vec[0] /= myxf.sca; vec[1] /= myxf.sca; vec[2] /= myxf.sca; | 
| 967 | 
                VCOPY(sc->norm[i], vec); | 
| 968 | 
        } | 
| 969 | 
                                        /* add comment */ | 
| 970 | 
        cp = strcpy(comm, "Transformed by:"); | 
| 971 | 
        for (i = 0; i < xac; i++) { | 
| 972 | 
                while (*cp) cp++; | 
| 973 | 
                *cp++ = ' '; | 
| 974 | 
                strcpy(cp, xav[i]); | 
| 975 | 
        } | 
| 976 | 
        addComment(sc, comm); | 
| 977 | 
        return(xac);                    /* all done */ | 
| 978 | 
} | 
| 979 | 
 | 
| 980 | 
/* Ditto, using transform string rather than pre-parsed words */ | 
| 981 | 
int | 
| 982 | 
xfmScene(Scene *sc, const char *xfm) | 
| 983 | 
{ | 
| 984 | 
        char    *xav[MAXAC+1]; | 
| 985 | 
        int     xac, i; | 
| 986 | 
 | 
| 987 | 
        if ((sc == NULL) | (xfm == NULL)) | 
| 988 | 
                return(0); | 
| 989 | 
                                        /* skip spaces at beginning */ | 
| 990 | 
        while (isspace(*xfm)) | 
| 991 | 
                xfm++; | 
| 992 | 
        if (!*xfm) | 
| 993 | 
                return(0); | 
| 994 | 
                                        /* parse string into words */ | 
| 995 | 
        xav[0] = savqstr((char *)xfm); | 
| 996 | 
        xac = 1; i = 0; | 
| 997 | 
        for ( ; ; ) { | 
| 998 | 
                while (!isspace(xfm[++i])) | 
| 999 | 
                        if (!xfm[i]) | 
| 1000 | 
                                break; | 
| 1001 | 
                while (isspace(xfm[i])) | 
| 1002 | 
                        xav[0][i++] = '\0'; | 
| 1003 | 
                if (!xfm[i]) | 
| 1004 | 
                        break; | 
| 1005 | 
                if (xac >= MAXAC-1) { | 
| 1006 | 
                        freeqstr(xav[0]); | 
| 1007 | 
                        return(0); | 
| 1008 | 
                } | 
| 1009 | 
                xav[xac++] = xav[0] + i; | 
| 1010 | 
        } | 
| 1011 | 
        xav[xac] = NULL; | 
| 1012 | 
        i = xfScene(sc, xac, xav); | 
| 1013 | 
        freeqstr(xav[0]); | 
| 1014 | 
        return(i); | 
| 1015 | 
} | 
| 1016 | 
#undef MAXAC | 
| 1017 | 
 | 
| 1018 | 
/* Delete unreferenced vertices, normals, texture coords */ | 
| 1019 | 
void | 
| 1020 | 
deleteUnreferenced(Scene *sc) | 
| 1021 | 
{ | 
| 1022 | 
        int     *vmap; | 
| 1023 | 
        Face    *f; | 
| 1024 | 
        int     nused, i; | 
| 1025 | 
                                                /* allocate index map */ | 
| 1026 | 
        if (!sc->nverts) | 
| 1027 | 
                return; | 
| 1028 | 
        i = sc->nverts; | 
| 1029 | 
        if (sc->ntex > i) | 
| 1030 | 
                i = sc->ntex; | 
| 1031 | 
        if (sc->nnorms > i) | 
| 1032 | 
                i = sc->nnorms; | 
| 1033 | 
        vmap = (int *)emalloc(sizeof(int)*i); | 
| 1034 | 
                                                /* remap positions */ | 
| 1035 | 
        for (i = nused = 0; i < sc->nverts; i++) { | 
| 1036 | 
                if (sc->vert[i].vflist == NULL) { | 
| 1037 | 
                        vmap[i] = -1; | 
| 1038 | 
                        continue; | 
| 1039 | 
                } | 
| 1040 | 
                if (nused != i) | 
| 1041 | 
                        sc->vert[nused] = sc->vert[i]; | 
| 1042 | 
                vmap[i] = nused++; | 
| 1043 | 
        } | 
| 1044 | 
        if (nused == sc->nverts) | 
| 1045 | 
                goto skip_pos; | 
| 1046 | 
        sc->vert = (Vertex *)erealloc(sc->vert, | 
| 1047 | 
                                sizeof(Vertex)*(nused+(CHUNKSIZ-1))); | 
| 1048 | 
        sc->nverts = nused; | 
| 1049 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 1050 | 
                for (i = f->nv; i--; ) | 
| 1051 | 
                        if ((f->v[i].vid = vmap[f->v[i].vid]) < 0) | 
| 1052 | 
                                error(CONSISTENCY, | 
| 1053 | 
                                        "Link error in del_unref_verts()"); | 
| 1054 | 
skip_pos: | 
| 1055 | 
                                                /* remap texture coord's */ | 
| 1056 | 
        if (!sc->ntex) | 
| 1057 | 
                goto skip_tex; | 
| 1058 | 
        memset((void *)vmap, 0, sizeof(int)*sc->ntex); | 
| 1059 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 1060 | 
                for (i = f->nv; i--; ) | 
| 1061 | 
                        if (f->v[i].tid >= 0) | 
| 1062 | 
                                vmap[f->v[i].tid] = 1; | 
| 1063 | 
        for (i = nused = 0; i < sc->ntex; i++) { | 
| 1064 | 
                if (!vmap[i]) | 
| 1065 | 
                        continue; | 
| 1066 | 
                if (nused != i) | 
| 1067 | 
                        sc->tex[nused] = sc->tex[i]; | 
| 1068 | 
                vmap[i] = nused++; | 
| 1069 | 
        } | 
| 1070 | 
        if (nused == sc->ntex) | 
| 1071 | 
                goto skip_tex; | 
| 1072 | 
        sc->tex = (TexCoord *)erealloc(sc->tex, | 
| 1073 | 
                                sizeof(TexCoord)*(nused+(CHUNKSIZ-1))); | 
| 1074 | 
        sc->ntex = nused; | 
| 1075 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 1076 | 
                for (i = f->nv; i--; ) | 
| 1077 | 
                        if (f->v[i].tid >= 0) | 
| 1078 | 
                                f->v[i].tid = vmap[f->v[i].tid]; | 
| 1079 | 
skip_tex: | 
| 1080 | 
                                                /* remap normals */ | 
| 1081 | 
        if (!sc->nnorms) | 
| 1082 | 
                goto skip_norms; | 
| 1083 | 
        memset((void *)vmap, 0, sizeof(int)*sc->nnorms); | 
| 1084 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 1085 | 
                for (i = f->nv; i--; ) | 
| 1086 | 
                        if (f->v[i].nid >= 0) | 
| 1087 | 
                                vmap[f->v[i].nid] = 1; | 
| 1088 | 
        for (i = nused = 0; i < sc->nnorms; i++) { | 
| 1089 | 
                if (!vmap[i]) | 
| 1090 | 
                        continue; | 
| 1091 | 
                if (nused != i) | 
| 1092 | 
                        memcpy(sc->norm[nused], sc->norm[i], sizeof(Normal)); | 
| 1093 | 
                vmap[i] = nused++; | 
| 1094 | 
        } | 
| 1095 | 
        if (nused == sc->nnorms) | 
| 1096 | 
                goto skip_norms; | 
| 1097 | 
        sc->norm = (Normal *)erealloc(sc->norm, | 
| 1098 | 
                                sizeof(Normal)*(nused+(CHUNKSIZ-1))); | 
| 1099 | 
        sc->nnorms = nused; | 
| 1100 | 
        for (f = sc->flist; f != NULL; f = f->next) | 
| 1101 | 
                for (i = f->nv; i--; ) | 
| 1102 | 
                        if (f->v[i].nid >= 0) | 
| 1103 | 
                                f->v[i].nid = vmap[f->v[i].nid]; | 
| 1104 | 
skip_norms: | 
| 1105 | 
                                                /* clean up */ | 
| 1106 | 
        efree(vmap); | 
| 1107 | 
} | 
| 1108 | 
 | 
| 1109 | 
/* Free a scene */ | 
| 1110 | 
void | 
| 1111 | 
freeScene(Scene *sc) | 
| 1112 | 
{ | 
| 1113 | 
        int     i; | 
| 1114 | 
        Face    *f; | 
| 1115 | 
         | 
| 1116 | 
        if (sc == NULL) | 
| 1117 | 
                return; | 
| 1118 | 
        clearComments(sc); | 
| 1119 | 
        for (i = sc->ngrps; i-- > 0; ) | 
| 1120 | 
                freeqstr(sc->grpname[i]); | 
| 1121 | 
        efree(sc->grpname); | 
| 1122 | 
        for (i = sc->nmats; i-- > 0; ) | 
| 1123 | 
                freeqstr(sc->matname[i]); | 
| 1124 | 
        efree(sc->matname); | 
| 1125 | 
        efree(sc->vert); | 
| 1126 | 
        efree(sc->tex); | 
| 1127 | 
        efree(sc->norm); | 
| 1128 | 
        while ((f = sc->flist) != NULL) { | 
| 1129 | 
                sc->flist = f->next; | 
| 1130 | 
                efree(f); | 
| 1131 | 
        } | 
| 1132 | 
        efree(sc); | 
| 1133 | 
} |