ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/readwfobj.c
Revision: 2.10
Committed: Tue Feb 7 20:28:16 2023 UTC (14 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, HEAD
Changes since 2.9: +8 -1 lines
Log Message:
perf: added calls to flockfile()/funlockfile() for more efficient reading

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: readwfobj.c,v 2.9 2022/01/15 02:00:21 greg Exp $";
3 #endif
4 /*
5 * readobj.c
6 *
7 * Routines for reading a Wavefront .OBJ file
8 *
9 * Created by Greg Ward on Wed Feb 11 2004.
10 */
11
12 #include "rtio.h"
13 #include "rterror.h"
14 #include "fvect.h"
15 #include "paths.h"
16 #include <ctype.h>
17 #include "objutil.h"
18
19 #define MAXARG 512 /* maximum # arguments in a statement */
20
21 static int lineno; /* current line number */
22
23 /* read the next statement from fp */
24 static int
25 get_stmt(char *av[MAXARG], FILE *fp)
26 {
27 static char sbuf[MAXARG*16];
28 register char *cp;
29 register int i;
30
31 do {
32 if (fgetline(cp=sbuf, sizeof(sbuf), fp) == NULL)
33 return(0);
34 i = 0;
35 for ( ; ; ) {
36 while (isspace(*cp) || *cp == '\\') {
37 if (*cp == '\n')
38 lineno++;
39 *cp++ = '\0';
40 }
41 if (!*cp)
42 break;
43 if (i >= MAXARG-1) {
44 sprintf(errmsg,
45 "Too many arguments near line %d (limit %d)",
46 lineno+1, MAXARG-1);
47 error(WARNING, errmsg);
48 break;
49 }
50 av[i++] = cp;
51 while (*++cp && !isspace(*cp))
52 ;
53 }
54 av[i] = NULL;
55 lineno++;
56 } while (!i);
57
58 return(i);
59 }
60
61 /* convert vertex string to index */
62 static int
63 cvtndx(VNDX vi, const Scene *sc, const VNDX ondx, const char *vs)
64 {
65 /* get point */
66 vi[0] = atoi(vs);
67 if (vi[0] > 0) {
68 if ((vi[0] += ondx[0] - 1) >= sc->nverts)
69 return(0);
70 } else if (vi[0] < 0) {
71 vi[0] += sc->nverts;
72 if (vi[0] < 0)
73 return(0);
74 } else
75 return(0);
76 /* get map coord. */
77 while (*vs)
78 if (*vs++ == '/')
79 break;
80 vi[1] = atoi(vs);
81 if (vi[1] > 0) {
82 if ((vi[1] += ondx[1] - 1) >= sc->ntex)
83 return(0);
84 } else if (vi[1] < 0) {
85 vi[1] += sc->ntex;
86 if (vi[1] < 0)
87 return(0);
88 } else
89 vi[1] = -1;
90 /* get normal */
91 while (*vs)
92 if (*vs++ == '/')
93 break;
94 vi[2] = atoi(vs);
95 if (vi[2] > 0) {
96 if ((vi[2] += ondx[2] - 1) >= sc->nnorms)
97 return(0);
98 } else if (vi[2] < 0) {
99 vi[2] += sc->nnorms;
100 if (vi[2] < 0)
101 return(0);
102 } else
103 vi[2] = -1;
104 return(1);
105 }
106
107 /* report syntax error */
108 static void
109 syntax(const char *fn, const char *er)
110 {
111 sprintf(errmsg, "%s: Wavefront syntax error near line %d: %s",
112 fn, lineno, er);
113 error(USER, errmsg);
114 }
115
116 /* combine multi-group name into single identifier w/o spaces */
117 static char *
118 group_name(int ac, char **av)
119 {
120 static char nambuf[256];
121 char *cp;
122 if (ac < 1)
123 return("NO_NAME");
124 if (ac == 1)
125 return(av[0]);
126 for (cp = nambuf; ac--; av++) {
127 strcpy(cp, *av);
128 while (*cp) {
129 /* XXX white space disallowed by current get_stmt()
130 if (isspace(*cp))
131 *cp = '_';
132 else
133 */
134 if (*cp == '.')
135 *cp = '_';
136 ++cp;
137 }
138 *cp++ = '.';
139 }
140 *--cp = '\0';
141 return(nambuf);
142 }
143
144 /* add a new face to scene */
145 static int
146 add_face(Scene *sc, const VNDX ondx, int ac, char *av[])
147 {
148 VNDX vdef[4];
149 VNDX *varr = vdef;
150 Face *f = NULL;
151 int i;
152
153 if (ac < 3) /* legal polygon? */
154 return(0);
155 if (ac > 4) /* need to allocate array? */
156 varr = (VNDX *)emalloc(ac*sizeof(VNDX));
157 for (i = ac; i--; ) /* index each vertex */
158 if (!cvtndx(varr[i], sc, ondx, av[i]))
159 break;
160 if (i < 0) /* create face if indices OK */
161 f = addFace(sc, varr, ac);
162 if (varr != vdef)
163 efree(varr);
164 return(f != NULL);
165 }
166
167 /* Load a .OBJ file */
168 Scene *
169 loadOBJ(Scene *sc, const char *fspec)
170 {
171 FILE *fp;
172 char *argv[MAXARG];
173 int argc;
174 char buf[1024];
175 int nstats=0, nunknown=0;
176 int onfaces;
177 VNDX ondx;
178
179 if (fspec == NULL) {
180 fp = stdin;
181 fspec = "<stdin>";
182 #if POPEN_SUPPORT
183 } else if (fspec[0] == '!') {
184 if ((fp = popen(fspec+1, "r")) == NULL) {
185 sprintf(errmsg, "%s: cannot execute", fspec);
186 error(SYSTEM, errmsg);
187 return(NULL);
188 }
189 #endif
190 } else if ((fp = fopen(fspec, "r")) == NULL) {
191 sprintf(errmsg, "%s: cannot open for reading", fspec);
192 error(SYSTEM, errmsg);
193 return(NULL);
194 }
195 #ifdef getc_unlocked /* avoid stupid semaphores */
196 flockfile(fp);
197 #endif
198 if (sc == NULL)
199 sc = newScene();
200 lineno = 0;
201 onfaces = sc->nfaces;
202 ondx[0] = sc->nverts;
203 ondx[1] = sc->ntex;
204 ondx[2] = sc->nnorms;
205 while ((argc = get_stmt(argv, fp)) > 0) {
206 switch (argv[0][0]) {
207 case 'v': /* vertex */
208 switch (argv[0][1]) {
209 case '\0': /* point */
210 if (badarg(argc-1,argv+1,"fff")) {
211 syntax(fspec, "bad vertex");
212 goto failure;
213 }
214 addVertex(sc, atof(argv[1]),
215 atof(argv[2]),
216 atof(argv[3]));
217 break;
218 case 't': /* texture coord. */
219 if (argv[0][2])
220 goto unknown;
221 if (badarg(argc-1,argv+1,"ff"))
222 goto unknown;
223 addTexture(sc, atof(argv[1]), atof(argv[2]));
224 break;
225 case 'n': /* normal */
226 if (argv[0][2])
227 goto unknown;
228 if (badarg(argc-1,argv+1,"fff")) {
229 syntax(fspec, "bad normal");
230 goto failure;
231 }
232 if (addNormal(sc, atof(argv[1]),
233 atof(argv[2]),
234 atof(argv[3])) < 0) {
235 syntax(fspec, "zero normal");
236 goto failure;
237 }
238 break;
239 default:
240 goto unknown;
241 }
242 break;
243 case 'f': /* face */
244 if (argv[0][1])
245 goto unknown;
246 if (!add_face(sc, ondx, argc-1, argv+1)) {
247 syntax(fspec, "bad face");
248 goto failure;
249 }
250 break;
251 case 'u': /* usemtl/usemap */
252 if (!strcmp(argv[0], "usemap"))
253 break;
254 if (strcmp(argv[0], "usemtl"))
255 goto unknown;
256 if (argc != 2) {
257 syntax(fspec, "bad # arguments");
258 goto failure;
259 }
260 setMaterial(sc, argv[1]);
261 break;
262 case 'o': /* object name */
263 case 'g': /* group name */
264 if (argc < 2) {
265 syntax(fspec, "missing argument");
266 goto failure;
267 }
268 setGroup(sc, group_name(argc-1, argv+1));
269 break;
270 case '#': /* comment */
271 continue;
272 default:; /* something we don't deal with */
273 unknown:
274 nunknown++;
275 break;
276 }
277 nstats++;
278 if (verbose && !(nstats & 0x3fff))
279 fprintf(stderr, " %8d statements\r", nstats);
280 }
281 #if POPEN_SUPPORT
282 if (fspec[0] == '!') {
283 if (pclose(fp) != 0) {
284 sprintf(errmsg, "Bad return status from: %s", fspec+1);
285 error(USER, errmsg);
286 freeScene(sc);
287 return(NULL);
288 }
289 } else
290 #endif
291 if (fp != stdin)
292 fclose(fp);
293 #ifdef getc_unlocked
294 else
295 funlockfile(fp);
296 #endif
297 if (verbose)
298 fprintf(stderr, "Read %d statements\n", nstats);
299 if (strlen(fspec) < sizeof(buf)-32)
300 sprintf(buf, "%d statements read from \"%s\"", nstats, fspec);
301 else
302 sprintf(buf, "%d statements read from (TOO LONG TO SHOW)", nstats);
303 addComment(sc, buf);
304 if (nunknown) {
305 sprintf(buf, "\t%d unrecognized", nunknown);
306 addComment(sc, buf);
307 }
308 sprintf(buf, "%d faces", sc->nfaces - onfaces);
309 addComment(sc, buf);
310 sprintf(buf, "\t%d vertices, %d texture coordinates, %d surface normals",
311 sc->nverts - ondx[0],
312 sc->ntex - ondx[1],
313 sc->nnorms - ondx[2]);
314 addComment(sc, buf);
315 return(sc);
316 failure:
317 #if POPEN_SUPPORT
318 if (fspec[0] == '!')
319 pclose(fp);
320 else
321 #endif
322 if (fp != stdin)
323 fclose(fp);
324 freeScene(sc);
325 return(NULL);
326 }