ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/3ds2mgf.c
Revision: 2.1
Committed: Fri Feb 18 00:40:25 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, rad5R2, rad4R2P2, rad5R0, rad5R1, rad4R2, rad4R1, rad4R2P1, rad5R3, HEAD
Log Message:
Major code reorg, moving mgflib to common and introducing BSDF material

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2     static const char RCSid[] = "$Id: 3ds2mgf.c,v 1.10 2008/11/10 19:08:18 greg Exp $";
3     #endif
4     /*
5     3DS2POV.C by Steve Anger and Jeff Bowermaster
6     MGF output added by Greg Ward
7    
8     Reads a 3D Studio .3DS file and writes a POV-Ray, Vivid,
9     Polyray, MGF or raw scene file.
10    
11     Version 2.0 Written Feb/96
12    
13     Compiled with MSDOS GNU C++ 2.4.1 or generic ANSI-C compiler
14     */
15    
16     #include <stdio.h>
17     #include <stdlib.h>
18     #include <math.h>
19     #include <string.h>
20     #include <ctype.h>
21     #include "rayopt3ds.h"
22    
23     #ifdef __TURBOC__
24     #include <alloc.h>
25     extern unsigned _stklen = 16384;
26     #endif
27    
28    
29     #define FALSE 0
30     #define TRUE 1
31    
32     /* Internal bounding modes */
33     #define OFF 0
34     #define ON 1
35     #define AUTO 2
36    
37     #define MAX_LIB 10
38     #define ASPECT 1.333
39    
40     /* Output formats */
41     #define POV10 0
42     #define POV20 1
43     #define VIVID 2
44     #define POLYRAY 3
45     #define MGF 4
46     #define RAW 99
47    
48     #define DEG(x) ((double)(180.0/M_PI)*(x))
49     #define RAD(x) ((double)(M_PI/180.0)*(x))
50    
51     #ifndef M_PI
52     #define M_PI (3.14159265358979323846)
53     #endif
54    
55     #ifndef MAXFLOAT
56     #define MAXFLOAT (1e37)
57     #endif
58     /* RGB chromaticity definitions for MGF */
59     #define CIE_x_r 0.640
60     #define CIE_y_r 0.330
61     #define CIE_x_g 0.290
62     #define CIE_y_g 0.600
63     #define CIE_x_b 0.150
64     #define CIE_y_b 0.060
65     /* computed luminances from above */
66     #define CIE_Y_r 0.265
67     #define CIE_Y_g 0.670
68     #define CIE_Y_b 0.065
69    
70     /* A generic list type */
71     #define LIST_INSERT(root, node) list_insert ((List **)&root, (List *)node)
72     #define LIST_FIND(root, name) list_find ((List **)&root, name)
73     #define LIST_DELETE(root, node) list_delete ((List **)&root, (List *)node)
74     #define LIST_KILL(root) list_kill ((List **)&root)
75    
76     #define LIST_FIELDS \
77     char name[80]; \
78     void *next;
79    
80    
81     typedef unsigned char byte;
82     typedef unsigned short word;
83     typedef unsigned long dword;
84    
85     typedef struct {
86     LIST_FIELDS
87     } List;
88    
89    
90     typedef struct {
91     int a, b, c;
92     } Face;
93    
94    
95     typedef struct {
96     float red, green, blue;
97     } Colour;
98    
99    
100     /* Transformation command */
101     typedef struct {
102     LIST_FIELDS
103    
104     Matrix matrix;
105     } Transform;
106    
107    
108     /* Morph command */
109     typedef struct {
110     LIST_FIELDS
111    
112     int count; /* Number of objects in morph */
113     char names[4][80]; /* Name of n'th object in average */
114     float weight[4]; /* Weight applied to n'th object */
115    
116     Matrix matrix;
117     } Morph;
118    
119    
120     /* Omni light command */
121     typedef struct {
122     LIST_FIELDS
123    
124     Vector pos; /* Light position */
125     Colour col; /* Light colour */
126     } OmniLight;
127    
128    
129     /* Spotlight command */
130     typedef struct {
131     LIST_FIELDS
132    
133     Vector pos; /* Spotlight position */
134     Vector target; /* Spotlight target location */
135     Colour col; /* Spotlight colour */
136     float hotspot; /* Hotspot angle (degrees) */
137     float falloff; /* Falloff angle (degrees) */
138     int shadow_flag; /* Shadow flag (not used) */
139     } Spotlight;
140    
141    
142     /* Camera command */
143     typedef struct {
144     LIST_FIELDS
145    
146     Vector pos; /* Camera location */
147     Vector target; /* Camera target */
148     float bank; /* Banking angle (degrees) */
149     float lens; /* Camera lens size (mm) */
150     } Camera;
151    
152    
153     /* Material list */
154     typedef struct {
155     LIST_FIELDS
156    
157     int external; /* Externally defined material? */
158     } Material;
159    
160    
161     /* Object summary */
162     typedef struct {
163     LIST_FIELDS
164    
165     Vector center; /* Min value of object extents */
166     Vector lengths; /* Max value of object extents */
167     } Summary;
168    
169    
170     /* Material property */
171     typedef struct {
172     LIST_FIELDS
173    
174     Colour ambient;
175     Colour diffuse;
176     Colour specular;
177     float shininess;
178     float transparency;
179     float reflection;
180     int self_illum;
181     int two_side;
182     char tex_map[40];
183     float tex_strength;
184     char bump_map[40];
185     float bump_strength;
186     } MatProp;
187    
188    
189     /* Default material property */
190     MatProp DefaultMaterial = { "Default", NULL, {1.0, 1.0, 1.0}, {1.0, 1.0, 1.0},
191     {1.0, 1.0, 1.0}, 70.0, 0.0, 0.0, FALSE, FALSE };
192    
193     /* A mesh object */
194     typedef struct {
195     LIST_FIELDS
196    
197     int vertices; /* Number of vertices */
198     Vector *vertex; /* List of object vertices */
199    
200     int faces; /* Number of faces */
201     Face *face; /* List of object faces */
202     Material **mtl; /* Materials for each face */
203    
204     Matrix matrix; /* Local mesh matrix */
205     Matrix invmatrix;
206     Vector center; /* Center of object */
207     Vector lengths; /* Dimensions of object */
208    
209     int hidden; /* Hidden flag */
210     int shadow; /* Shadow flag */
211     } Mesh;
212    
213    
214     typedef struct {
215     dword start;
216     dword end;
217     dword length;
218     word tag;
219     } Chunk;
220    
221    
222     typedef struct {
223     byte red;
224     byte green;
225     byte blue;
226     } Colour_24;
227    
228    
229     Colour Black = {0.0, 0.0, 0.0};
230    
231     OmniLight *omni_list = NULL;
232     Spotlight *spot_list = NULL;
233     Camera *cam_list = NULL;
234     Mesh *mesh_list = NULL;
235     Transform *trans_list = NULL;
236     Morph *morph_list = NULL;
237     Material *mtl_list = NULL;
238     List *excl_list = NULL;
239     List *box_list = NULL;
240     MatProp *mprop_list = NULL;
241     Summary *summary = NULL;
242    
243    
244     FILE *in;
245     FILE *out;
246     char inname[80];
247     char outname[80];
248     char vuename[80];
249     char obj_name[80] = "";
250     Colour fog_colour = {0.0, 0.0, 0.0};
251     Colour col = {0.0, 0.0, 0.0};
252     Colour global_amb = {0.1, 0.1, 0.1};
253     Vector pos = {0.0, 0.0, 0.0};
254     Vector target = {0.0, 0.0, 0.0};
255     float fog_distance = 0.0;
256     float hotspot = -1;
257     float falloff = -1;
258     Mesh *mesh = NULL;
259     int frame = -1;
260     char libname[MAX_LIB][80];
261     float smooth = 60.0;
262     int bound = 0;
263     int verbose = 0;
264     int format = POV20;
265     int internal_bounding = AUTO;
266     int box_all = FALSE;
267     int cameras = 0;
268     int libs = 0;
269     float vue_version = 1.0;
270     Matrix *ani_matrix = NULL;
271     int no_opt = FALSE;
272     FILE *meshf = NULL;
273    
274    
275     void process_args (int argc, char *argv[]);
276     void parse_option (char *option);
277     void list_insert (List **root, List *new_node);
278     void *list_find (List **root, char *name);
279     void list_delete (List **root, List *node);
280     void list_kill (List **root);
281     Material *update_materials (char *new_material, int ext);
282     MatProp *create_mprop (void);
283     void read_library (char *fname);
284     void write_intro (FILE *f);
285     void write_summary (FILE *f);
286     void write_bgsolid (FILE *f, Colour col);
287     void write_light (FILE *f, char *name, Vector pos, Colour col);
288     void write_spot (FILE *f, char *name, Vector pos, Vector target, Colour col,
289     float hotspot, float falloff);
290     void write_fog (FILE *f, Colour col, float dist);
291     void write_camera (FILE *f, char *name, Vector pos, Vector target, float lens,
292     float bank);
293     void write_material (FILE *f, char *mat);
294     void write_pov10_material (FILE *f, MatProp *m);
295     void write_pov20_material (FILE *f, MatProp *m);
296     void write_vivid_material (FILE *f, MatProp *m);
297     void write_polyray_material (FILE *f, MatProp *m);
298     void write_mgf_material (FILE *f, MatProp *m);
299     void write_mesh (FILE *f, Mesh *mesh);
300     Transform *parse_transform (char *string);
301     Morph *parse_morph (char *string);
302     OmniLight *parse_omnilight (char *string);
303     Spotlight *parse_spotlight (char *string);
304     Camera *parse_camera (char *string);
305     void read_frame (char *filename, int frame_no);
306     void find_frame (FILE *f, int frame_no);
307     void save_animation (void);
308     Mesh *create_mesh (char *name, int vertices, int faces);
309     Mesh *copy_mesh (Mesh *mesh);
310     void free_mesh_data (Mesh *mesh);
311     void update_limits (Mesh *mesh);
312     char *before (char *str, char *target);
313     char *after (char *str, char *target);
314     char *between (char *str, char *target1, char *target2);
315     char *parse_string (char *str);
316     char upcase (char c);
317     float colour_intens (Colour *colour);
318     void parse_file (void);
319     void parse_3ds (Chunk *mainchunk);
320     void parse_mdata (Chunk *mainchunk);
321     void parse_fog (Chunk *mainchunk);
322     void parse_fog_bgnd (void);
323     void parse_mat_entry (Chunk *mainchunk);
324     char *parse_mapname (Chunk *mainchunk);
325     void parse_named_object (Chunk *mainchunk);
326     void parse_n_tri_object (Chunk *mainchunk);
327     void parse_point_array (void);
328     void parse_face_array (Chunk *mainchunk);
329     void parse_msh_mat_group (void);
330     void parse_smooth_group (void);
331     void parse_mesh_matrix (void);
332     void parse_n_direct_light (Chunk *mainchunk);
333     void parse_dl_spotlight (void);
334     void parse_n_camera (void);
335     void parse_colour (Colour *colour);
336     void parse_colour_f (Colour *colour);
337     void parse_colour_24 (Colour_24 *colour);
338     float parse_percentage (void);
339     short parse_int_percentage (void);
340     float parse_float_percentage (void);
341     void start_chunk (Chunk *chunk);
342     void end_chunk (Chunk *chunk);
343     byte read_byte (void);
344     word read_word (void);
345     dword read_dword (void);
346     float read_float (void);
347     void read_point (Vector v);
348     char *read_string (void);
349     float findfov (float lens);
350     int read_mgfmatname (char *s, int n, FILE *f);
351    
352     char *progname;
353    
354    
355     int main (int argc, char *argv[])
356     {
357     char meshfname[128];
358     Material *m;
359     int i;
360    
361     process_args (argc, argv);
362    
363     if (!no_opt) {
364     opt_set_format (format);
365     opt_set_dec (4);
366     opt_set_bound (bound);
367     opt_set_smooth (smooth);
368     opt_set_quiet (!verbose);
369     opt_set_fname (outname, "");
370     } else if (format == MGF) {
371     strcpy(meshfname, outname);
372     add_ext(meshfname, "inc", 1);
373     if (!strcmp(meshfname, outname)) {
374     printf ("Output and mesh file names are identical!\n");
375     exit (1);
376     }
377     if ((meshf = fopen (meshfname, "w")) == NULL) {
378     printf ("Cannot open mesh output file %s!\n", meshfname);
379     exit (1);
380     }
381     }
382    
383     if ((in = fopen (inname, "rb")) == NULL) {
384     printf ("Cannot open input file %s!\n", inname);
385     exit (1);
386     }
387    
388     if ((out = fopen (outname, "w")) == NULL) {
389     printf ("Cannot open output file %s!\n", outname);
390     exit (1);
391     }
392    
393     /* Load the names of pre-defined materials */
394     for (i = 0; i < MAX_LIB; i++) {
395     if (strlen(libname[i]) > 0)
396     read_library (libname[i]);
397     }
398    
399     /* Load the instructions for the current frame */
400     if (strlen(vuename) > 0)
401     read_frame (vuename, frame);
402    
403     printf("Output to: %s\n", outname);
404    
405     if (frame >= 0)
406     printf ("Generating frame #%d\n", frame);
407    
408     printf("\nPlease wait; Processing...\n");
409    
410     write_intro(out);
411    
412     parse_file();
413    
414     fclose(in);
415    
416     for (m = mtl_list; m != NULL; m = m->next) {
417     if (!m->external)
418     write_material (out, m->name);
419     }
420    
421     if (frame >= 0)
422     save_animation();
423    
424     if (!no_opt) {
425     write_summary (out);
426     fflush (out);
427    
428     opt_finish();
429     } else if (meshf != NULL) {
430     fclose(meshf);
431     fprintf (out, "i %s\n", meshfname);
432     }
433    
434     fclose (out);
435    
436     LIST_KILL (omni_list);
437     LIST_KILL (spot_list);
438     LIST_KILL (cam_list);
439     LIST_KILL (mesh_list);
440     LIST_KILL (trans_list);
441     LIST_KILL (morph_list);
442     LIST_KILL (mtl_list);
443     LIST_KILL (excl_list);
444     LIST_KILL (box_list);
445     LIST_KILL (mprop_list);
446     LIST_KILL (summary);
447    
448     return 0;
449     }
450    
451    
452     /* Handle the command line args */
453     void process_args (int argc, char *argv[])
454     {
455     int i;
456     char *env_opt, *option;
457    
458     printf("\n\nAutodesk 3D Studio to Raytracer file Translator. Feb/96\n");
459     printf("Version 2.0 by Steve Anger and Jeff Bowermaster 1996\n");
460     printf ("\n");
461    
462     if (argc < 2) {
463     printf ("Usage: %s inputfile[.3ds] [outputfile] [options]\n\n", argv[0]);
464     printf ("Options: -snnn - Smooth triangles with angles < nnn\n");
465     printf (" -l<filename> - Specifies native material library\n");
466     printf (" -a<filename> - Use animation information in specified file\n");
467     printf (" -fnnn - Generate frame nnn of animation\n");
468     printf (" -x<object> - Exclude this object from conversion\n");
469     printf (" -b<object> - Convert this object as a box\n");
470     printf (" +i, -i - Turn internal bounding on or off\n");
471     printf (" +v, -v - Turn verbose status messages on or off\n");
472     printf (" -op - Output to POV-Ray 2.0 format\n");
473     printf (" -op1 - Output to POV-Ray 1.0 format\n");
474     printf (" -ov - Output to Vivid format\n");
475     printf (" -ol - Output to poLyray format\n");
476     printf (" -om - Output to MGF\n");
477     printf (" -or - Output to RAW triangle format\n\n");
478     printf ("ex. %s birdshow +v -lmaterials.inc\n\n", argv[0]);
479     exit(1);
480     }
481     /* figure default format from name */
482     progname = strrchr(argv[0], '/');
483     if (progname == NULL) progname = argv[0];
484     else progname++;
485     if (!strcmp(progname, "3ds2pov"))
486     format = POV20;
487     else if (!strcmp(progname, "3ds2viv"))
488     format = VIVID;
489     else if (!strcmp(progname, "3ds2pi"))
490     format = POLYRAY;
491     else if (!strcmp(progname, "3ds2mgf"))
492     format = MGF;
493     else if (!strcmp(progname, "3ds2raw"))
494     format = RAW;
495     else
496     format = POV20; /* default if program name strange */
497    
498     strcpy (inname, "");
499     strcpy (outname, "");
500     strcpy (vuename, "");
501    
502     for (i = 0; i < MAX_LIB; i++)
503     strcpy (libname[i], "");
504    
505     frame = -1;
506     smooth = 70.0;
507     bound = 0;
508     verbose = 0;
509     internal_bounding = AUTO;
510     box_all = FALSE;
511     libs = 0;
512    
513     /* Parse the enviroment string options */
514     env_opt = getenv ("3DS2POV");
515    
516     if (env_opt != NULL) {
517     option = parse_string (env_opt);
518    
519     while (strlen(option) > 0) {
520     parse_option (option);
521     option = parse_string (NULL);
522     }
523     }
524    
525     /* Parse the command line options */
526     for (i = 1; i < argc; i++)
527     parse_option (argv[i]);
528    
529     if (strlen(inname) == 0)
530     abortmsg ("No input file specified", 1);
531    
532     if (strlen(outname) == 0)
533     strcpy (outname, inname);
534    
535     switch (format) {
536     case POV10:
537     case POV20: add_ext (outname, "pov", 1); break;
538     case VIVID: add_ext (outname, "v", 1); break;
539     case POLYRAY: add_ext (outname, "pi", 1); break;
540     case MGF: add_ext (outname, "mgf", 1); break;
541     case RAW: add_ext (outname, "raw", 1); break;
542     }
543    
544     switch (internal_bounding) {
545     case OFF: bound = 2; break;
546     case ON: bound = 0; break;
547     case AUTO: bound = (format == POV10) ? 0 : 2; break;
548     }
549    
550     if ((strlen(vuename) > 0) != (frame >= 0))
551     abortmsg ("The -a and -f parameters must be used together", 1);
552    
553     if (format == RAW || (format == MGF && smooth < 0.1))
554     no_opt = TRUE;
555     }
556    
557    
558     void parse_option (char *option)
559     {
560     List *excl, *box;
561     char name[80];
562    
563     if (option[0] == '-' || option[0] == '+') {
564     switch (upcase(option[1])) {
565     case 'A': strcpy (vuename, &option[2]);
566     break;
567    
568     case 'B': strcpy (name, parse_string (&option[2]));
569     if (strlen(name) == 0)
570     box_all = TRUE;
571     else {
572     cleanup_name (name);
573    
574     box = malloc (sizeof (*box));
575     strcpy (box->name, name);
576    
577     LIST_INSERT (box_list, box);
578     }
579     break;
580    
581     case 'F': if (option[2] != '\0')
582     frame = atoi (&option[2]);
583     break;
584    
585     case 'I': if (option[0] == '-')
586     internal_bounding = OFF;
587     else
588     internal_bounding = ON;
589     break;
590    
591     case 'L': if (libs == MAX_LIB)
592     abortmsg ("Too many libraries specified", 1);
593    
594     strcpy (libname[libs++], &option[2]);
595     break;
596    
597     case 'O': switch (upcase(option[2])) {
598     case 'P': if (option[3] == '1')
599     format = POV10;
600     else
601     format = POV20;
602     break;
603    
604     case 'V': format = VIVID;
605     break;
606    
607     case 'L': format = POLYRAY;
608     break;
609    
610     case 'R': format = RAW;
611     break;
612    
613     case 'M': format = MGF;
614     break;
615    
616     default: printf ("Invalid output format %s specified\n", option);
617     exit(1);
618     }
619     break;
620    
621     case 'S': if (option[2] != '\0')
622     smooth = atof (&option[2]);
623     break;
624    
625     case 'U': printf ("Warning: -u parameter no long has any effect\n");
626     printf (" use +i or -i instead.\n");
627     break;
628    
629     case 'V': if (option[0] == '-')
630     verbose = 0;
631     else
632     verbose = 1;
633     break;
634    
635     case 'X': strcpy (name, parse_string (&option[2]));
636     cleanup_name (name);
637    
638     excl = malloc (sizeof (*excl));
639     strcpy (excl->name, name);
640    
641     LIST_INSERT (excl_list, excl);
642     break;
643    
644     default : printf ("\nInvalid option %s specified\n", option);
645     exit (1);
646     }
647     }
648     else if (strlen (inname) == 0) {
649     strcpy (inname, option);
650     add_ext (inname, "3ds", 0);
651     }
652     else if (strlen (outname) == 0)
653     strcpy (outname, option);
654     else
655     abortmsg ("Too many file names specified.\n", 1);
656     }
657    
658    
659     /* Insert a new node into the list */
660     void list_insert (List **root, List *new_node)
661     {
662     new_node->next = *root;
663    
664     *root = new_node;
665     }
666    
667    
668     /* Find the node with the specified name */
669     void *list_find (List **root, char *name)
670     {
671     List *p;
672    
673     for (p = *root; p != NULL; p = p->next) {
674     if (strcmp (p->name, name) == 0)
675     break;
676     }
677    
678     return (void *)p;
679     }
680    
681    
682     /* Delete the indicated node from the list */
683     void list_delete (List **root, List *node)
684     {
685     List *prev;
686    
687     prev = *root;
688     while (prev != NULL && prev->next != node)
689     prev = prev->next;
690    
691     if (prev == NULL)
692     *root = node->next;
693     else
694     prev->next = node->next;
695    
696     free (node);
697     }
698    
699    
700     /* Delete the entire list */
701     void list_kill (List **root)
702     {
703     List *temp;
704    
705     while (*root != NULL) {
706     temp = *root;
707     *root = (*root)->next;
708     free (temp);
709     }
710     }
711    
712    
713     /* Add a new material to the material list */
714     Material *update_materials (char *new_material, int ext)
715     {
716     Material *p;
717    
718     p = LIST_FIND (mtl_list, new_material);
719    
720     if (p == NULL) {
721     p = malloc (sizeof (*p));
722    
723     if (p == NULL)
724     abortmsg ("Out of memory adding material", 1);
725    
726     strcpy (p->name, new_material);
727     p->external = ext;
728    
729     LIST_INSERT (mtl_list, p);
730     }
731    
732     return p;
733     }
734    
735    
736     MatProp *create_mprop()
737     {
738     MatProp *new_mprop;
739    
740     new_mprop = malloc (sizeof(*new_mprop));
741     if (new_mprop == NULL)
742     abortmsg ("Out of memory adding material", 1);
743    
744     strcpy (new_mprop->name, "");
745     new_mprop->ambient = Black;
746     new_mprop->diffuse = Black;
747     new_mprop->specular = Black;
748     new_mprop->shininess = 0.0;
749     new_mprop->transparency = 0.0;
750     new_mprop->reflection = 0.0;
751     new_mprop->self_illum = FALSE;
752     new_mprop->two_side = FALSE;
753    
754     strcpy (new_mprop->tex_map, "");
755     new_mprop->tex_strength = 0.0;
756    
757     strcpy (new_mprop->bump_map, "");
758     new_mprop->bump_strength = 0.0;
759    
760     return new_mprop;
761     }
762    
763    
764     /* Load in any predefined materials */
765     void read_library (char *fname)
766     {
767     FILE *lib;
768     char string[256], name[80];
769    
770     if ((lib = fopen (fname, "r")) == NULL) {
771     printf ("Cannot open texture library file %s!\n", fname);
772     exit(1);
773     }
774    
775     switch (format) {
776     case POV10:
777     case POV20:
778     while (fgets (string, 256, lib) != NULL) {
779     if (strstr (string, "#declare")) {
780     strcpy (name, between (string, "#declare", "="));
781     cleanup_name (name);
782     (void)update_materials (name, TRUE);
783     }
784     }
785     break;
786    
787     case VIVID:
788     while (fgets (string, 256, lib) != NULL) {
789     if (strstr (string, "#define")) {
790     (void)parse_string (string);
791     strcpy (name, parse_string (NULL));
792     cleanup_name (name);
793     (void)update_materials (name, TRUE);
794     }
795     }
796     break;
797    
798     case POLYRAY:
799     while (fgets (string, 256, lib) != NULL) {
800     if (strstr (string, "define")) {
801     (void)parse_string (string);
802     strcpy (name, parse_string (NULL));
803     cleanup_name (name);
804     (void)update_materials (name, TRUE);
805     }
806     }
807     break;
808    
809     case MGF:
810     while (read_mgfmatname(name, 80, lib))
811     (void)update_materials (name, TRUE);
812     break;
813     }
814    
815     fclose (lib);
816     }
817    
818    
819     /* parse the next MGF material name from f, return FALSE if EOF */
820     int read_mgfmatname (char *s, int n, FILE *f)
821     {
822     char inpline[128];
823     register char *cp, *cp2;
824     register int i;
825     /* find material */
826     while (fgets(inpline, sizeof(inpline), f) != NULL) {
827     for (cp = inpline; isspace(*cp); cp++)
828     ;
829     if (*cp++ != 'm' || !isspace(*cp++))
830     continue;
831     while (isspace(*cp))
832     cp++;
833     if (!*cp)
834     continue;
835     for (i=n, cp2=s; *cp && !isspace(*cp); cp++) /* get name */
836     if (--i > 0)
837     *cp2++ = *cp;
838     *cp2 = '\0';
839     while (isspace(*cp))
840     cp++;
841     if (*cp++ != '=' || !isspace(*cp)) /* not defined? */
842     continue;
843     return TRUE;
844     }
845     return FALSE;
846     }
847    
848    
849     void write_intro (FILE *f)
850     {
851     int i;
852    
853     switch (format) {
854     case POV10:
855     case POV20:
856     fprintf (f, "#include \"colors.inc\"\n");
857     fprintf (f, "#include \"shapes.inc\"\n");
858     fprintf (f, "#include \"textures.inc\"\n");
859    
860     for (i = 0; i < MAX_LIB; i++) {
861     if (strlen(libname[i]) > 0)
862     fprintf (f, "#include \"%s\"\n", libname[i]);
863     }
864    
865     fprintf (f, "\n");
866     break;
867    
868     case VIVID:
869     fprintf (f, "#include color.vc\n");
870    
871     for (i = 0; i < MAX_LIB; i++) {
872     if (strlen(libname[i]) > 0)
873     fprintf (f, "#include %s\n", libname[i]);
874     }
875    
876     fprintf (f, "\n");
877     break;
878    
879     case POLYRAY:
880     fprintf (f, "include \"colors.inc\"\n");
881    
882     for (i = 0; i < MAX_LIB; i++) {
883     if (strlen(libname[i]) > 0)
884     fprintf (f, "include \"%s\"\n", libname[i]);
885     }
886    
887     fprintf (f, "\n");
888     break;
889    
890     case MGF:
891     fprintf (f, "c R =\n\tcxy %.3f %.3f\n", CIE_x_r, CIE_y_r);
892     fprintf (f, "c G =\n\tcxy %.3f %.3f\n", CIE_x_g, CIE_y_g);
893     fprintf (f, "c B =\n\tcxy %.3f %.3f\n", CIE_x_b, CIE_y_b);
894    
895     for (i = 0; i < MAX_LIB; i++) {
896     if (strlen(libname[i]) > 0)
897     fprintf (f, "i %s\n", libname[i]);
898     }
899    
900     fprintf (f, "\n");
901     break;
902     }
903     }
904    
905    
906     /* Write the object summary */
907     void write_summary (FILE *f)
908     {
909     char *comstr;
910     Summary *s;
911    
912     if (summary == NULL)
913     return;
914    
915     switch (format) {
916     case POV10:
917     case POV20:
918     case VIVID:
919     case POLYRAY:
920     comstr = "//";
921     break;
922     case MGF:
923     comstr = "# ";
924     break;
925     default:
926     printf ("Illegal format in write_summary() '%c'\n", format);
927     exit(1);
928     }
929     fprintf (f, "%s Object CenterX CenterY CenterZ LengthX LengthY LengthZ\n", comstr);
930     fprintf (f, "%s ---------- ---------- ---------- ---------- ---------- ---------- ----------\n", comstr);
931    
932     for (s = summary; s != NULL; s = s->next) {
933     fprintf (f, "%s %-10s%11.2f%11.2f%11.2f%11.2f%11.2f%11.2f\n",
934     comstr, s->name, s->center[X], s->center[Y], s->center[Z],
935     s->lengths[X], s->lengths[Y], s->lengths[Z]);
936     }
937    
938     fprintf (f, "\n");
939     }
940    
941    
942     /* Write background solid colour */
943     void write_bgsolid (FILE *f, Colour col)
944     {
945     switch (format) {
946     case POV10:
947     fprintf (f, "/* Background colour */\n");
948     fprintf (f, "object {\n");
949     fprintf (f, " sphere { <0.0 0.0 0.0> 1e6 }\n");
950     fprintf (f, " texture {\n");
951     fprintf (f, " ambient 1.0\n");
952     fprintf (f, " diffuse 0.0\n");
953     fprintf (f, " color red %4.2f green %4.2f blue %4.2f\n",
954     col.red, col.green, col.blue);
955     fprintf (f, " }\n");
956     fprintf (f, "}\n\n");
957     break;
958    
959     case POV20:
960     fprintf (f, "background { color red %4.2f green %4.2f blue %4.2f }\n\n",
961     col.red, col.green, col.blue);
962     break;
963    
964     case POLYRAY:
965     fprintf (f, "background <%4.2f, %4.2f, %4.2f>\n\n",
966     col.red, col.green, col.blue);
967     break;
968     }
969     }
970    
971    
972     void write_light (FILE *f, char *name, Vector pos, Colour col)
973     {
974     switch (format) {
975     case POV10:
976     fprintf (f, "/* Light: %s */\n", name);
977     fprintf (f, "object {\n");
978     fprintf (f, " light_source { <%.4f %.4f %.4f> color red %4.2f green %4.2f blue %4.2f }\n",
979     pos[X], pos[Y], pos[Z], col.red, col.green, col.blue);
980     fprintf (f, "}\n\n");
981     break;
982    
983     case POV20:
984     fprintf (f, "/* Light: %s */\n", name);
985     fprintf (f, "light_source {\n");
986     fprintf (f, " <%.4f, %.4f, %.4f> color rgb <%4.2f, %4.2f, %4.2f>\n",
987     pos[X], pos[Y], pos[Z], col.red, col.green, col.blue);
988     fprintf (f, "}\n\n");
989     break;
990    
991     case VIVID:
992     fprintf (f, "/* Light: %s */\n", name);
993     fprintf (f, "light {\n");
994     fprintf (f, " type point\n");
995     fprintf (f, " position %.4f %.4f %.4f\n",
996     pos[X], pos[Y], pos[Z]);
997     fprintf (f, " color %4.2f %4.2f %4.2f\n",
998     col.red, col.green, col.blue);
999     fprintf (f, "}\n\n");
1000     break;
1001    
1002     case POLYRAY:
1003     fprintf (f, "// Light: %s\n", name);
1004     fprintf (f, "light <%4.2f, %4.2f, %4.2f>, <%.4f, %.4f, %.4f>\n\n",
1005     col.red, col.green, col.blue, pos[X], pos[Y], pos[Z]);
1006     break;
1007    
1008     case MGF:
1009     fprintf (f, "\n# Light\n");
1010     if (name[0]) fprintf (f, "o %s\n", name);
1011     fprintf (f, "m\n\tsides 1\n\tc\n\t\t\tcmix %.3f R %.3f G %.3f B\n\ted %e\n",
1012     CIE_Y_r*col.red, CIE_Y_g*col.green, CIE_Y_b*col.blue,
1013     100000.0*(CIE_Y_r*col.red + CIE_Y_g*col.green + CIE_Y_b*col.blue));
1014     fprintf (f, "v c =\n\tp %.4f %.4f %.4f\nsph c .01\n",
1015     pos[X], pos[Y], pos[Z]);
1016     if (name[0]) fprintf (f, "o\n");
1017     fprintf (f, "\n");
1018     break;
1019     }
1020     }
1021    
1022    
1023     void write_spot (FILE *f, char *name, Vector pos, Vector target, Colour col,
1024     float hotspot, float falloff)
1025     {
1026     switch (format) {
1027     case POV10:
1028     fprintf (f, "/* Spotlight: %s */\n", name);
1029     fprintf (f, "object {\n");
1030     fprintf (f, " light_source {\n");
1031     fprintf (f, " <%.4f %.4f %.4f> color red %4.2f green %4.2f blue %4.2f\n",
1032     pos[X], pos[Y], pos[Z],
1033     col.red, col.green, col.blue);
1034     fprintf (f, " spotlight\n");
1035     fprintf (f, " point_at <%.4f %.4f %.4f>\n",
1036     target[X], target[Y], target[Z]);
1037     fprintf (f, " tightness 0\n");
1038     fprintf (f, " radius %.2f\n", 0.5*hotspot);
1039     fprintf (f, " falloff %.2f\n", 0.5*falloff);
1040     fprintf (f, " }\n");
1041     fprintf (f, "}\n\n");
1042     break;
1043    
1044     case POV20:
1045     fprintf (f, "/* Spotlight: %s */\n", name);
1046     fprintf (f, "light_source {\n");
1047     fprintf (f, " <%.4f, %.4f, %.4f> color rgb <%4.2f, %4.2f, %4.2f>\n",
1048     pos[X], pos[Y], pos[Z],
1049     col.red, col.green, col.blue);
1050     fprintf (f, " spotlight\n");
1051     fprintf (f, " point_at <%.4f, %.4f, %.4f>\n",
1052     target[X], target[Y], target[Z]);
1053     fprintf (f, " tightness 0\n");
1054     fprintf (f, " radius %.2f\n", 0.5*hotspot);
1055     fprintf (f, " falloff %.2f\n", 0.5*falloff);
1056     fprintf (f, "}\n\n");
1057     break;
1058    
1059     case VIVID:
1060     fprintf (f, "/* Spotlight: %s */\n", name);
1061     fprintf (f, "light {\n");
1062     fprintf (f, " type spot\n");
1063     fprintf (f, " position %.4f %.4f %.4f\n",
1064     pos[X], pos[Y], pos[Z]);
1065     fprintf (f, " at %.4f %.4f %.4f\n",
1066     target[X], target[Y], target[Z]);
1067     fprintf (f, " color %4.2f %4.2f %4.2f\n",
1068     col.red, col.green, col.blue);
1069     fprintf (f, " min_angle %.2f\n", hotspot);
1070     fprintf (f, " max_angle %.2f\n", falloff);
1071     fprintf (f, "}\n\n");
1072     break;
1073    
1074     case POLYRAY:
1075     fprintf (f, "// Spotlight: %s\n", name);
1076     fprintf (f, "spot_light <%4.2f, %4.2f, %4.2f>, <%.4f, %.4f, %.4f>,\n",
1077     col.red, col.green, col.blue, pos[X], pos[Y], pos[Z]);
1078     fprintf (f, " <%.4f, %.4f, %.4f>, 0.0, %.2f, %.2f\n\n",
1079     target[X], target[Y], target[Z], hotspot/2.0, falloff/2.0);
1080     break;
1081    
1082     case MGF:
1083     fprintf (f, "\n# Spotlight\n");
1084     if (name[0]) fprintf (f, "o %s\n", name);
1085     fprintf (f, "# hotspot: %.2f\n# falloff: %.2f\n", hotspot, falloff);
1086     fprintf (f, "m\n\tsides 1\n\tc\n\t\t\tcmix %.3f R %.3f G %.3f B\n\ted %e\n",
1087     CIE_Y_r*col.red, CIE_Y_g*col.green, CIE_Y_b*col.blue,
1088     100000.0*(CIE_Y_r*col.red + CIE_Y_g*col.green + CIE_Y_b*col.blue));
1089     fprintf (f, "v c =\n\tp %.4f %.4f %.4f\n\tn %.4f %.4f %.4f\n",
1090     pos[X], pos[Y], pos[Z],
1091     target[X]-pos[X], target[Y]-pos[Y], target[Z]-pos[Z]);
1092     fprintf (f, "ring c 0 .01\n");
1093     if (name[0]) fprintf (f, "o\n");
1094     fprintf (f, "\n");
1095     break;
1096     }
1097     }
1098    
1099    
1100     void write_fog (FILE *f, Colour col, float dist)
1101     {
1102     if (dist <= 0.0)
1103     return;
1104    
1105     switch (format) {
1106     case POV10:
1107     fprintf (f, "fog {\n");
1108     fprintf (f, " color red %4.2f green %4.2f blue %4.2f %.4f\n",
1109     col.red, col.green, col.blue, dist/2.0);
1110     fprintf (f, "}\n\n");
1111     break;
1112    
1113     case POV20:
1114     fprintf (f, "fog {\n");
1115     fprintf (f, " color red %4.2f green %4.2f blue %4.2f distance %.4f\n",
1116     col.red, col.green, col.blue, dist/2.0);
1117     fprintf (f, "}\n\n");
1118     break;
1119     }
1120     }
1121    
1122    
1123     void write_camera (FILE *f, char *name, Vector pos, Vector target,
1124     float lens, float bank)
1125     {
1126     float fov;
1127    
1128     cameras++;
1129    
1130     fov = findfov (lens);
1131    
1132     switch (format) {
1133     case POV10:
1134     /* Comment out multiple cameras */
1135     if (cameras > 1)
1136     fprintf (f, "/*\n");
1137    
1138     fprintf (f, "/* Camera: %s */\n", name);
1139     fprintf (f, "camera {\n");
1140     fprintf (f, " location <%.4f %.4f %.4f>\n",
1141     pos[X], pos[Y], pos[Z]);
1142     fprintf (f, " direction <0 %.3f 0>\n", 0.60/tan(0.5*RAD(fov)) );
1143     fprintf (f, " up <0 0 1>\n");
1144     fprintf (f, " sky <0 0 1>\n");
1145     fprintf (f, " right <%.3f 0 0>\n", ASPECT);
1146     fprintf (f, " look_at <%.4f %.4f %.4f>\n",
1147     target[X], target[Y], target[Z]);
1148     if (bank != 0.0)
1149     fprintf (f, " /* Bank angle = %.2f */\n", bank);
1150    
1151     fprintf (f, "}\n");
1152    
1153     if (cameras > 1)
1154     fprintf (f, "*/\n");
1155    
1156     fprintf (f, "\n");
1157     break;
1158    
1159     case POV20:
1160     /* Comment out multiple cameras */
1161     if (cameras > 1)
1162     fprintf (f, "/*\n");
1163    
1164     fprintf (f, "/* Camera: %s */\n", name);
1165     fprintf (f, "camera {\n");
1166     fprintf (f, " location <%.4f, %.4f, %.4f>\n",
1167     pos[X], pos[Y], pos[Z]);
1168     fprintf (f, " direction <0, %.3f, 0>\n", 0.60/tan(0.5*RAD(fov)) );
1169     fprintf (f, " up <0, 0, 1>\n");
1170     fprintf (f, " sky <0, 0, 1>\n");
1171     fprintf (f, " right <%.3f, 0, 0>\n", ASPECT);
1172     fprintf (f, " look_at <%.4f, %.4f, %.4f>\n",
1173     target[X], target[Y], target[Z]);
1174     if (bank != 0.0)
1175     fprintf (f, " /* Bank angle = %.2f */\n", bank);
1176    
1177     fprintf (f, "}\n");
1178    
1179     if (cameras > 1)
1180     fprintf (f, "*/\n");
1181    
1182     fprintf (f, "\n");
1183     break;
1184    
1185     case VIVID:
1186     fprintf (f, "/* Camera: %s */\n", name);
1187    
1188     if (cameras > 1)
1189     fprintf (f, "/*\n");
1190    
1191     fprintf (f, "studio {\n");
1192     fprintf (f, " from %.4f %.4f %.4f\n",
1193     pos[X], pos[Y], pos[Z]);
1194     fprintf (f, " at %.4f %.4f %.4f\n",
1195     target[X], target[Y], target[Z]);
1196     fprintf (f, " up 0 0 1\n");
1197     fprintf (f, " angle %.2f\n", 1.1*fov);
1198     fprintf (f, " aspect %.3f\n", ASPECT);
1199     fprintf (f, " resolution 320 200\n");
1200     fprintf (f, " antialias none\n");
1201     fprintf (f, "}\n");
1202    
1203     if (cameras > 1)
1204     fprintf (f, "*/\n");
1205    
1206     fprintf (f, "\n");
1207     break;
1208    
1209     case POLYRAY:
1210     if (cameras == 1) {
1211     fprintf (f, "// Camera: %s\n", name);
1212     fprintf (f, "viewpoint {\n");
1213     fprintf (f, " from <%.4f, %.4f, %.4f>\n",
1214     pos[X], pos[Y], pos[Z]);
1215     fprintf (f, " at <%.4f, %.4f, %.4f>\n",
1216     target[X], target[Y], target[Z]);
1217     fprintf (f, " up <0, 0, 1>\n");
1218     fprintf (f, " angle %.2f\n", 0.85*fov);
1219     fprintf (f, " aspect %.3f\n", -(ASPECT));
1220     fprintf (f, " resolution 320, 200\n");
1221     fprintf (f, "}\n");
1222     }
1223    
1224     fprintf (f, "\n");
1225     break;
1226    
1227     case MGF:
1228     fprintf (f, "# Camera %s\n", name);
1229     fprintf (f, "# from: %.4f %.4f %.4f\n", pos[X], pos[Y], pos[Z]);
1230     fprintf (f, "# to: %.4f %.4f %.4f\n", target[X], target[Y], target[Z]);
1231     fprintf (f, "# lens length: %.2f\n", lens);
1232     fprintf (f, "# bank: %.2f\n", bank);
1233     break;
1234     }
1235     }
1236    
1237    
1238     void write_material (FILE *f, char *mat)
1239     {
1240     MatProp *mprop = LIST_FIND (mprop_list, mat);
1241    
1242     if (mprop == NULL) {
1243     mprop = &DefaultMaterial;
1244     (void)strcpy(mprop->name, mat);
1245     }
1246    
1247     switch (format) {
1248     case POV10:
1249     write_pov10_material (f, mprop);
1250     break;
1251    
1252     case POV20:
1253     write_pov20_material (f, mprop);
1254     break;
1255    
1256     case VIVID:
1257     write_vivid_material (f, mprop);
1258     break;
1259    
1260     case POLYRAY:
1261     write_polyray_material (f, mprop);
1262     break;
1263    
1264     case MGF:
1265     write_mgf_material (f, mprop);
1266     break;
1267     }
1268     }
1269    
1270    
1271     void write_pov10_material (FILE *f, MatProp *m)
1272     {
1273     float amb = 0.1, dif = 0.9, spec = 1.0;
1274     float dist_white, dist_diff, phong, phong_size;
1275     float red, green, blue;
1276    
1277     /* amb = get_ambient (m); */
1278    
1279     if (m->self_illum) {
1280     amb = 0.9;
1281     dif = 0.1;
1282     }
1283    
1284     dist_white = fabs(1.0 - m->specular.red) +
1285     fabs(1.0 - m->specular.green) +
1286     fabs(1.0 - m->specular.blue);
1287    
1288     dist_diff = fabs(m->diffuse.red - m->specular.red) +
1289     fabs(m->diffuse.green - m->specular.green) +
1290     fabs(m->diffuse.blue - m->specular.blue);
1291    
1292    
1293     phong_size = 0.7*m->shininess;
1294     if (phong_size < 1.0) phong_size = 1.0;
1295    
1296     if (phong_size > 30.0)
1297     phong = 1.0;
1298     else
1299     phong = phong_size/30.0;
1300    
1301     fprintf (f, "#declare %s = texture {\n", m->name);
1302     fprintf (f, " ambient %.2f\n", amb);
1303     fprintf (f, " diffuse %.2f\n", dif);
1304     fprintf (f, " phong %.2f\n", phong);
1305     fprintf (f, " phong_size %.1f\n", phong_size);
1306    
1307     if (dist_diff < dist_white)
1308     fprintf (f, " metallic\n");
1309    
1310     if (m->reflection > 0.0) {
1311     spec = (m->specular.red + m->specular.green + m->specular.blue)/3.0;
1312     fprintf (f, " reflection %.3f\n", spec * m->reflection);
1313     }
1314    
1315     if (m->transparency > 0.0) {
1316     red = m->diffuse.red;
1317     green = m->diffuse.green;
1318     blue = m->diffuse.blue;
1319    
1320     /* Saturate the colour towards white as the transparency increases */
1321     red = ((1.0 - m->transparency) * red) + m->transparency;
1322     green = ((1.0 - m->transparency) * green) + m->transparency;
1323     blue = ((1.0 - m->transparency) * blue) + m->transparency;
1324    
1325     fprintf (f, " color red %.3f green %.3f blue %.3f alpha %.3f\n",
1326     red, green, blue, m->transparency);
1327     fprintf (f, " ior 1.1\n");
1328     fprintf (f, " refraction 1.0\n");
1329     }
1330     else
1331     fprintf (f, " color red %.3f green %.3f blue %.3f\n",
1332     m->diffuse.red, m->diffuse.green, m->diffuse.blue);
1333    
1334     if (strlen (m->tex_map) > 0) {
1335     fprintf (f, " /* Image map: %s, Strength: %.2f */\n",
1336     m->tex_map, m->tex_strength);
1337     }
1338    
1339     if (strlen (m->bump_map) > 0) {
1340     fprintf (f, " /* Bump map: %s, Strength: %.2f */\n",
1341     m->bump_map, m->bump_strength);
1342     }
1343    
1344     fprintf (f, "}\n\n");
1345     }
1346    
1347    
1348     void write_pov20_material (FILE *f, MatProp *m)
1349     {
1350     float amb = 0.1, dif = 0.9, spec = 1.0;
1351     float dist_white, dist_diff, phong, phong_size;
1352     float red, green, blue;
1353    
1354     /* amb = get_ambient (m); */
1355    
1356     if (m->self_illum) {
1357     amb = 0.9;
1358     dif = 0.1;
1359     }
1360    
1361     dist_white = fabs(1.0 - m->specular.red) +
1362     fabs(1.0 - m->specular.green) +
1363     fabs(1.0 - m->specular.blue);
1364    
1365     dist_diff = fabs(m->diffuse.red - m->specular.red) +
1366     fabs(m->diffuse.green - m->specular.green) +
1367     fabs(m->diffuse.blue - m->specular.blue);
1368    
1369     phong_size = 0.7*m->shininess;
1370     if (phong_size < 1.0) phong_size = 1.0;
1371    
1372     if (phong_size > 30.0)
1373     phong = 1.0;
1374     else
1375     phong = phong_size/30.0;
1376    
1377     fprintf (f, "#declare %s = texture {\n", m->name);
1378     fprintf (f, " finish {\n");
1379     fprintf (f, " ambient %.2f\n", amb);
1380     fprintf (f, " diffuse %.2f\n", dif);
1381     fprintf (f, " phong %.2f\n", phong);
1382     fprintf (f, " phong_size %.1f\n", phong_size);
1383    
1384     if (dist_diff < dist_white)
1385     fprintf (f, " metallic\n");
1386    
1387     if (m->reflection > 0.0) {
1388     spec = (m->specular.red + m->specular.green + m->specular.blue)/3.0;
1389     fprintf (f, " reflection %.3f\n", spec * m->reflection);
1390     }
1391    
1392     if (m->transparency > 0.0) {
1393     fprintf (f, " ior 1.1\n");
1394     fprintf (f, " refraction 1.0\n");
1395     }
1396    
1397     fprintf (f, " }\n");
1398    
1399     if (m->transparency > 0.0) {
1400     red = m->diffuse.red;
1401     green = m->diffuse.green;
1402     blue = m->diffuse.blue;
1403    
1404     /* Saturate the colour towards white as the transparency increases */
1405     red = ((1.0 - m->transparency) * red) + m->transparency;
1406     green = ((1.0 - m->transparency) * green) + m->transparency;
1407     blue = ((1.0 - m->transparency) * blue) + m->transparency;
1408    
1409     fprintf (f, " pigment { rgbf <%.3f, %.3f, %.3f, %.3f> }\n",
1410     red, green, blue, m->transparency);
1411     }
1412     else
1413     fprintf (f, " pigment { rgb <%.3f, %.3f, %.3f> }\n",
1414     m->diffuse.red, m->diffuse.green, m->diffuse.blue);
1415    
1416     if (strlen (m->tex_map) > 0) {
1417     fprintf (f, " /* Image map: %s, Strength: %.2f */\n",
1418     m->tex_map, m->tex_strength);
1419     }
1420    
1421     if (strlen (m->bump_map) > 0) {
1422     fprintf (f, " /* Bump map: %s, Strength: %.2f */\n",
1423     m->bump_map, m->bump_strength);
1424     }
1425    
1426     fprintf (f, "}\n\n");
1427     }
1428    
1429    
1430     void write_vivid_material (FILE *f, MatProp *m)
1431     {
1432     float amb = 0.1, dif = 0.9;
1433    
1434     /* amb = get_ambient (m); */
1435    
1436     if (m->self_illum) {
1437     amb = 0.9;
1438     dif = 0.1;
1439     }
1440    
1441     if (m->transparency > 0.0) {
1442     dif = dif - m->transparency;
1443     if (dif < 0.0) dif = 0.0;
1444     }
1445    
1446     fprintf (f, "#define %s \\ \n", m->name);
1447     fprintf (f, " surface { \\ \n");
1448     fprintf (f, " ambient %.3f %.3f %.3f \\ \n",
1449     amb*m->ambient.red, amb*m->ambient.green, amb*m->ambient.blue);
1450    
1451     fprintf (f, " diffuse %.3f %.3f %.3f \\ \n",
1452     dif*m->diffuse.red, dif*m->diffuse.green, dif*m->diffuse.blue);
1453    
1454     fprintf (f, " shine %.1f %.3f %.3f %.3f \\ \n",
1455     0.7*m->shininess, m->specular.red, m->specular.green, m->specular.blue);
1456    
1457     if (m->transparency > 0.0) {
1458     fprintf (f, " transparent %.3f*white \\ \n", 1.0 - (1.0 - m->transparency)/14.0);
1459     fprintf (f, " ior 1.1 \\ \n");
1460     }
1461    
1462     if (m->reflection > 0.0)
1463     fprintf (f, " specular %.3f*white \\ \n", m->reflection);
1464    
1465     if (strlen (m->tex_map) > 0) {
1466     fprintf (f, " /* Image map: %s, Strength: %.2f */ \\ \n",
1467     m->tex_map, m->tex_strength);
1468     }
1469    
1470     if (strlen (m->bump_map) > 0) {
1471     fprintf (f, " /* Bump map: %s, Strength: %.2f */ \\ \n",
1472     m->bump_map, m->bump_strength);
1473     }
1474    
1475     fprintf (f, " }\n\n");
1476     }
1477    
1478    
1479     void write_polyray_material (FILE *f, MatProp *m)
1480     {
1481     float amb = 0.1, dif = 0.9, spec;
1482    
1483     /* amb = get_ambient (m); */
1484    
1485     if (m->self_illum) {
1486     amb = 0.9;
1487     dif = 0.1;
1488     }
1489    
1490     if (m->transparency > 0.0) {
1491     dif = dif - m->transparency;
1492     if (dif < 0.0) dif = 0.0;
1493     }
1494    
1495     if (m->shininess == 0.0)
1496     m->shininess = 0.1;
1497    
1498     if (m->shininess > 40.0)
1499     spec = 1.0;
1500     else
1501     spec = m->shininess/40.0;
1502    
1503     fprintf (f, "define %s\n", m->name);
1504     fprintf (f, "texture {\n");
1505     fprintf (f, " surface {\n");
1506     fprintf (f, " ambient <%.3f, %.3f, %.3f>, %.1f\n",
1507     m->ambient.red, m->ambient.green, m->ambient.blue, amb);
1508    
1509     fprintf (f, " diffuse <%.3f, %.3f, %.3f>, %.1f\n",
1510     m->diffuse.red, m->diffuse.green, m->diffuse.blue, dif);
1511    
1512     fprintf (f, " specular <%.3f, %.3f, %.3f>, %.2f\n",
1513     m->specular.red, m->specular.green, m->specular.blue, spec);
1514    
1515     fprintf (f, " microfacet Reitz %.1f\n", 400.0/m->shininess);
1516    
1517     if (m->transparency > 0.0)
1518     fprintf (f, " transmission %.3f, 1.1\n", m->transparency);
1519    
1520     if (m->reflection > 0.0)
1521     fprintf (f, " reflection %.3f\n", m->reflection);
1522    
1523     if (strlen (m->tex_map) > 0) {
1524     fprintf (f, " // Image map: %s, Strength: %.2f\n",
1525     m->tex_map, m->tex_strength);
1526     }
1527    
1528     if (strlen (m->bump_map) > 0) {
1529     fprintf (f, " // Bump map: %s, Strength: %.2f\n",
1530     m->bump_map, m->bump_strength);
1531     }
1532    
1533     fprintf (f, " }\n");
1534     fprintf (f, "}\n\n");
1535     }
1536    
1537    
1538     void write_mgf_material (FILE *f, MatProp *m)
1539     {
1540     float dmag, smag, rdmag, rsmag, tdmag, tsmag, total;
1541    
1542     fprintf (f, "m %s =\n", m->name);
1543     fprintf (f, "\tsides %d\n", m->two_side ? 2 : 1);
1544     dmag = CIE_Y_r*m->diffuse.red + CIE_Y_g*m->diffuse.green
1545     + CIE_Y_b*m->diffuse.blue;
1546     smag = CIE_Y_r*m->specular.red + CIE_Y_g*m->specular.green
1547     + CIE_Y_b*m->specular.blue;
1548     rdmag = dmag;
1549     rsmag = smag * m->reflection;
1550     tdmag = 0.0;
1551     tsmag = m->transparency;
1552     total = rdmag + rsmag + tdmag + tsmag;
1553     if (total > 0.99) {
1554     total = 0.9/total;
1555     dmag *= total;
1556     smag *= total;
1557     rdmag *= total;
1558     rsmag *= total;
1559     tdmag *= total;
1560     tsmag *= total;
1561     total = 0.9;
1562     }
1563     if (dmag > 0.005) {
1564     fprintf (f, "\tc\n\t\tcmix %.3f R %.3f G %.3f B\n",
1565     CIE_Y_r*m->diffuse.red,
1566     CIE_Y_g*m->diffuse.green,
1567     CIE_Y_b*m->diffuse.blue);
1568     if (rdmag > 0.005)
1569     fprintf (f, "\trd %.4f\n", rdmag);
1570     if (tdmag > 0.005)
1571     fprintf (f, "\ttd %.4f\n", tdmag);
1572     if (m->self_illum)
1573     fprintf (f, "\ted %.4f\n", dmag);
1574     }
1575     if (m->shininess > 1.1 && rsmag > 0.005) {
1576     fprintf (f, "\tc\n\t\tcmix %.3f R %.3f G %.3f B\n",
1577     CIE_Y_r*m->specular.red,
1578     CIE_Y_g*m->specular.green,
1579     CIE_Y_b*m->specular.blue);
1580     fprintf (f, "\trs %.4f %.4f\n", rsmag, 0.6/sqrt(m->shininess));
1581     }
1582     if (tsmag > 0.005)
1583     fprintf (f, "\tc\n\tts %.4f 0\n", tsmag);
1584    
1585     if (strlen (m->tex_map) > 0) {
1586     fprintf (f, "# image map: %s, strength: %.2f\n",
1587     m->tex_map, m->tex_strength);
1588     }
1589    
1590     if (strlen (m->bump_map) > 0) {
1591     fprintf (f, "# bump map: %s, strength: %.2f\n",
1592     m->bump_map, m->bump_strength);
1593     }
1594    
1595     fprintf (f, "\n");
1596     }
1597    
1598    
1599     /* Write a mesh file */
1600     void write_mesh (FILE *f, Mesh *mesh)
1601     {
1602     int i;
1603     char curmat[80];
1604     Vector va, vb, vc;
1605     Summary *new_summary;
1606     Matrix obj_matrix;
1607    
1608     if (mesh->hidden || LIST_FIND (excl_list, mesh->name))
1609     return;
1610    
1611     /* Add this object's stats to the summary */
1612     new_summary = malloc (sizeof(*new_summary));
1613     if (new_summary == NULL)
1614     abortmsg ("Out of memory adding summary", 1);
1615    
1616     strcpy (new_summary->name, mesh->name);
1617     vect_copy (new_summary->center, mesh->center);
1618     vect_copy (new_summary->lengths, mesh->lengths);
1619    
1620     LIST_INSERT (summary, new_summary);
1621    
1622     /* Compute the object transformation matrix for animations */
1623     if (ani_matrix != NULL) {
1624     mat_copy (obj_matrix, *ani_matrix);
1625     if (vue_version > 2.0)
1626     mat_mult (obj_matrix, mesh->invmatrix, obj_matrix);
1627     }
1628    
1629     switch (format) {
1630     case MGF:
1631     if (no_opt) {
1632     if (mesh->name[0]) fprintf (meshf, "o %s\n", mesh->name);
1633     for (i = 0; i < mesh->vertices; i++) {
1634     vect_copy(va, mesh->vertex[i]);
1635     if (ani_matrix != NULL)
1636     vect_transform (va, va, obj_matrix);
1637     fprintf (meshf, "v v%d =\n\tp %.5f %.5f %.5f\n",
1638     i, va[X], va[Y], va[Z]);
1639     }
1640     curmat[0] = '\0';
1641     for (i = 0; i < mesh->faces; i++) {
1642     if (strcmp(mesh->mtl[i]->name, curmat)) {
1643     strcpy(curmat, mesh->mtl[i]->name);
1644     fprintf (meshf, "m %s\n", curmat);
1645     }
1646     fprintf (meshf, "f v%d v%d v%d\n", mesh->face[i].a,
1647     mesh->face[i].b, mesh->face[i].c);
1648     }
1649     if (mesh->name[0]) fprintf (meshf, "o\n");
1650     break;
1651     }
1652     /*FALL THROUGH*/
1653     case POV10:
1654     case POV20:
1655     case VIVID:
1656     case POLYRAY:
1657     opt_set_vert (mesh->vertices);
1658    
1659     for (i = 0; i < mesh->faces; i++) {
1660     vect_copy (va, mesh->vertex[mesh->face[i].a]);
1661     vect_copy (vb, mesh->vertex[mesh->face[i].b]);
1662     vect_copy (vc, mesh->vertex[mesh->face[i].c]);
1663    
1664     opt_set_texture (mesh->mtl[i]->name);
1665    
1666     opt_add_tri (va[X], va[Y], va[Z], vc[X], vc[Y], vc[Z],
1667     vb[X], vb[Y], vb[Z]);
1668     }
1669    
1670     fflush (f);
1671    
1672     if (ani_matrix != NULL)
1673     opt_set_transform (obj_matrix);
1674    
1675     if (box_all || LIST_FIND (box_list, mesh->name))
1676     opt_write_box (mesh->name);
1677     else
1678     opt_write_file (mesh->name);
1679    
1680     break;
1681    
1682     case RAW:
1683     fprintf (f, "%s\n", mesh->name);
1684    
1685     for (i = 0; i < mesh->faces; i++) {
1686     vect_copy (va, mesh->vertex[mesh->face[i].a]);
1687     vect_copy (vb, mesh->vertex[mesh->face[i].b]);
1688     vect_copy (vc, mesh->vertex[mesh->face[i].c]);
1689    
1690     if (ani_matrix != NULL) {
1691     vect_transform (va, va, obj_matrix);
1692     vect_transform (vb, vb, obj_matrix);
1693     vect_transform (vc, vc, obj_matrix);
1694     }
1695    
1696     fprintf (f, "%f %f %f %f %f %f %f %f %f\n",
1697     va[X], va[Y], va[Z], vb[X], vb[Y], vb[Z],
1698     vc[X], vc[Y], vc[Z]);
1699     }
1700    
1701     break;
1702     }
1703     }
1704    
1705    
1706     /* Parses an object transformation and returns a pointer to the
1707     newly allocated transformation */
1708     Transform *parse_transform (char *string)
1709     {
1710     Transform *t;
1711     char *token;
1712     int token_no;
1713    
1714     t = (Transform *)malloc (sizeof(*t));
1715     if (t == NULL)
1716     abortmsg ("Out of memory allocating transform", 1);
1717    
1718     mat_identity (t->matrix);
1719    
1720     token = parse_string (string);
1721     token_no = 0;
1722    
1723     while (strlen(token) > 0) {
1724     switch (token_no) {
1725     case 0: break;
1726     case 1: strcpy (t->name, token); break;
1727     case 2: t->matrix[0][0] = atof(token); break;
1728     case 3: t->matrix[0][1] = atof(token); break;
1729     case 4: t->matrix[0][2] = atof(token); break;
1730     case 5: t->matrix[1][0] = atof(token); break;
1731     case 6: t->matrix[1][1] = atof(token); break;
1732     case 7: t->matrix[1][2] = atof(token); break;
1733     case 8: t->matrix[2][0] = atof(token); break;
1734     case 9: t->matrix[2][1] = atof(token); break;
1735     case 10: t->matrix[2][2] = atof(token); break;
1736     case 11: t->matrix[3][0] = atof(token); break;
1737     case 12: t->matrix[3][1] = atof(token); break;
1738     case 13: t->matrix[3][2] = atof(token); break;
1739    
1740     default: abortmsg ("Error parsing transform", 1);
1741     }
1742    
1743     token = parse_string (NULL);
1744     token_no++;
1745     }
1746    
1747     t->matrix[0][3] = 0.0;
1748     t->matrix[1][3] = 0.0;
1749     t->matrix[2][3] = 0.0;
1750     t->matrix[3][3] = 1.0;
1751    
1752     cleanup_name (t->name);
1753    
1754     return t;
1755     }
1756    
1757    
1758     /* Parses a morph command and returns a pointer to the
1759     newly allocated morph */
1760     Morph *parse_morph (char *string)
1761     {
1762     Morph *m;
1763     char *token;
1764     int i, token_no;
1765    
1766     m = (Morph *)malloc (sizeof(*m));
1767     if (m == NULL)
1768     abortmsg ("Out of memory allocating morph", 1);
1769    
1770     mat_identity (m->matrix);
1771    
1772     token = parse_string (string);
1773    
1774     token = parse_string (NULL);
1775     strcpy (m->name, token);
1776    
1777     token = parse_string (NULL);
1778     m->count = atoi (token);
1779    
1780     if (strlen (m->name) == 0 || m->count < 1 || m->count > 4)
1781     abortmsg ("Error parsing morph command", 1);
1782    
1783     cleanup_name (m->name);
1784    
1785     for (i = 0; i < m->count; i++) {
1786     token = parse_string (NULL);
1787     strcpy (m->names[i], token);
1788    
1789     token = parse_string (NULL);
1790     m->weight[i] = atof (token);
1791    
1792     if (strlen (m->names[i]) == 0)
1793     abortmsg ("Error parsing morph command", 1);
1794    
1795     cleanup_name (m->names[i]);
1796     }
1797    
1798     token = parse_string (NULL);
1799     token_no = 0;
1800    
1801     while (strlen(token) > 0) {
1802     switch (token_no) {
1803     case 0: m->matrix[0][0] = atof(token); break;
1804     case 1: m->matrix[0][1] = atof(token); break;
1805     case 2: m->matrix[0][2] = atof(token); break;
1806     case 3: m->matrix[1][0] = atof(token); break;
1807     case 4: m->matrix[1][1] = atof(token); break;
1808     case 5: m->matrix[1][2] = atof(token); break;
1809     case 6: m->matrix[2][0] = atof(token); break;
1810     case 7: m->matrix[2][1] = atof(token); break;
1811     case 8: m->matrix[2][2] = atof(token); break;
1812     case 9: m->matrix[3][0] = atof(token); break;
1813     case 10: m->matrix[3][1] = atof(token); break;
1814     case 11: m->matrix[3][2] = atof(token); break;
1815    
1816     default: abortmsg ("Error parsing morph command", 1);
1817     }
1818    
1819     token = parse_string (NULL);
1820     token_no++;
1821     }
1822    
1823     m->matrix[0][3] = 0.0;
1824     m->matrix[1][3] = 0.0;
1825     m->matrix[2][3] = 0.0;
1826     m->matrix[3][3] = 1.0;
1827    
1828     return m;
1829     }
1830    
1831    
1832     /* Parses an omni light and returns a pointer to the
1833     newly allocated light */
1834     OmniLight *parse_omnilight (char *string)
1835     {
1836     OmniLight *o;
1837     char *token;
1838     int token_no;
1839    
1840     o = (OmniLight *)malloc (sizeof(*o));
1841     if (o == NULL)
1842     abortmsg ("Out of memory allocating omnilight", 1);
1843    
1844     token = parse_string (string);
1845     token_no = 0;
1846    
1847     while (strlen(token) > 0) {
1848     switch (token_no) {
1849     case 0: break;
1850     case 1: strcpy (o->name, token); break;
1851     case 2: o->pos[X] = atof (token); break;
1852     case 3: o->pos[Y] = atof (token); break;
1853     case 4: o->pos[Z] = atof (token); break;
1854     case 5: o->col.red = atof (token); break;
1855     case 6: o->col.green = atof (token); break;
1856     case 7: o->col.blue = atof (token); break;
1857    
1858     default: abortmsg ("Error parsing omnilight", 1);
1859     }
1860    
1861     token = parse_string (NULL);
1862     token_no++;
1863     }
1864    
1865     cleanup_name (o->name);
1866    
1867     return o;
1868     }
1869    
1870    
1871     /* Parses a spotlight and returns a pointer to the
1872     newly allocated spotlight */
1873     Spotlight *parse_spotlight (char *string)
1874     {
1875     Spotlight *s;
1876     char *token;
1877     int token_no;
1878    
1879     s = (Spotlight *)malloc (sizeof(*s));
1880     if (s == NULL)
1881     abortmsg ("Out of memory allocating spotlight", 1);
1882    
1883     token = parse_string (string);
1884     token_no = 0;
1885    
1886     while (strlen(token) > 0) {
1887     switch (token_no) {
1888     case 0: break;
1889     case 1: strcpy (s->name, token); break;
1890     case 2: s->pos[X] = atof (token); break;
1891     case 3: s->pos[Y] = atof (token); break;
1892     case 4: s->pos[Z] = atof (token); break;
1893     case 5: s->target[X] = atof (token); break;
1894     case 6: s->target[Y] = atof (token); break;
1895     case 7: s->target[Z] = atof (token); break;
1896     case 8: s->col.red = atof (token); break;
1897     case 9: s->col.green = atof (token); break;
1898     case 10: s->col.blue = atof (token); break;
1899     case 11: s->hotspot = atof (token); break;
1900     case 12: s->falloff = atof (token); break;
1901     case 13: break;
1902    
1903     default: abortmsg ("Error parsing spotlight", 1);
1904     }
1905    
1906     token = parse_string (NULL);
1907     token_no++;
1908     }
1909    
1910     cleanup_name (s->name);
1911    
1912     return s;
1913     }
1914    
1915    
1916     /* Parses a camera command and returns a pointer to the
1917     newly allocated camera */
1918     Camera *parse_camera (char *string)
1919     {
1920     Camera *c;
1921     char *token;
1922     int token_no;
1923    
1924     c = (Camera *)malloc (sizeof(*c));
1925     if (c == NULL)
1926     abortmsg ("Out of memory allocating camera", 1);
1927    
1928     token = parse_string (string);
1929     token_no = 0;
1930    
1931     while (strlen(token) > 0) {
1932     switch (token_no) {
1933     case 0: break;
1934     case 1: c->pos[X] = atof (token); break;
1935     case 2: c->pos[Y] = atof (token); break;
1936     case 3: c->pos[Z] = atof (token); break;
1937     case 4: c->target[X] = atof (token); break;
1938     case 5: c->target[Y] = atof (token); break;
1939     case 6: c->target[Z] = atof (token); break;
1940     case 7: c->bank = atof (token); break;
1941     case 8: c->lens = atof (token); break;
1942    
1943     default: abortmsg ("Error parsing camera", 1);
1944     }
1945    
1946     token = parse_string (NULL);
1947     token_no++;
1948     }
1949    
1950     return c;
1951     }
1952    
1953    
1954     /* Load the transforms, camera movements, etc for the specified frame */
1955     void read_frame (char *filename, int frame_no)
1956     {
1957     FILE *f;
1958     char fname[80];
1959     char string[256];
1960     char *token;
1961    
1962     /* Open the .vue file */
1963     strcpy (fname, filename); /* Make a copy we can mess with */
1964     add_ext (fname, "vue", 0);
1965    
1966     f = fopen (fname, "r");
1967     if (f == NULL) {
1968     printf ("Error opening file '%s'\n", fname);
1969     exit(1);
1970     }
1971    
1972     /* Load the specified frame */
1973     find_frame (f, frame_no);
1974    
1975     while (fgets (string, 256, f) != NULL) {
1976     token = parse_string (string);
1977    
1978     if (strcmp (token, "frame") == 0)
1979     break;
1980     else if (strcmp (token, "transform") == 0) {
1981     LIST_INSERT (trans_list, parse_transform (string));
1982     }
1983     else if (strcmp (token, "morph") == 0) {
1984     LIST_INSERT (morph_list, parse_morph (string));
1985     }
1986     else if (strcmp (token, "light") == 0) {
1987     LIST_INSERT (omni_list, parse_omnilight (string));
1988     }
1989     else if (strcmp (token, "spotlight") == 0) {
1990     LIST_INSERT (spot_list, parse_spotlight (string));
1991     }
1992     else if (strcmp (token, "camera") == 0) {
1993     if (cam_list != NULL)
1994     abortmsg ("ERROR - Multiple cameras in .vue file", 1);
1995    
1996     LIST_INSERT (cam_list, parse_camera (string));
1997     }
1998     else if (strcmp (token, "top") == 0)
1999     abortmsg ("ERROR - Orthogonal viewports are not supported", 1);
2000     else if (strcmp (token, "bottom") == 0)
2001     abortmsg ("ERROR - Orthogonal viewports are not supported", 1);
2002     else if (strcmp (token, "left") == 0)
2003     abortmsg ("ERROR - Orthogonal viewports are not supported", 1);
2004     else if (strcmp (token, "right") == 0)
2005     abortmsg ("ERROR - Orthogonal viewports are not supported", 1);
2006     else if (strcmp (token, "front") == 0)
2007     abortmsg ("ERROR - Orthogonal viewports are not supported", 1);
2008     else if (strcmp (token, "back") == 0)
2009     abortmsg ("ERROR - Orthogonal viewports are not supported", 1);
2010     else if (strcmp (token, "user") == 0)
2011     abortmsg ("ERROR - User viewports are not supported", 1);
2012     }
2013    
2014     fclose(f);
2015     }
2016    
2017    
2018     void find_frame (FILE *f, int frame_no)
2019     {
2020     char string[256];
2021     char *token;
2022     int frame = 0;
2023    
2024     /* Search the .vue file for the required frame */
2025     while (1) {
2026     /* Read the next line in the file */
2027     if (fgets (string, 256, f) == NULL) {
2028     printf ("Unable to locate frame #%d in .vue file\n", frame_no);
2029     exit(1);
2030     }
2031    
2032     token = parse_string (string);
2033    
2034     if (strcmp (token, "frame") == 0) {
2035     token = parse_string (NULL);
2036    
2037     if (strlen(token) == 0) {
2038     printf ("Unable to locate frame #%d in .vue file\n", frame_no);
2039     exit(1);
2040     }
2041    
2042     frame = atoi (token);
2043    
2044     if (frame == frame_no)
2045     break;
2046     }
2047     else if (strcmp (token, "VERSION") == 0) {
2048     token = parse_string (NULL);
2049    
2050     vue_version = atoi(token) / 100.0;
2051     }
2052     }
2053     }
2054    
2055    
2056     void save_animation()
2057     {
2058     Mesh *mesh, *master;
2059     Transform *t;
2060     Morph *m;
2061     Vector temp;
2062     int i, j;
2063    
2064     printf ("\n");
2065    
2066     for (t = trans_list; t != NULL; t = t->next) {
2067     printf ("Transforming object: %s\n", t->name);
2068    
2069     ani_matrix = &(t->matrix);
2070    
2071     mesh = LIST_FIND (mesh_list, t->name);
2072    
2073     if (mesh == NULL) {
2074     printf ("Unable to locate mesh object %s\n", t->name);
2075     exit(1);
2076     }
2077    
2078     write_mesh (out, mesh);
2079     }
2080    
2081     for (m = morph_list; m != NULL; m = m->next) {
2082     printf ("Morphing object: %s\n", m->name);
2083    
2084     ani_matrix = &(m->matrix);
2085    
2086     mesh = LIST_FIND (mesh_list, m->name);
2087     if (mesh == NULL) {
2088     printf ("Unable to locate mesh object %s\n", m->name);
2089     exit(1);
2090     }
2091    
2092     /* Make a copy to mess with */
2093     master = copy_mesh (mesh);
2094     master->hidden = FALSE;
2095    
2096     strcpy (master->name, m->name);
2097    
2098     for (i = 0; i < master->vertices; i++)
2099     vect_init (master->vertex[i], 0.0, 0.0, 0.0);
2100    
2101     for (i = 0; i < m->count; i++) {
2102     mesh = LIST_FIND (mesh_list, m->names[i]);
2103     if (mesh == NULL) {
2104     printf ("Unable to locate mesh object %s\n", m->names[0]);
2105     exit(1);
2106     }
2107    
2108     if (mesh->vertices != master->vertices)
2109     abortmsg ("Morphed objects do not contain the same number of vertices", 1);
2110    
2111     if (mesh->faces != master->faces)
2112     abortmsg ("Morphed objects do not contain the same number of faces", 1);
2113    
2114     for (j = 0; j < master->vertices; j++) {
2115     vect_transform (temp, mesh->vertex[j], mesh->invmatrix);
2116     vect_scale (temp, temp, m->weight[i]);
2117     vect_add (master->vertex[j], master->vertex[j], temp);
2118     }
2119     }
2120    
2121     for (i = 0; i < master->vertices; i++)
2122     vect_transform (master->vertex[i], master->vertex[i], master->matrix);
2123    
2124     write_mesh (out, master);
2125    
2126     free_mesh_data (master);
2127     free (master);
2128     }
2129    
2130     for (mesh = mesh_list; mesh != NULL; mesh = mesh->next)
2131     free_mesh_data (mesh);
2132     }
2133    
2134    
2135     /* Create a new mesh */
2136     Mesh *create_mesh (char *name, int vertices, int faces)
2137     {
2138     Mesh *new_mesh;
2139    
2140     new_mesh = malloc (sizeof(*new_mesh));
2141     if (new_mesh == NULL)
2142     abortmsg ("Out of memory allocating mesh", 1);
2143    
2144     strcpy (new_mesh->name, name);
2145    
2146     new_mesh->vertices = vertices;
2147    
2148     if (vertices <= 0)
2149     new_mesh->vertex = NULL;
2150     else {
2151     new_mesh->vertex = malloc (vertices * sizeof(*new_mesh->vertex));
2152     if (new_mesh->vertex == NULL)
2153     abortmsg ("Out of memory allocating mesh", 1);
2154     }
2155    
2156     new_mesh->faces = faces;
2157    
2158     if (faces <= 0) {
2159     new_mesh->face = NULL;
2160     new_mesh->mtl = NULL;
2161     }
2162     else {
2163     new_mesh->face = malloc (faces * sizeof(*new_mesh->face));
2164     if (new_mesh->face == NULL)
2165     abortmsg ("Out of memory allocating mesh", 1);
2166    
2167     new_mesh->mtl = malloc (faces * sizeof(*new_mesh->mtl));
2168     if (new_mesh->mtl == NULL)
2169     abortmsg ("Out of memory allocating mesh", 1);
2170     }
2171    
2172     vect_init (new_mesh->center, 0.0, 0.0, 0.0);
2173     vect_init (new_mesh->lengths, 0.0, 0.0, 0.0);
2174    
2175     mat_identity (new_mesh->matrix);
2176     mat_identity (new_mesh->invmatrix);
2177    
2178     new_mesh->hidden = FALSE;
2179     new_mesh->shadow = TRUE;
2180    
2181     return new_mesh;
2182     }
2183    
2184    
2185     /* Creates a duplicate copy of a mesh */
2186     Mesh *copy_mesh (Mesh *mesh)
2187     {
2188     Mesh *new_mesh;
2189     int i;
2190    
2191     new_mesh = create_mesh (mesh->name, mesh->vertices, mesh->faces);
2192    
2193     if (new_mesh == NULL)
2194     abortmsg ("Out of memory allocating mesh", 1);
2195    
2196     for (i = 0; i < mesh->vertices; i++)
2197     vect_copy (new_mesh->vertex[i], mesh->vertex[i]);
2198    
2199     for (i = 0; i < mesh->faces; i++) {
2200     new_mesh->face[i] = mesh->face[i];
2201     new_mesh->mtl[i] = mesh->mtl[i];
2202     }
2203    
2204     mat_copy (new_mesh->matrix, mesh->matrix);
2205     mat_copy (new_mesh->invmatrix, mesh->invmatrix);
2206    
2207     vect_copy (new_mesh->center, mesh->center);
2208     vect_copy (new_mesh->lengths, mesh->lengths);
2209    
2210     new_mesh->hidden = mesh->hidden;
2211     new_mesh->shadow = mesh->shadow;
2212    
2213     return new_mesh;
2214     }
2215    
2216    
2217     /* Free all data associated with mesh object */
2218     void free_mesh_data (Mesh *mesh)
2219     {
2220     if (mesh->vertex != NULL)
2221     free (mesh->vertex);
2222    
2223     if (mesh->face != NULL)
2224     free (mesh->face);
2225    
2226     if (mesh->mtl != NULL)
2227     free (mesh->mtl);
2228     }
2229    
2230    
2231     /* Updates the center (pivot) point of the mesh */
2232     void update_limits (Mesh *mesh)
2233     {
2234     Vector vmin = {+MAXFLOAT, +MAXFLOAT, +MAXFLOAT};
2235     Vector vmax = {-MAXFLOAT, -MAXFLOAT, -MAXFLOAT};
2236     int i;
2237    
2238     for (i = 0; i < mesh->vertices; i++) {
2239     vect_min (vmin, vmin, mesh->vertex[i]);
2240     vect_max (vmax, vmax, mesh->vertex[i]);
2241     }
2242    
2243     vect_add (mesh->center, vmin, vmax);
2244     vect_scale (mesh->center, mesh->center, 0.5);
2245    
2246     vect_sub (mesh->lengths, vmax, vmin);
2247     }
2248    
2249    
2250     /* Return the sub-string of 'str' that is before 'target' */
2251     char *before (char *str, char *target)
2252     {
2253     static char result[256];
2254     char *search;
2255    
2256     strncpy (result, str, 256);
2257     result[255] = '\0';
2258    
2259     search = strstr (result, target);
2260    
2261     if (search != NULL)
2262     *search = '\0';
2263    
2264     return result;
2265     }
2266    
2267    
2268     /* Return the sub-string of 'str' that is after 'target' */
2269     char *after (char *str, char *target)
2270     {
2271     static char result[256];
2272     char *search;
2273    
2274     search = strstr (str, target);
2275    
2276     if (search == NULL)
2277     strncpy (result, "", 256);
2278     else
2279     strncpy (result, search + strlen(target), 256);
2280    
2281     result[255] = '\0';
2282    
2283     return result;
2284     }
2285    
2286    
2287     /* Return the sub-string of 'str' that is between 'target1' and 'target2' */
2288     char *between (char *str, char *target1, char *target2)
2289     {
2290     static char result[256];
2291    
2292     strcpy (result, after (str, target1));
2293     strcpy (result, before (result, target2));
2294    
2295     return result;
2296     }
2297    
2298    
2299     /* Works like the C strtok() function except that it can handle */
2300     /* tokens enclosed in double quotes */
2301     char *parse_string (char *str)
2302     {
2303     static char result[256];
2304     static char *p;
2305     char QUOTE = '\"';
2306     int index;
2307    
2308     strcpy (result, "");
2309     index = 0;
2310    
2311     if (str != NULL)
2312     p = str;
2313    
2314     /* Find the start of the next token */
2315     while (isspace (*p))
2316     p++;
2317    
2318     if (*p == QUOTE) {
2319     p++;
2320    
2321     while (*p != '\0' && *p != QUOTE)
2322     result[index++] = *p++;
2323    
2324     if (*p == QUOTE)
2325     p++;
2326     }
2327     else {
2328     while (*p != '\0' && !isspace(*p))
2329     result[index++] = *p++;
2330     }
2331    
2332     result[index] = '\0';
2333    
2334     return result;
2335     }
2336    
2337    
2338     /* Convert character 'c' to upper case */
2339     char upcase (char c)
2340     {
2341     if (c >= 'a' && c <= 'z')
2342     c = c - 'a' + 'A';
2343    
2344     return c;
2345     }
2346    
2347    
2348     float colour_intens (Colour *colour)
2349     {
2350     return sqrt (colour->red * colour->red +
2351     colour->green * colour->green +
2352     colour->blue * colour->blue);
2353     }
2354    
2355    
2356     void parse_file()
2357     {
2358     Chunk chunk;
2359    
2360     start_chunk(&chunk);
2361    
2362     if (chunk.tag == 0x4D4D)
2363     parse_3ds (&chunk);
2364     else
2365     abortmsg ("Error: Input file is not .3DS format", 1);
2366    
2367     end_chunk (&chunk);
2368     }
2369    
2370    
2371     void parse_3ds (Chunk *mainchunk)
2372     {
2373     Chunk chunk;
2374    
2375     do {
2376     start_chunk (&chunk);
2377     if (feof(in)) {
2378     fprintf(stderr, "%s: unexpected EOF\n", progname);
2379     break;
2380     }
2381     if (chunk.end <= mainchunk->end) {
2382     switch (chunk.tag) {
2383     case 0x3D3D: parse_mdata (&chunk);
2384     break;
2385     }
2386     }
2387    
2388     end_chunk (&chunk);
2389     } while (chunk.end <= mainchunk->end);
2390     }
2391    
2392    
2393     void parse_mdata (Chunk *mainchunk)
2394     {
2395     Chunk chunk;
2396     Colour bgnd_colour;
2397    
2398     do {
2399     start_chunk (&chunk);
2400    
2401     if (chunk.end <= mainchunk->end) {
2402     switch (chunk.tag) {
2403     case 0x2100: parse_colour (&global_amb);
2404     break;
2405     case 0x1200: parse_colour (&bgnd_colour);
2406     break;
2407     case 0x1201: write_bgsolid (out, bgnd_colour);
2408     break;
2409     case 0x2200: parse_fog (&chunk);
2410     break;
2411     case 0x2210: parse_fog_bgnd();
2412     break;
2413     case 0x2201: write_fog (out, fog_colour, fog_distance);
2414     break;
2415     case 0xAFFF: parse_mat_entry (&chunk);
2416     break;
2417     case 0x4000: parse_named_object (&chunk);
2418     break;
2419     }
2420     }
2421    
2422     end_chunk (&chunk);
2423     } while (chunk.end <= mainchunk->end);
2424     }
2425    
2426    
2427     void parse_fog (Chunk *mainchunk)
2428     {
2429     Chunk chunk;
2430    
2431     (void)read_float();
2432     (void)read_float();
2433     fog_distance = read_float();
2434     (void)read_float();
2435    
2436     parse_colour (&fog_colour);
2437    
2438     do {
2439     start_chunk (&chunk);
2440    
2441     if (chunk.end <= mainchunk->end) {
2442     switch (chunk.tag) {
2443     case 0x2210: parse_fog_bgnd();
2444     break;
2445     }
2446     }
2447    
2448     end_chunk (&chunk);
2449     } while (chunk.end <= mainchunk->end);
2450     }
2451    
2452    
2453     void parse_fog_bgnd()
2454     {
2455    
2456     }
2457    
2458    
2459     void parse_mat_entry (Chunk *mainchunk)
2460     {
2461     Chunk chunk;
2462     MatProp *mprop;
2463    
2464     mprop = create_mprop();
2465    
2466     do {
2467     start_chunk (&chunk);
2468    
2469     if (chunk.end <= mainchunk->end) {
2470     switch (chunk.tag) {
2471     case 0xA000: strcpy (mprop->name, read_string());
2472     cleanup_name (mprop->name);
2473     break;
2474    
2475     case 0xA010: parse_colour (&mprop->ambient);
2476     break;
2477    
2478     case 0xA020: parse_colour (&mprop->diffuse);
2479     break;
2480    
2481     case 0xA030: parse_colour (&mprop->specular);
2482     break;
2483    
2484     case 0xA040: mprop->shininess = 100.0*parse_percentage();
2485     break;
2486    
2487     case 0xA050: mprop->transparency = parse_percentage();
2488     break;
2489    
2490     case 0xA080: mprop->self_illum = TRUE;
2491     break;
2492    
2493     case 0xA081: mprop->two_side = TRUE;
2494     break;
2495    
2496     case 0xA220: mprop->reflection = parse_percentage();
2497     (void)parse_mapname (&chunk);
2498     break;
2499    
2500     case 0xA310: if (mprop->reflection == 0.0)
2501     mprop->reflection = 1.0;
2502     break;
2503    
2504     case 0xA200: mprop->tex_strength = parse_percentage();
2505     strcpy (mprop->tex_map, parse_mapname (&chunk));
2506     break;
2507    
2508     case 0xA230: mprop->bump_strength = parse_percentage();
2509     strcpy (mprop->bump_map, parse_mapname (&chunk));
2510     break;
2511     }
2512     }
2513    
2514     end_chunk (&chunk);
2515     } while (chunk.end <= mainchunk->end);
2516    
2517     LIST_INSERT (mprop_list, mprop);
2518     }
2519    
2520    
2521     char *parse_mapname (Chunk *mainchunk)
2522     {
2523     static char name[80] = "";
2524     Chunk chunk;
2525    
2526     do {
2527     start_chunk (&chunk);
2528    
2529     if (chunk.end <= mainchunk->end) {
2530     switch (chunk.tag) {
2531     case 0xA300: strcpy (name, read_string());
2532     break;
2533     }
2534     }
2535    
2536     end_chunk (&chunk);
2537     } while (chunk.end <= mainchunk->end);
2538    
2539     return name;
2540     }
2541    
2542    
2543     void parse_named_object (Chunk *mainchunk)
2544     {
2545     Chunk chunk;
2546    
2547     strcpy (obj_name, read_string());
2548     cleanup_name (obj_name);
2549    
2550     printf ("Working on: %s\n", obj_name);
2551    
2552     mesh = NULL;
2553    
2554     do {
2555     start_chunk (&chunk);
2556    
2557     if (chunk.end <= mainchunk->end) {
2558     switch (chunk.tag) {
2559     case 0x4100: parse_n_tri_object (&chunk);
2560     break;
2561     case 0x4600: parse_n_direct_light (&chunk);
2562     break;
2563     case 0x4700: parse_n_camera();
2564     break;
2565     case 0x4010: if (mesh != NULL) mesh->hidden = TRUE;
2566     break;
2567     case 0x4012: if (mesh != NULL) mesh->shadow = FALSE;
2568     break;
2569     }
2570     }
2571    
2572     end_chunk (&chunk);
2573     } while (chunk.end <= mainchunk->end);
2574    
2575     if (mesh != NULL) {
2576     update_limits (mesh);
2577    
2578     if (frame >= 0)
2579     LIST_INSERT (mesh_list, mesh);
2580     else {
2581     write_mesh (out, mesh);
2582    
2583     free_mesh_data (mesh);
2584     free (mesh);
2585     }
2586     }
2587     }
2588    
2589    
2590     void parse_n_tri_object (Chunk *mainchunk)
2591     {
2592     Chunk chunk;
2593    
2594     mesh = create_mesh (obj_name, 0, 0);
2595    
2596     do {
2597     start_chunk (&chunk);
2598    
2599     if (chunk.end <= mainchunk->end) {
2600     switch (chunk.tag) {
2601     case 0x4110: parse_point_array();
2602     break;
2603     case 0x4120: parse_face_array (&chunk);
2604     break;
2605     case 0x4160: parse_mesh_matrix();
2606     break;
2607     }
2608     }
2609    
2610     end_chunk (&chunk);
2611     } while (chunk.end <= mainchunk->end);
2612     }
2613    
2614    
2615     void parse_point_array()
2616     {
2617     int i;
2618    
2619     mesh->vertices = read_word();
2620     mesh->vertex = malloc (mesh->vertices * sizeof(*(mesh->vertex)));
2621     if (mesh->vertex == NULL)
2622     abortmsg ("Out of memory allocating mesh", 1);
2623    
2624     for (i = 0; i < mesh->vertices; i++)
2625     read_point (mesh->vertex[i]);
2626     }
2627    
2628    
2629     void parse_face_array (Chunk *mainchunk)
2630     {
2631     Chunk chunk;
2632     int i;
2633    
2634     mesh->faces = read_word();
2635     mesh->face = malloc (mesh->faces * sizeof(*(mesh->face)));
2636     if (mesh->face == NULL)
2637     abortmsg ("Out of memory allocating mesh", 1);
2638    
2639     mesh->mtl = malloc (mesh->faces * sizeof(*(mesh->mtl)));
2640     if (mesh->mtl == NULL)
2641     abortmsg ("Out of memory allocating mesh", 1);
2642    
2643     for (i = 0; i < mesh->faces; i++) {
2644     mesh->face[i].a = read_word();
2645     mesh->face[i].b = read_word();
2646     mesh->face[i].c = read_word();
2647     (void)read_word();
2648    
2649     mesh->mtl[i] = NULL;
2650     }
2651    
2652     do {
2653     start_chunk (&chunk);
2654    
2655     if (chunk.end <= mainchunk->end) {
2656     switch (chunk.tag) {
2657     case 0x4130: parse_msh_mat_group();
2658     break;
2659     case 0x4150: parse_smooth_group();
2660     break;
2661     }
2662     }
2663    
2664     end_chunk (&chunk);
2665     } while (chunk.end <= mainchunk->end);
2666    
2667     for (i = 0; i < mesh->faces; i++) {
2668     if (mesh->mtl[i] == NULL)
2669     mesh->mtl[i] = update_materials ("Default", 0);
2670     }
2671     }
2672    
2673    
2674     void parse_msh_mat_group()
2675     {
2676     Material *new_mtl;
2677     char mtlname[80];
2678     int mtlcnt;
2679     int i, face;
2680    
2681     strcpy (mtlname, read_string());
2682     cleanup_name (mtlname);
2683    
2684     new_mtl = update_materials (mtlname, 0);
2685    
2686     mtlcnt = read_word();
2687    
2688     for (i = 0; i < mtlcnt; i++) {
2689     face = read_word();
2690     mesh->mtl[face] = new_mtl;
2691     }
2692     }
2693    
2694    
2695     void parse_smooth_group()
2696     {
2697    
2698     }
2699    
2700    
2701     void parse_mesh_matrix()
2702     {
2703     int i, j;
2704    
2705     if (mesh != NULL) {
2706     for (i = 0; i < 4; i++) {
2707     for (j = 0; j < 3; j++)
2708     mesh->matrix[i][j] = read_float();
2709     }
2710    
2711     mat_inv (mesh->invmatrix, mesh->matrix);
2712     }
2713     }
2714    
2715    
2716     void parse_n_direct_light (Chunk *mainchunk)
2717     {
2718     Chunk chunk;
2719     Spotlight *s;
2720     OmniLight *o;
2721     int light_off = FALSE;
2722     int spot_flag = FALSE;
2723    
2724     read_point (pos);
2725     parse_colour (&col);
2726    
2727     do {
2728     start_chunk (&chunk);
2729    
2730     if (chunk.end <= mainchunk->end) {
2731     switch (chunk.tag) {
2732     case 0x4620: light_off = TRUE;
2733     break;
2734     case 0x4610: parse_dl_spotlight();
2735     spot_flag = TRUE;
2736     break;
2737     }
2738     }
2739    
2740     end_chunk (&chunk);
2741     } while (chunk.end <= mainchunk->end);
2742    
2743     if (light_off)
2744     return;
2745    
2746     if (!spot_flag) {
2747     if (frame >= 0) {
2748     o = LIST_FIND (omni_list, obj_name);
2749    
2750     if (o != NULL) {
2751     pos[X] = o->pos[X];
2752     pos[Y] = o->pos[Y];
2753     pos[Z] = o->pos[Z];
2754     col = o->col;
2755     }
2756     }
2757    
2758     write_light (out, obj_name, pos, col);
2759     }
2760     else {
2761     if (frame >= 0) {
2762     s = LIST_FIND (spot_list, obj_name);
2763    
2764     if (s != NULL) {
2765     pos[X] = s->pos[X];
2766     pos[Y] = s->pos[Y];
2767     pos[Z] = s->pos[Z];
2768     target[X] = s->target[X];
2769     target[Y] = s->target[Y];
2770     target[Z] = s->target[Z];
2771     col = s->col;
2772     hotspot = s->hotspot;
2773     falloff = s->falloff;
2774     }
2775     }
2776    
2777     if (falloff <= 0.0)
2778     falloff = 180.0;
2779    
2780     if (hotspot <= 0.0)
2781     hotspot = 0.7*falloff;
2782    
2783     write_spot (out, obj_name, pos, target, col, hotspot, falloff);
2784     }
2785     }
2786    
2787    
2788     void parse_dl_spotlight()
2789     {
2790     read_point (target);
2791    
2792     hotspot = read_float();
2793     falloff = read_float();
2794     }
2795    
2796    
2797     void parse_n_camera()
2798     {
2799     float bank;
2800     float lens;
2801    
2802     read_point (pos);
2803     read_point (target);
2804     bank = read_float();
2805     lens = read_float();
2806    
2807     if (frame >= 0 && cam_list != NULL) {
2808     pos[X] = cam_list->pos[X];
2809     pos[Y] = cam_list->pos[Y];
2810     pos[Z] = cam_list->pos[Z];
2811     target[X] = cam_list->target[X];
2812     target[Y] = cam_list->target[Y];
2813     target[Z] = cam_list->target[Z];
2814     lens = cam_list->lens;
2815     bank = cam_list->bank;
2816     }
2817    
2818     write_camera (out, obj_name, pos, target, lens, bank);
2819     }
2820    
2821    
2822     void parse_colour (Colour *colour)
2823     {
2824     Chunk chunk;
2825     Colour_24 colour_24;
2826    
2827     start_chunk (&chunk);
2828    
2829     switch (chunk.tag) {
2830     case 0x0010: parse_colour_f (colour);
2831     break;
2832    
2833     case 0x0011: parse_colour_24 (&colour_24);
2834     colour->red = colour_24.red/255.0;
2835     colour->green = colour_24.green/255.0;
2836     colour->blue = colour_24.blue/255.0;
2837     break;
2838    
2839     default: abortmsg ("Error parsing colour", 1);
2840     }
2841    
2842     end_chunk (&chunk);
2843     }
2844    
2845    
2846     void parse_colour_f (Colour *colour)
2847     {
2848     colour->red = read_float();
2849     colour->green = read_float();
2850     colour->blue = read_float();
2851     }
2852    
2853    
2854     void parse_colour_24 (Colour_24 *colour)
2855     {
2856     colour->red = read_byte();
2857     colour->green = read_byte();
2858     colour->blue = read_byte();
2859     }
2860    
2861    
2862     float parse_percentage()
2863     {
2864     Chunk chunk;
2865     float percent = 0.0;
2866    
2867     start_chunk (&chunk);
2868    
2869     switch (chunk.tag) {
2870     case 0x0030: percent = parse_int_percentage()/100.0;
2871     break;
2872    
2873     case 0x0031: percent = parse_float_percentage();
2874     break;
2875    
2876     default: printf ("WARNING: Error parsing percentage");
2877     }
2878    
2879     end_chunk (&chunk);
2880    
2881     return percent;
2882     }
2883    
2884    
2885     short parse_int_percentage()
2886     {
2887     word percent = read_word();
2888    
2889     return percent;
2890     }
2891    
2892    
2893     float parse_float_percentage()
2894     {
2895     float percent = read_float();
2896    
2897     return percent;
2898     }
2899    
2900    
2901     void start_chunk (Chunk *chunk)
2902     {
2903     chunk->start = ftell(in);
2904     chunk->tag = read_word();
2905     chunk->length = read_dword();
2906     if (chunk->length < sizeof(word)+sizeof(dword))
2907     chunk->length = sizeof(word) + sizeof(dword);
2908     chunk->end = chunk->start + chunk->length;
2909     }
2910    
2911    
2912     void end_chunk (Chunk *chunk)
2913     {
2914     fseek (in, chunk->end, 0);
2915     }
2916    
2917    
2918     byte read_byte()
2919     {
2920     byte data;
2921    
2922     data = fgetc (in);
2923    
2924     return data;
2925     }
2926    
2927    
2928     word read_word()
2929     {
2930     word data;
2931    
2932     data = fgetc (in);
2933     data |= fgetc (in) << 8;
2934    
2935     return data;
2936     }
2937    
2938    
2939     dword read_dword()
2940     {
2941     dword data;
2942    
2943     data = read_word();
2944     data |= read_word() << 16;
2945    
2946     return data;
2947     }
2948    
2949    
2950     float read_float()
2951     {
2952     union { dword i; char c[8]; } u;
2953     dword data;
2954    
2955     data = read_dword();
2956    
2957     if (sizeof(dword) == sizeof(float))
2958     return *(float *)&data;
2959    
2960     u.i = 1;
2961     if (u.c[0] == 0)
2962     return *(float *)&data; /* assume big-endian */
2963    
2964     if (sizeof(dword) != 2*sizeof(float)) {
2965     fputs("Unsupported word length\n", stderr);
2966     exit(1);
2967     }
2968     u.i = data;
2969     return *(float *)&u.c[4];
2970     }
2971    
2972    
2973     void read_point (Vector v)
2974     {
2975     v[X] = read_float();
2976     v[Y] = read_float();
2977     v[Z] = read_float();
2978     }
2979    
2980    
2981     char *read_string()
2982     {
2983     static char string[80];
2984     int i;
2985    
2986     for (i = 0; i < 80; i++) {
2987     string[i] = read_byte();
2988    
2989     if (string[i] == '\0')
2990     break;
2991     }
2992    
2993     return string;
2994     }
2995    
2996    
2997     float findfov (float lens)
2998     {
2999     static float lens_table[13] =
3000     { 15.0, 17.0, 24.0, 35.0, 50.0, 85.0, 100.0, 135.0, 200.0,
3001     500.0, 625.0, 800.0, 1000.0 };
3002     static float fov_table[13] =
3003     { 115.0, 102.0, 84.0, 63.0, 46.0, 28.0, 24.0, 18.0,
3004     12.0, 5.0, 4.0, 3.125, 2.5 };
3005    
3006     float fov, f1, f2, l1, l2;
3007     int i;
3008    
3009     if (lens < 15.0)
3010     lens = 15.0;
3011     else if (lens > 1000.0)
3012     lens = 1000.0;
3013    
3014     for (i = 0; i < 13; i++)
3015     if (lens < lens_table[i])
3016     break;
3017    
3018     if (i == 13)
3019     i = 12;
3020     else if (i == 0)
3021     i = 1;
3022    
3023     f1 = fov_table[i-1];
3024     f2 = fov_table[i];
3025     l1 = lens_table[i-1];
3026     l2 = lens_table[i];
3027    
3028     fov = f1 + (lens - l1) * (f2 - f1) / (l2 - l1);
3029    
3030     return fov;
3031     }
3032    
3033