1 |
greg |
1.1 |
/* Copyright (c) 1990 Regents of the University of California */ |
2 |
|
|
|
3 |
|
|
#ifndef lint |
4 |
|
|
static char SCCSid[] = "$SunId$ LBL"; |
5 |
|
|
#endif |
6 |
|
|
|
7 |
|
|
/* |
8 |
|
|
* Convert Neutral File Format input to Radiance scene description. |
9 |
|
|
* |
10 |
|
|
* 12/9/90 Greg Ward |
11 |
|
|
*/ |
12 |
|
|
|
13 |
|
|
/****************************************************************** |
14 |
|
|
|
15 |
|
|
Since Eric Haines wrote such excellent documentation of his |
16 |
|
|
Neutral File Format, I am just going to reprint it here with |
17 |
|
|
my added comments in braces {}. |
18 |
|
|
|
19 |
|
|
Neutral File Format (NFF), by Eric Haines |
20 |
|
|
|
21 |
|
|
Draft document #1, 10/3/88 |
22 |
|
|
|
23 |
|
|
The NFF (Neutral File Format) is designed as a minimal scene description |
24 |
|
|
language. The language was designed in order to test various rendering |
25 |
|
|
algorithms and efficiency schemes. It is meant to describe the geometry and |
26 |
|
|
basic surface characteristics of objects, the placement of lights, and the |
27 |
|
|
viewing frustum for the eye. Some additional information is provided for |
28 |
|
|
esthetic reasons (such as the color of the objects, which is not strictly |
29 |
|
|
necessary for testing rendering algorithms). |
30 |
|
|
|
31 |
|
|
Future enhancements include: circle and torus objects, spline surfaces |
32 |
|
|
with trimming curves, directional lights, characteristics for positional |
33 |
|
|
lights, CSG descriptions, and probably more by the time you read this. |
34 |
|
|
Comments, suggestions, and criticisms are all welcome. |
35 |
|
|
|
36 |
|
|
At present the NFF file format is used in conjunction with the SPD (Standard |
37 |
|
|
Procedural Database) software, a package designed to create a variety of |
38 |
|
|
databases for testing rendering schemes. The SPD package is available |
39 |
|
|
from Netlib and via ftp from drizzle.cs.uoregon.edu. For more information |
40 |
|
|
about SPD see "A Proposal for Standard Graphics Environments," IEEE Computer |
41 |
|
|
Graphics and Applications, vol. 7, no. 11, November 1987, pp. 3-5. |
42 |
|
|
|
43 |
|
|
By providing a minimal interface, NFF is meant to act as a simple format to |
44 |
|
|
allow the programmer to quickly write filters to move from NFF to the |
45 |
|
|
local file format. Presently the following entities are supported: |
46 |
|
|
A simple perspective frustum |
47 |
|
|
A positional (vs. directional) light source description |
48 |
|
|
A background color description |
49 |
|
|
A surface properties description |
50 |
|
|
Polygon, polygonal patch, cylinder/cone, and sphere descriptions |
51 |
|
|
|
52 |
|
|
Files are output as lines of text. For each entity, the first line |
53 |
|
|
defines its type. The rest of the first line and possibly other lines |
54 |
|
|
contain further information about the entity. Entities include: |
55 |
|
|
|
56 |
|
|
"v" - viewing vectors and angles { optionally creates view file } |
57 |
|
|
"l" - positional light location { it's there, but bad to use } |
58 |
|
|
"b" - background color { ditto } |
59 |
|
|
"f" - object material properties { this is flakey } |
60 |
|
|
"c" - cone or cylinder primitive |
61 |
|
|
"s" - sphere primitive |
62 |
|
|
"p" - polygon primitive |
63 |
|
|
"pp" - polygonal patch primitive { interpreted same as p for now } |
64 |
|
|
|
65 |
|
|
These are explained in depth below: { see conversion routines } |
66 |
|
|
|
67 |
|
|
***********************************************************************/ |
68 |
|
|
|
69 |
|
|
#include <stdio.h> |
70 |
|
|
|
71 |
|
|
char *viewfile = NULL; /* view parameters file */ |
72 |
|
|
|
73 |
|
|
char *progname; |
74 |
|
|
|
75 |
|
|
|
76 |
|
|
main(argc, argv) /* convert NFF file to Radiance */ |
77 |
|
|
int argc; |
78 |
|
|
char *argv[]; |
79 |
|
|
{ |
80 |
|
|
int i; |
81 |
|
|
|
82 |
|
|
progname = argv[0]; |
83 |
|
|
for (i = 1; i < argc; i++) |
84 |
|
|
if (argc-i > 1 && !strcmp(argv[i], "-vf")) |
85 |
|
|
viewfile = argv[++i]; |
86 |
|
|
else |
87 |
|
|
break; |
88 |
|
|
if (i-argc > 1) |
89 |
|
|
goto userr; |
90 |
|
|
if (i-argc == 1 && freopen(argv[i], "r", stdin) == NULL) { |
91 |
|
|
perror(argv[i]); |
92 |
|
|
exit(1); |
93 |
|
|
} |
94 |
|
|
init(); |
95 |
|
|
nff2rad(); |
96 |
|
|
exit(0); |
97 |
|
|
userr: |
98 |
|
|
fprintf(stderr, "Usage: %s [-vf viewfile] [input]\n", progname); |
99 |
|
|
exit(1); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
|
103 |
|
|
init() /* spit out initial definitions */ |
104 |
|
|
{ |
105 |
|
|
printf("# File created by %s\n", progname); |
106 |
|
|
printf("\nvoid light light\n"); |
107 |
|
|
printf("0\n0\n3 1 1 1\n"); |
108 |
|
|
printf("\nvoid plastic fill\n"); |
109 |
|
|
printf("0\n0\n5 .5 .5 .5 0 0\n"); |
110 |
|
|
} |
111 |
|
|
|
112 |
|
|
|
113 |
|
|
nff2rad() /* convert NFF on stdin to Radiance on stdout */ |
114 |
|
|
{ |
115 |
|
|
register int c; |
116 |
|
|
|
117 |
|
|
while ((c = getchar()) != EOF) |
118 |
|
|
switch (c) { |
119 |
|
|
case ' ': /* white space */ |
120 |
|
|
case '\t': |
121 |
|
|
case '\n': |
122 |
|
|
case '\f': |
123 |
|
|
case '\r': |
124 |
|
|
continue; |
125 |
|
|
case '#': /* comment */ |
126 |
|
|
comment(); |
127 |
|
|
break; |
128 |
|
|
case 'v': /* view point */ |
129 |
|
|
view(); |
130 |
|
|
break; |
131 |
|
|
case 'l': /* light source */ |
132 |
|
|
light(); |
133 |
|
|
break; |
134 |
|
|
case 'b': /* background color */ |
135 |
|
|
background(); |
136 |
|
|
break; |
137 |
|
|
case 'f': /* fill material */ |
138 |
|
|
fill(); |
139 |
|
|
break; |
140 |
|
|
case 'c': /* cylinder or cone */ |
141 |
|
|
cone(); |
142 |
|
|
break; |
143 |
|
|
case 's': /* sphere */ |
144 |
|
|
sphere(); |
145 |
|
|
break; |
146 |
|
|
case 'p': /* polygon or patch */ |
147 |
|
|
poly(); |
148 |
|
|
break; |
149 |
|
|
default: /* unknown */ |
150 |
|
|
fprintf(stderr, "%c: unknown NFF primitive\n", c); |
151 |
|
|
exit(1); |
152 |
|
|
} |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
|
156 |
|
|
/******************************************* |
157 |
|
|
|
158 |
|
|
Comment. Description: |
159 |
|
|
"#" [ string ] |
160 |
|
|
|
161 |
|
|
Format: |
162 |
|
|
# [ string ] |
163 |
|
|
|
164 |
|
|
As soon as a "#" character is detected, the rest of the line is considered |
165 |
|
|
a comment. |
166 |
|
|
|
167 |
|
|
******************/ |
168 |
|
|
|
169 |
|
|
comment() |
170 |
|
|
{ |
171 |
|
|
register int c; |
172 |
|
|
|
173 |
|
|
putchar('#'); |
174 |
|
|
while ((c = getchar()) != EOF) { |
175 |
|
|
putchar(c); |
176 |
|
|
if (c == '\n') |
177 |
|
|
break; |
178 |
|
|
} |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
|
182 |
|
|
/*************************************************** |
183 |
|
|
|
184 |
|
|
Viewpoint location. Description: |
185 |
|
|
"v" |
186 |
|
|
"from" Fx Fy Fz |
187 |
|
|
"at" Ax Ay Az |
188 |
|
|
"up" Ux Uy Uz |
189 |
|
|
"angle" angle |
190 |
|
|
"hither" hither |
191 |
|
|
"resolution" xres yres |
192 |
|
|
|
193 |
|
|
Format: |
194 |
|
|
|
195 |
|
|
v |
196 |
|
|
from %g %g %g |
197 |
|
|
at %g %g %g |
198 |
|
|
up %g %g %g |
199 |
|
|
angle %g |
200 |
|
|
hither %g |
201 |
|
|
resolution %d %d |
202 |
|
|
|
203 |
|
|
The parameters are: |
204 |
|
|
|
205 |
|
|
From: the eye location in XYZ. |
206 |
|
|
At: a position to be at the center of the image, in XYZ world |
207 |
|
|
coordinates. A.k.a. "lookat". |
208 |
|
|
Up: a vector defining which direction is up, as an XYZ vector. |
209 |
|
|
Angle: in degrees, defined as from the center of top pixel row to |
210 |
|
|
bottom pixel row and left column to right column. |
211 |
|
|
Resolution: in pixels, in x and in y. |
212 |
|
|
|
213 |
|
|
Note that no assumptions are made about normalizing the data (e.g. the |
214 |
|
|
from-at distance does not have to be 1). Also, vectors are not |
215 |
|
|
required to be perpendicular to each other. |
216 |
|
|
|
217 |
|
|
For all databases some viewing parameters are always the same: |
218 |
|
|
Yon is "at infinity." |
219 |
|
|
Aspect ratio is 1.0. |
220 |
|
|
|
221 |
|
|
A view entity must be defined before any objects are defined (this |
222 |
|
|
requirement is so that NFF files can be used by hidden surface machines). |
223 |
|
|
|
224 |
|
|
***************/ |
225 |
|
|
|
226 |
|
|
view() |
227 |
|
|
{ |
228 |
|
|
static FILE *fp = NULL; |
229 |
|
|
float from[3], at[3], up[3], angle; |
230 |
|
|
|
231 |
|
|
if (scanf(" from %f %f %f", &from[0], &from[1], &from[2]) != 3) |
232 |
|
|
goto fmterr; |
233 |
|
|
if (scanf(" at %f %f %f", &at[0], &at[1], &at[2]) != 3) |
234 |
|
|
goto fmterr; |
235 |
|
|
if (scanf(" up %f %f %f", &up[0], &up[1], &up[2]) != 3) |
236 |
|
|
goto fmterr; |
237 |
|
|
if (scanf(" angle %f", &angle) != 1) |
238 |
|
|
goto fmterr; |
239 |
|
|
scanf(" hither %*f"); |
240 |
|
|
scanf(" resolution %*d %*d"); |
241 |
|
|
if (viewfile != NULL) { |
242 |
|
|
if (fp == NULL && (fp = fopen(viewfile, "a")) == NULL) { |
243 |
|
|
perror(viewfile); |
244 |
|
|
exit(1); |
245 |
|
|
} |
246 |
|
|
fprintf(fp, |
247 |
|
|
"VIEW= -vp %g %g %g -vd %g %g %g -vu %g %g %g -vh %g -vv %g\n", |
248 |
|
|
from[0], from[1], from[2], |
249 |
|
|
at[0]-from[0], at[1]-from[1], at[2]-from[2], |
250 |
|
|
up[0], up[1], up[2], |
251 |
|
|
angle, angle); |
252 |
|
|
} |
253 |
|
|
return; |
254 |
|
|
fmterr: |
255 |
|
|
fprintf(stderr, "%s: view syntax error\n", progname); |
256 |
|
|
exit(1); |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
|
260 |
|
|
/******************************** |
261 |
|
|
|
262 |
|
|
Positional light. A light is defined by XYZ position. Description: |
263 |
|
|
"l" X Y Z |
264 |
|
|
|
265 |
|
|
Format: |
266 |
|
|
l %g %g %g |
267 |
|
|
|
268 |
|
|
All light entities must be defined before any objects are defined (this |
269 |
|
|
requirement is so that NFF files can be used by hidden surface machines). |
270 |
|
|
Lights have a non-zero intensity of no particular value [this definition |
271 |
|
|
may change soon, with the addition of an intensity and/or color]. |
272 |
|
|
|
273 |
|
|
**************************/ |
274 |
|
|
|
275 |
|
|
light() |
276 |
|
|
{ |
277 |
|
|
static int nlights = 0; |
278 |
|
|
register int c; |
279 |
|
|
float x, y, z; |
280 |
|
|
|
281 |
|
|
if (scanf("%f %f %f", &x, &y, &z) != 3) { |
282 |
|
|
fprintf(stderr, "%s: light source syntax error\n", progname); |
283 |
|
|
exit(1); |
284 |
|
|
} |
285 |
|
|
while ((c = getchar()) != EOF && c != '\n') |
286 |
|
|
; |
287 |
|
|
printf("\nlight sphere l%d ", ++nlights); |
288 |
|
|
printf("0\n0\n4 %g %g %g 1\n", x, y, z); |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
|
292 |
|
|
/************************************************** |
293 |
|
|
|
294 |
|
|
Background color. A color is simply RGB with values between 0 and 1: |
295 |
|
|
"b" R G B |
296 |
|
|
|
297 |
|
|
Format: |
298 |
|
|
b %g %g %g |
299 |
|
|
|
300 |
|
|
If no background color is set, assume RGB = {0,0,0}. |
301 |
|
|
|
302 |
|
|
********************/ |
303 |
|
|
|
304 |
|
|
background() |
305 |
|
|
{ |
306 |
|
|
float r, g, b; |
307 |
|
|
|
308 |
|
|
if (scanf("%f %f %f", &r, &g, &b) != 3) { |
309 |
|
|
fprintf(stderr, "%s: background syntax error\n", progname); |
310 |
|
|
exit(1); |
311 |
|
|
} |
312 |
|
|
printf("\nvoid glow backg_color\n"); |
313 |
|
|
printf("0\n0\n4 %g %g %g 0\n", r, g, b); |
314 |
|
|
printf("\nbackg_color source background\n"); |
315 |
|
|
printf("0\n0\n4 0 0 1 360\n"); |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
|
319 |
|
|
/**************************************************** |
320 |
|
|
|
321 |
|
|
Fill color and shading parameters. Description: |
322 |
|
|
"f" red green blue Kd Ks Shine T index_of_refraction |
323 |
|
|
|
324 |
|
|
Format: |
325 |
|
|
f %g %g %g %g %g %g %g %g |
326 |
|
|
|
327 |
|
|
RGB is in terms of 0.0 to 1.0. |
328 |
|
|
|
329 |
|
|
Kd is the diffuse component, Ks the specular, Shine is the Phong cosine |
330 |
|
|
power for highlights, T is transmittance (fraction of light passed per |
331 |
|
|
unit). Usually, 0 <= Kd <= 1 and 0 <= Ks <= 1, though it is not required |
332 |
|
|
that Kd + Ks == 1. Note that transmitting objects ( T > 0 ) are considered |
333 |
|
|
to have two sides for algorithms that need these (normally objects have |
334 |
|
|
one side). |
335 |
|
|
|
336 |
|
|
The fill color is used to color the objects following it until a new color |
337 |
|
|
is assigned. |
338 |
|
|
|
339 |
|
|
*********************/ |
340 |
|
|
|
341 |
|
|
fill() |
342 |
|
|
{ |
343 |
|
|
float r, g, b, d, s, p, t, n; |
344 |
|
|
|
345 |
|
|
if (scanf("%f %f %f %f %f %f %f %f", &r, &g, &b, |
346 |
|
|
&d, &s, &p, &t, &n) != 8) { |
347 |
|
|
fprintf(stderr, "%s: fill material syntax error\n", progname); |
348 |
|
|
exit(1); |
349 |
|
|
} |
350 |
|
|
d /= 1.-s-t; |
351 |
|
|
r *= d; |
352 |
|
|
g *= d; |
353 |
|
|
b *= d; |
354 |
|
|
if (p > 1.) |
355 |
|
|
p = 1./p; |
356 |
|
|
if (t > .001) { /* has transmission */ |
357 |
|
|
printf("\nvoid trans fill\n"); |
358 |
greg |
1.2 |
printf("0\n0\n7 %g %g %g %g 0 %g 1\n", r, g, b, s, t); |
359 |
greg |
1.1 |
} else { /* no transmission */ |
360 |
|
|
printf("\nvoid plastic fill\n"); |
361 |
|
|
printf("0\n0\n5 %g %g %g %g %g\n", r, g, b, s, p); |
362 |
|
|
} |
363 |
|
|
} |
364 |
|
|
|
365 |
|
|
|
366 |
|
|
/***************************************************** |
367 |
|
|
|
368 |
|
|
Cylinder or cone. A cylinder is defined as having a radius and an axis |
369 |
|
|
defined by two points, which also define the top and bottom edge of the |
370 |
|
|
cylinder. A cone is defined similarly, the difference being that the apex |
371 |
|
|
and base radii are different. The apex radius is defined as being smaller |
372 |
|
|
than the base radius. Note that the surface exists without endcaps. The |
373 |
|
|
cone or cylinder description: |
374 |
|
|
|
375 |
|
|
"c" |
376 |
|
|
base.x base.y base.z base_radius |
377 |
|
|
apex.x apex.y apex.z apex_radius |
378 |
|
|
|
379 |
|
|
Format: |
380 |
|
|
c |
381 |
|
|
%g %g %g %g |
382 |
|
|
%g %g %g %g |
383 |
|
|
|
384 |
|
|
A negative value for both radii means that only the inside of the object is |
385 |
|
|
visible (objects are normally considered one sided, with the outside |
386 |
|
|
visible). Note that the base and apex cannot be coincident for a cylinder |
387 |
|
|
or cone. |
388 |
|
|
|
389 |
|
|
************************/ |
390 |
|
|
|
391 |
|
|
cone() |
392 |
|
|
{ |
393 |
|
|
static int ncs = 0; |
394 |
|
|
int invert; |
395 |
|
|
float x0, y0, z0, x1, y1, z1, r0, r1; |
396 |
|
|
|
397 |
|
|
if (scanf("%f %f %f %f %f %f %f %f", &x0, &y0, &z0, &r0, |
398 |
|
|
&x1, &y1, &z1, &r1) != 8) { |
399 |
|
|
fprintf(stderr, "%s: cylinder or cone syntax error\n", |
400 |
|
|
progname); |
401 |
|
|
exit(1); |
402 |
|
|
} |
403 |
|
|
if (invert = r0 < 0.) { |
404 |
|
|
r0 = -r0; |
405 |
|
|
r1 = -r1; |
406 |
|
|
} |
407 |
|
|
if (r0-r1 < .001 && r1-r0 < .001) { /* cylinder */ |
408 |
|
|
printf("\nfill %s c%d ", invert?"tube":"cylinder", ++ncs); |
409 |
|
|
printf("0\n0\n7\n"); |
410 |
|
|
printf("\t%g\t%g\t%g\n", x0, y0, z0); |
411 |
|
|
printf("\t%g\t%g\t%g\n", x1, y1, z1); |
412 |
|
|
printf("\t%g\n", r0); |
413 |
|
|
} else { /* cone */ |
414 |
|
|
printf("\nfill %s c%d ", invert?"cup":"cone", ++ncs); |
415 |
|
|
printf("0\n0\n8\n"); |
416 |
|
|
printf("\t%g\t%g\t%g\n", x0, y0, z0); |
417 |
|
|
printf("\t%g\t%g\t%g\n", x1, y1, z1); |
418 |
|
|
printf("\t%g\t%g\n", r0, r1); |
419 |
|
|
} |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
|
423 |
|
|
/***************************************** |
424 |
|
|
|
425 |
|
|
Sphere. A sphere is defined by a radius and center position: |
426 |
|
|
"s" center.x center.y center.z radius |
427 |
|
|
|
428 |
|
|
Format: |
429 |
|
|
s %g %g %g %g |
430 |
|
|
|
431 |
|
|
If the radius is negative, then only the sphere's inside is visible |
432 |
|
|
(objects are normally considered one sided, with the outside visible). |
433 |
|
|
|
434 |
|
|
******************/ |
435 |
|
|
|
436 |
|
|
sphere() |
437 |
|
|
{ |
438 |
|
|
static int nspheres = 0; |
439 |
|
|
float x, y, z, r; |
440 |
|
|
|
441 |
|
|
if (scanf("%f %f %f %f", &x, &y, &z, &r) != 4) { |
442 |
|
|
fprintf(stderr, "%s: sphere syntax error\n", progname); |
443 |
|
|
exit(1); |
444 |
|
|
} |
445 |
|
|
if (r < 0.) { |
446 |
|
|
printf("\nfill bubble s%d ", ++nspheres); |
447 |
|
|
printf("0\n0\n4 %g %g %g %g\n", x, y, z, -r); |
448 |
|
|
} else { |
449 |
|
|
printf("\nfill sphere s%d ", ++nspheres); |
450 |
|
|
printf("0\n0\n4 %g %g %g %g\n", x, y, z, r); |
451 |
|
|
} |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
|
455 |
|
|
/********************************************* |
456 |
|
|
|
457 |
|
|
Polygon. A polygon is defined by a set of vertices. With these databases, |
458 |
|
|
a polygon is defined to have all points coplanar. A polygon has only |
459 |
|
|
one side, with the order of the vertices being counterclockwise as you |
460 |
|
|
face the polygon (right-handed coordinate system). The first two edges |
461 |
|
|
must form a non-zero convex angle, so that the normal and side visibility |
462 |
|
|
can be determined. Description: |
463 |
|
|
|
464 |
|
|
"p" total_vertices |
465 |
|
|
vert1.x vert1.y vert1.z |
466 |
|
|
[etc. for total_vertices vertices] |
467 |
|
|
|
468 |
|
|
Format: |
469 |
|
|
p %d |
470 |
|
|
[ %g %g %g ] <-- for total_vertices vertices |
471 |
|
|
|
472 |
|
|
-------- |
473 |
|
|
|
474 |
|
|
Polygonal patch. A patch is defined by a set of vertices and their normals. |
475 |
|
|
With these databases, a patch is defined to have all points coplanar. |
476 |
|
|
A patch has only one side, with the order of the vertices being |
477 |
|
|
counterclockwise as you face the patch (right-handed coordinate system). |
478 |
|
|
The first two edges must form a non-zero convex angle, so that the normal |
479 |
|
|
and side visibility can be determined. Description: |
480 |
|
|
|
481 |
|
|
"pp" total_vertices |
482 |
|
|
vert1.x vert1.y vert1.z norm1.x norm1.y norm1.z |
483 |
|
|
[etc. for total_vertices vertices] |
484 |
|
|
|
485 |
|
|
Format: |
486 |
|
|
pp %d |
487 |
|
|
[ %g %g %g %g %g %g ] <-- for total_vertices vertices |
488 |
|
|
|
489 |
|
|
*******************/ |
490 |
|
|
|
491 |
|
|
poly() |
492 |
|
|
{ |
493 |
|
|
static int npolys = 0; |
494 |
|
|
int ispatch; |
495 |
|
|
int nverts; |
496 |
|
|
float x, y, z; |
497 |
|
|
|
498 |
|
|
ispatch = getchar(); |
499 |
|
|
if (ispatch != 'p') { |
500 |
|
|
ungetc(ispatch, stdin); |
501 |
|
|
ispatch = 0; |
502 |
|
|
} |
503 |
|
|
if (scanf("%d", &nverts) != 1) |
504 |
|
|
goto fmterr; |
505 |
|
|
printf("\nfill polygon p%d ", ++npolys); |
506 |
|
|
printf("0\n0\n%d\n", 3*nverts); |
507 |
|
|
while (nverts-- > 0) { |
508 |
|
|
if (scanf("%f %f %f", &x, &y, &z) != 3) |
509 |
|
|
goto fmterr; |
510 |
|
|
if (ispatch) |
511 |
|
|
scanf("%*f %*f %*f"); |
512 |
|
|
printf("\t%g\t%g\t%g\n", x, y, z); |
513 |
|
|
} |
514 |
|
|
return; |
515 |
|
|
fmterr: |
516 |
|
|
fprintf(stderr, "%s: polygon or patch syntax error\n", progname); |
517 |
|
|
exit(1); |
518 |
|
|
} |