--- ray/src/common/objutil.c 2020/04/02 20:44:15 2.2 +++ ray/src/common/objutil.c 2021/02/12 01:57:49 2.11 @@ -1,5 +1,5 @@ #ifndef lint -static const char RCSid[] = "$Id: objutil.c,v 2.2 2020/04/02 20:44:15 greg Exp $"; +static const char RCSid[] = "$Id: objutil.c,v 2.11 2021/02/12 01:57:49 greg Exp $"; #endif /* * Basic .OBJ scene handling routines. @@ -19,7 +19,7 @@ static const char RCSid[] = "$Id: objutil.c,v 2.2 2020 int findName(const char *nm, const char **nmlist, int n) { - register int i; + int i; for (i = n; i-- > 0; ) if (!strcmp(nmlist[i], nm)) @@ -322,7 +322,7 @@ replace_vertex(Scene *sc, int prev, int repl, double e goto linkerr; /* XXX doesn't allow for multiple references to prev in face */ f->v[j].vid = repl; /* replace vertex itself */ - if (faceArea(sc, f, NULL) <= FTINY) + if (faceArea(sc, f, NULL) <= FTINY*FTINY) f->flags |= FACE_DEGENERATE; if (f->v[j].tid >= 0) /* replace texture if appropriate */ for (i = 0; repl_tex[i] >= 0; i++) { @@ -460,6 +460,8 @@ findDuplicateFaces(Scene *sc) ++nfound; } } + if (verbose) + fprintf(stderr, "Found %d duplicate faces\n", nfound); return(nfound); } @@ -547,6 +549,19 @@ addComment(Scene *sc, const char *comment) sc->descr[sc->ndescr++] = savqstr((char *)comment); } +/* Find index for comment containing the given string (starting from n) */ +int +findComment(Scene *sc, const char *match, int n) +{ + if (n >= sc->ndescr) + return(-1); + n *= (n > 0); + while (n < sc->ndescr) + if (strstr(sc->descr[n], match) != NULL) + return(n); + return(-1); +} + /* Clear comments */ void clearComments(Scene *sc) @@ -599,10 +614,107 @@ newScene(void) return(sc); } -/* Duplicate a scene */ +/* Add a vertex to a scene */ +int +addVertex(Scene *sc, double x, double y, double z) +{ + sc->vert = chunk_alloc(Vertex, sc->vert, sc->nverts); + sc->vert[sc->nverts].p[0] = x; + sc->vert[sc->nverts].p[1] = y; + sc->vert[sc->nverts].p[2] = z; + sc->vert[sc->nverts].vflist = NULL; + return(sc->nverts++); +} + +/* Add a texture coordinate to a scene */ +int +addTexture(Scene *sc, double u, double v) +{ + sc->tex = chunk_alloc(TexCoord, sc->tex, sc->ntex); + sc->tex[sc->ntex].u = u; + sc->tex[sc->ntex].v = v; + return(sc->ntex++); +} + +/* Add a surface normal to a scene */ +int +addNormal(Scene *sc, double xn, double yn, double zn) +{ + FVECT nrm; + + nrm[0] = xn; nrm[1] = yn; nrm[2] = zn; + if (normalize(nrm) == .0) + return(-1); + sc->norm = chunk_alloc(Normal, sc->norm, sc->nnorms); + VCOPY(sc->norm[sc->nnorms], nrm); + return(sc->nnorms++); +} + +/* Set current (last) group */ +void +setGroup(Scene *sc, const char *nm) +{ + sc->lastgrp = findName(nm, (const char **)sc->grpname, sc->ngrps); + if (sc->lastgrp >= 0) + return; + sc->grpname = chunk_alloc(char *, sc->grpname, sc->ngrps); + sc->grpname[sc->lastgrp=sc->ngrps++] = savqstr((char *)nm); +} + +/* Set current (last) material */ +void +setMaterial(Scene *sc, const char *nm) +{ + sc->lastmat = findName(nm, (const char **)sc->matname, sc->nmats); + if (sc->lastmat >= 0) + return; + sc->matname = chunk_alloc(char *, sc->matname, sc->nmats); + sc->matname[sc->lastmat=sc->nmats++] = savqstr((char *)nm); +} + +/* Add new face to a scene */ +Face * +addFace(Scene *sc, VNDX vid[], int nv) +{ + Face *f; + int i; + + if (nv < 3) + return(NULL); + f = (Face *)emalloc(sizeof(Face)+sizeof(VertEnt)*(nv-3)); + f->flags = 0; + f->nv = nv; + f->grp = sc->lastgrp; + f->mat = sc->lastmat; + for (i = 0; i < nv; i++) { /* add each vertex */ + int j; + f->v[i].vid = vid[i][0]; + f->v[i].tid = vid[i][1]; + f->v[i].nid = vid[i][2]; + f->v[i].fnext = NULL; + for (j = i; j-- > 0; ) + if (f->v[j].vid == vid[i][0]) + break; + if (j < 0) { /* first occurrence? */ + f->v[i].fnext = sc->vert[vid[i][0]].vflist; + sc->vert[vid[i][0]].vflist = f; + } else if (nv == 3) /* degenerate triangle? */ + f->flags |= FACE_DEGENERATE; + } + f->next = sc->flist; /* push onto face list */ + sc->flist = f; + sc->nfaces++; + /* check face area */ + if (!(f->flags & FACE_DEGENERATE) && faceArea(sc, f, NULL) <= FTINY*FTINY) + f->flags |= FACE_DEGENERATE; + return(f); +} + +/* Duplicate a scene, optionally selecting faces */ Scene * -dupScene(const Scene *osc) +dupScene(const Scene *osc, int flreq, int flexc) { + int fltest = flreq | flexc; Scene *sc; const Face *fo; Face *f; @@ -651,6 +763,8 @@ dupScene(const Scene *osc) sc->nnorms = osc->nnorms; } for (fo = osc->flist; fo != NULL; fo = fo->next) { + if ((fo->flags & fltest) != flreq) + continue; f = (Face *)emalloc(sizeof(Face) + sizeof(VertEnt)*(fo->nv-3)); memcpy((void *)f, (const void *)fo, sizeof(Face) + @@ -661,26 +775,66 @@ dupScene(const Scene *osc) sc->flist = f; sc->nfaces++; } + deleteUnreferenced(sc); /* jetsam */ return(sc); } -/* Transform entire scene */ #define MAXAC 100 + +/* Transform entire scene */ int +xfScene(Scene *sc, int xac, char *xav[]) +{ + char comm[24+MAXAC*8]; + char *cp; + XF myxf; + FVECT vec; + int i; + + if ((sc == NULL) | (xac <= 0) | (xav == NULL)) + return(0); + /* compute matrix */ + if (xf(&myxf, xac, xav) < xac) + return(0); + /* transform vertices */ + for (i = 0; i < sc->nverts; i++) { + VCOPY(vec, sc->vert[i].p); + multp3(vec, vec, myxf.xfm); + VCOPY(sc->vert[i].p, vec); + } + /* transform normals */ + for (i = 0; i < sc->nnorms; i++) { + VCOPY(vec, sc->norm[i]); + multv3(vec, vec, myxf.xfm); + vec[0] /= myxf.sca; vec[1] /= myxf.sca; vec[2] /= myxf.sca; + VCOPY(sc->norm[i], vec); + } + /* add comment */ + cp = strcpy(comm, "Transformed by:"); + for (i = 0; i < xac; i++) { + while (*cp) cp++; + *cp++ = ' '; + strcpy(cp, xav[i]); + } + addComment(sc, comm); + return(xac); /* all done */ +} + +/* Ditto, using transform string rather than pre-parsed words */ +int xfmScene(Scene *sc, const char *xfm) { char *xav[MAXAC+1]; int xac, i; - XF myxf; - FVECT vec; if ((sc == NULL) | (xfm == NULL)) return(0); - while (isspace(*xfm)) /* find first word */ + /* skip spaces at beginning */ + while (isspace(*xfm)) xfm++; if (!*xfm) return(0); - /* break into words for xf() */ + /* parse string into words */ xav[0] = strcpy((char *)malloc(strlen(xfm)+1), xfm); xac = 1; i = 0; for ( ; ; ) { @@ -691,33 +845,111 @@ xfmScene(Scene *sc, const char *xfm) xav[0][i++] = '\0'; if (!xfm[i]) break; - if (xac >= MAXAC-1) - goto bad_xform; + if (xac >= MAXAC-1) { + free(xav[0]); + return(0); + } xav[xac++] = xav[0] + i; } xav[xac] = NULL; - if (xf(&myxf, xac, xav) < xac) - goto bad_xform; + i = xfScene(sc, xac, xav); free(xav[0]); - /* transform vertices */ - for (i = 0; i < sc->nverts; i++) { - VCOPY(vec, sc->vert[i].p); - multp3(vec, vec, myxf.xfm); - VCOPY(sc->vert[i].p, vec); + return(i); +} +#undef MAXAC + +/* Delete unreferenced vertices, normals, texture coords */ +void +deleteUnreferenced(Scene *sc) +{ + int *vmap; + Face *f; + int nused, i; + /* allocate index map */ + if (!sc->nverts) + return; + i = sc->nverts; + if (sc->ntex > i) + i = sc->ntex; + if (sc->nnorms > i) + i = sc->nnorms; + vmap = (int *)emalloc(sizeof(int)*i); + /* remap positions */ + for (i = nused = 0; i < sc->nverts; i++) { + if (sc->vert[i].vflist == NULL) { + vmap[i] = -1; + continue; + } + if (nused != i) + sc->vert[nused] = sc->vert[i]; + vmap[i] = nused++; } - /* transform normals */ - for (i = 0; i < sc->nnorms; i++) { - VCOPY(vec, sc->norm[i]); - multv3(vec, vec, myxf.xfm); - vec[0] /= myxf.sca; vec[1] /= myxf.sca; vec[2] /= myxf.sca; - VCOPY(sc->norm[i], vec); + if (nused == sc->nverts) + goto skip_pos; + sc->vert = (Vertex *)erealloc((char *)sc->vert, + sizeof(Vertex)*(nused+(CHUNKSIZ-1))); + sc->nverts = nused; + for (f = sc->flist; f != NULL; f = f->next) + for (i = f->nv; i--; ) + if ((f->v[i].vid = vmap[f->v[i].vid]) < 0) + error(CONSISTENCY, + "Link error in del_unref_verts()"); +skip_pos: + /* remap texture coord's */ + if (!sc->ntex) + goto skip_tex; + memset((void *)vmap, 0, sizeof(int)*sc->ntex); + for (f = sc->flist; f != NULL; f = f->next) + for (i = f->nv; i--; ) + if (f->v[i].tid >= 0) + vmap[f->v[i].tid] = 1; + for (i = nused = 0; i < sc->ntex; i++) { + if (!vmap[i]) + continue; + if (nused != i) + sc->tex[nused] = sc->tex[i]; + vmap[i] = nused++; } - return xac; /* finito */ -bad_xform: - free(xav[0]); - return(0); + if (nused == sc->ntex) + goto skip_tex; + sc->tex = (TexCoord *)erealloc((char *)sc->tex, + sizeof(TexCoord)*(nused+(CHUNKSIZ-1))); + sc->ntex = nused; + for (f = sc->flist; f != NULL; f = f->next) + for (i = f->nv; i--; ) + if (f->v[i].tid >= 0) + f->v[i].tid = vmap[f->v[i].tid]; +skip_tex: + /* remap normals */ + if (!sc->nnorms) + goto skip_norms; + memset((void *)vmap, 0, sizeof(int)*sc->nnorms); + for (f = sc->flist; f != NULL; f = f->next) + for (i = f->nv; i--; ) + if (f->v[i].nid >= 0) + vmap[f->v[i].nid] = 1; + for (i = nused = 0; i < sc->nnorms; i++) { + if (!vmap[i]) + continue; + if (nused != i) + memcpy((void *)sc->norm[nused], + (void *)sc->norm[i], + sizeof(Normal)); + vmap[i] = nused++; + } + if (nused == sc->nnorms) + goto skip_norms; + sc->norm = (Normal *)erealloc((char *)sc->norm, + sizeof(Normal)*(nused+(CHUNKSIZ-1))); + sc->nnorms = nused; + for (f = sc->flist; f != NULL; f = f->next) + for (i = f->nv; i--; ) + if (f->v[i].nid >= 0) + f->v[i].nid = vmap[f->v[i].nid]; +skip_norms: + /* clean up */ + efree((char *)vmap); } -#undef MAXAC /* Free a scene */ void