1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <ctype.h>
6 #include <sys/stat.h>
7 #include <math.h>
8 
9 #ifdef __MSW__
10 # include <windows.h>
11 #endif
12 
13 #include "../include/string.h"
14 #include "../include/fio.h"
15 #include "../include/disk.h"
16 
17 #include "gw.h"
18 #include "x3d.h"
19 #include "text3d.h"
20 #include "sfm.h"
21 #include "v3dhf.h"
22 #include "v3dtex.h"
23 #include "v3dmh.h"
24 #include "v3dmp.h"
25 #include "v3dmodel.h"
26 #include "v3dfio.h"
27 
28 #include "cp.h"
29 #include "cpfio.h"
30 #include "sarreality.h"
31 #include "obj.h"
32 #include "sar.h"
33 #include "smoke.h"
34 #include "fire.h"
35 #include "weather.h"
36 #include "sartime.h"
37 #include "sarfio.h"
38 #include "simutils.h"
39 #include "simop.h"
40 #include "objsound.h"
41 #include "objutils.h"
42 #include "objio.h"
43 #include "config.h"
44 
45 #include "runway/runway_displaced_threshold.x3d"
46 #include "runway/runway_midway_markers.x3d"
47 #include "runway/runway_td_markers.x3d"
48 #include "runway/runway_threshold.x3d"
49 
50 
51 /* Utilities */
52 static const char *NEXT_ARG(const char *s);
53 static const char *GET_ARG_S(const char *s, char **v);
54 static const char *GET_ARG_B(const char *s, Boolean *v);
55 static const char *GET_ARG_I(const char *s, int *v);
56 static const char *GET_ARG_F(const char *s, float *v);
57 static const char *GET_ARG_RGBA(const char *s, sar_color_struct *v);
58 static const char *GET_ARG_POS(const char *s, sar_position_struct *v);
59 static const char *GET_ARG_DIR(const char *s, sar_direction_struct *v);
60 
61 /* Object Postload Checking */
62 static int SARObjLoadPostLoadCheck(
63 	int obj_num, sar_object_struct *obj_ptr,
64 	const char *filename
65 );
66 
67 /* Visual Model Loading */
68 sar_visual_model_struct *SARObjLoadX3DDataVisualModel(
69 	sar_core_struct *core_ptr, sar_scene_struct *scene,
70 	char **x3d_data,
71 	const sar_scale_struct *scale
72 );
73 sar_visual_model_struct *SARObjLoadTextVisualModel(
74 	sar_core_struct *core_ptr, sar_scene_struct *scene,
75 	float font_width, float font_height,	/* In meters */
76 	float character_spacing,		/* In meters */
77 	const char *s,
78 	float *string_width			/* In meters */
79 );
80 
81 /* Parameter Loading */
82 int SARObjLoadTranslate(
83 	sar_core_struct *core_ptr, sar_scene_struct *scene,
84 	sar_object_struct *obj_ptr, sar_parm_translate_struct *p_translate
85 );
86 int SARObjLoadTranslateRandom(
87 	sar_core_struct *core_ptr, sar_scene_struct *scene,
88 	sar_object_struct *obj_ptr,
89 	sar_parm_translate_random_struct *p_translate_random
90 );
91 static int SARObjLoadSoundSource(
92 	sar_scene_struct *scene,
93 	sar_sound_source_struct ***list, int *total,
94 	const char *line, const char *filename, int line_num
95 );
96 int SARObjLoadTexture(
97 	sar_core_struct *core_ptr, sar_scene_struct *scene,
98 	sar_parm_texture_load_struct *p_texture_load
99 );
100 int SARObjLoadHelipad(
101 	sar_core_struct *core_ptr, sar_scene_struct *scene,
102 	sar_parm_new_helipad_struct *p_new_helipad
103 );
104 int SARObjLoadRunway(
105 	sar_core_struct *core_ptr, sar_scene_struct *scene,
106 	sar_parm_new_runway_struct *p_new_runway
107 );
108 int SARObjLoadHuman(
109 	sar_core_struct *core_ptr, sar_scene_struct *scene,
110 	sar_parm_new_human_struct *p_new_human
111 );
112 int SARObjLoadFire(
113 	sar_core_struct *core_ptr, sar_scene_struct *scene,
114 	sar_parm_new_fire_struct *p_new_fire
115 );
116 int SARObjLoadSmoke(
117 	sar_core_struct *core_ptr, sar_scene_struct *scene,
118 	sar_parm_new_smoke_struct *p_new_smoke
119 );
120 int SARObjLoadHeightField(
121 	sar_core_struct *core_ptr,
122 	int obj_num, sar_object_struct *obj_ptr,
123 	sar_visual_model_struct *vmodel,
124 	void *p, const char *filename, int line_num,
125 	GLuint list
126 );
127 
128 static void SARObjLoadProcessVisualPrimitive(
129 	sar_core_struct *core_ptr,
130 	int obj_num, sar_object_struct *obj_ptr,
131 	sar_visual_model_struct *vmodel,
132 	void *p, const char *filename, int line_num
133 );
134 static void SARObjLoadProcessVisualModel(
135 	sar_core_struct *core_ptr,
136 	int obj_num, sar_object_struct *obj_ptr,
137 	sar_visual_model_struct *vmodel,
138 	v3d_model_struct *v3d_model,
139 	Boolean process_as_ir,
140 	const char *filename, int line_num
141 );
142 
143 static void SARObjLoadLine(
144 	sar_core_struct *core_ptr,
145 	int obj_num, sar_object_struct *obj_ptr,
146 	const char *line, const char *filename, int line_num
147 );
148 
149 int SARObjLoadFromFile(
150 	sar_core_struct *core_ptr, int obj_num, const char *filename
151 );
152 
153 
154 #ifndef SAR_COMMENT_CHAR
155 # define SAR_COMMENT_CHAR       '#'
156 #endif
157 
158 
159 #define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
160 #define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
161 #define ATOF(s)		(((s) != NULL) ? (float)atof(s) : 0.0f)
162 #define STRDUP(s)	(((s) != NULL) ? strdup(s) : NULL)
163 
164 #define MAX(a,b)	(((a) > (b)) ? (a) : (b))
165 #define MIN(a,b)	(((a) < (b)) ? (a) : (b))
166 #define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
167 #define STRLEN(s)	(((s) != NULL) ? (int)strlen(s) : 0)
168 #define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : 1)
169 
170 #define RADTODEG(r)	((r) * 180.0 / PI)
171 #define DEGTORAD(d)	((d) * PI / 180.0)
172 
173 #define ISCOMMENT(c)	((c) == SAR_COMMENT_CHAR)
174 #define ISCR(c)		(((c) == '\n') || ((c) == '\r'))
175 
176 
177 static int last_begin_primitive_type;
178 
179 
180 /*
181  *	Texture plane orientation codes:
182  */
183 #define TEX_ORIENT_NONE	0
184 #define TEX_ORIENT_XY	1
185 #define TEX_ORIENT_YZ	2
186 #define TEX_ORIENT_XZ	3
187 
188 static Boolean tex_on;
189 static int tex_orient;	/* One of TEX_ORIENT_* */
190 
191 typedef struct {
192 	float i, j;	/* Position of `upper left' in meters */
193 	float w, h;	/* Size `to lower right' in meters */
194 } tex_coord_struct;
195 static tex_coord_struct tex_coord;
196 
197 
198 /*
199  *	Seeks s past the current argument (if any) then past any blank
200  *	characters until the next argument or end of string is reached.
201  */
NEXT_ARG(const char * s)202 static const char *NEXT_ARG(const char *s)
203 {
204 	if(STRISEMPTY(s))
205 	    return(s);
206 
207 	while(!ISBLANK(*s) && (*s != '\0'))
208 	    s++;
209 	while(ISBLANK(*s))
210 	    s++;
211 
212 	return(s);
213 }
214 
215 /*
216  *	Gets the argument at the current position of the string s, the
217  *	return value will be a dynamically allocated string.
218  *
219  *	Returns the pointer to the next argument or NULL if the end of
220  *	the string is reached.
221  */
GET_ARG_S(const char * s,char ** v)222 static const char *GET_ARG_S(const char *s, char **v)
223 {
224 	if(STRISEMPTY(s))
225 	    return(s);
226 
227 	if(*s == '\"')
228 	{
229 	    int len;
230 	    const char *end = s + 1;
231 
232 	    s++;	/* Skip first quote character */
233 
234 	    /* Calculate length of word in quotes, seek until next
235 	     * unescaped quote character or end of string
236 	     */
237 	    while((*end != '\"') && (*end != '\0'))
238 	    {
239 		if(*end == '\\')
240 		{
241 		    end++;
242 		    if(*end != '\0')
243 			end++;
244 		}
245 		else
246 		    end++;
247 	    }
248 	    len = end - s;
249 
250 	    if(len > 0)
251 	    {
252 		char *s2 = (char *)malloc(len + 1);
253 		memcpy(s2, s, len);
254 		s2[len] = '\0';
255 
256 		free(*v);
257 		*v = s2;
258 	    }
259 
260 	    return(NEXT_ARG(end));
261 	}
262 	else
263 	{
264 	    int len;
265 	    const char *end = s;
266 
267 	    /* Calculate length of word, seek until next blank character
268 	     * or end of string
269 	     */
270 	    while(!ISBLANK(*end) && (*end != '\0'))
271 		end++;
272 	    len = end - s;
273 
274 	    if(len > 0)
275 	    {
276 		char *s2 = (char *)malloc(len + 1);
277 		memcpy(s2, s, len);
278 		s2[len] = '\0';
279 
280 		free(*v);
281 		*v = s2;
282 	    }
283 
284 	    return(NEXT_ARG(s));
285 	}
286 }
287 
288 /*
289  *      Gets the argument at the current position of the string s, the
290  *      return value will a Boolean.
291  *
292  *      Returns the pointer to the next argument or NULL if the end of
293  *      the string is reached.
294  */
GET_ARG_B(const char * s,Boolean * v)295 static const char *GET_ARG_B(const char *s, Boolean *v)
296 {
297 	if(STRISEMPTY(s))
298 	    return(s);
299 
300 	if(*s == '\"')
301 	{
302 	    char c = s[1];
303 	    const char *end = s + 1;
304 
305 	    s++;	/* Skip first quote character */
306 
307 	    /* Calculate length of word in quotes, seek until next
308 	     * unescaped quote character or end of string
309 	     */
310 	    while((*end != '\"') && (*end != '\0'))
311 	    {
312 		if(*end == '\\')
313 		{
314 		    end++;
315 		    if(*end != '\0')
316 			end++;
317 		}
318 		else
319 		    end++;
320 	    }
321 
322 	    /* Zero? */
323 	    if(c == '0')
324 		*v = False;
325 	    /* No? */
326 	    else if(toupper(c) == 'N')
327 		*v = False;
328 	    /* On or off? */
329 	    else if(toupper(c) == 'O')
330 		*v = (toupper(s[1]) == 'N') ? True : False;
331 	    /* All else assume true */
332 	    else
333 		*v = True;
334 
335 	    return(NEXT_ARG(end));
336 	}
337 	else
338 	{
339 	    char c = *s;
340 
341 	    /* Zero? */
342 	    if(c == '0')
343 		*v = False;
344 	    /* No? */
345 	    else if(toupper(c) == 'N')
346 		*v = False;
347 	    /* On or off? */
348 	    else if(toupper(c) == 'O')
349 		*v = (toupper(s[1]) == 'N') ? True : False;
350 	    /* All else assume true */
351 	    else
352 		*v = True;
353 
354 	    return(NEXT_ARG(s));
355 	}
356 }
357 
358 /*
359  *      Gets the argument at the current position of the string s, the
360  *      return value will an int.
361  *
362  *      Returns the pointer to the next argument or NULL if the end of
363  *      the string is reached.
364  */
GET_ARG_I(const char * s,int * v)365 static const char *GET_ARG_I(const char *s, int *v)
366 {
367 	if(STRISEMPTY(s))
368 	    return(s);
369 
370 	if(*s == '\"')
371 	{
372 	    const char *end = s + 1;
373 
374 	    s++;	/* Skip first quote character */
375 
376 	    /* Calculate length of word in quotes, seek until next
377 	     * unescaped quote character or end of string
378 	     */
379 	    while((*end != '\"') && (*end != '\0'))
380 	    {
381 		if(*end == '\\')
382 		{
383 		    end++;
384 		    if(*end != '\0')
385 			end++;
386 		}
387 		else
388 		    end++;
389 	    }
390 
391 	    *v = ATOI(s);
392 	    return(NEXT_ARG(end));
393 	}
394 	else
395 	{
396 	    *v = ATOI(s);
397 	    return(NEXT_ARG(s));
398 	}
399 }
400 
401 /*
402  *      Gets the argument at the current position of the string s, the
403  *      return value will an float.
404  *
405  *      Returns the pointer to the next argument or NULL if the end of
406  *      the string is reached.
407  */
GET_ARG_F(const char * s,float * v)408 static const char *GET_ARG_F(const char *s, float *v)
409 {
410 	if(STRISEMPTY(s))
411 	    return(s);
412 
413 	if(*s == '\"')
414 	{
415 	    const char *end = s + 1;
416 
417 	    s++;        /* Skip first quote character */
418 
419 	    /* Calculate length of word in quotes, seek until next
420 	     * unescaped quote character or end of string
421 	     */
422 	    while((*end != '\"') && (*end != '\0'))
423 	    {
424 		if(*end == '\\')
425 		{
426 		    end++;
427 		    if(*end != '\0')
428 			end++;
429 		}
430 		else
431 		    end++;
432 	    }
433 
434 	    *v = ATOF(s);
435 	    return(NEXT_ARG(end));
436 	}
437 	else
438 	{
439 	    *v = ATOF(s);
440 	    return(NEXT_ARG(s));
441 	}
442 }
443 
444 /*
445  *      Gets the argument at the current position of the string s, the
446  *      return value will a sar_color_struct.
447  *
448  *      Returns the pointer to the next argument or NULL if the end of
449  *      the string is reached.
450  */
GET_ARG_RGBA(const char * s,sar_color_struct * v)451 static const char *GET_ARG_RGBA(const char *s, sar_color_struct *v)
452 {
453 	if(STRISEMPTY(s))
454 	    return(s);
455 
456 	if(*s == '\"')
457 	{
458 	    s++;
459 
460 	    if(*s == '#')
461 	    {
462 		unsigned int r, g, b, a;
463 		const char *end = s + 1;
464 
465 		s++;		/* Skip '#' character */
466 
467 		/* Calculate length of word in quotes, seek until next
468 		 * unescaped quote character or end of string
469 		 */
470 		while((*end != '\"') && (*end != '\0'))
471 		{
472 		    if(*end == '\\')
473 		    {
474 			end++;
475 			if(*end != '\0')
476 			    end++;
477 		    }
478 		    else
479 			end++;
480 		}
481 
482 		sscanf(
483 		    s,
484 		    "%2x%2x%2x%2x",
485 		    &a, &r, &g, &b
486 		);
487 
488 		v->a = (float)a / (float)0xff;
489 		v->r = (float)r / (float)0xff;
490 		v->g = (float)g / (float)0xff;
491 		v->b = (float)b / (float)0xff;
492 
493 		return(NEXT_ARG(end));
494 	    }
495 	    else
496 	    {
497 		s--;
498 
499 		s = GET_ARG_F(s, &v->r);
500 		s = GET_ARG_F(s, &v->g);
501 		s = GET_ARG_F(s, &v->b);
502 		s = GET_ARG_F(s, &v->a);
503 
504 		return(s);
505 	    }
506 	}
507 	else
508 	{
509 	    s = GET_ARG_F(s, &v->r);
510 	    s = GET_ARG_F(s, &v->g);
511 	    s = GET_ARG_F(s, &v->b);
512 	    s = GET_ARG_F(s, &v->a);
513 
514 	    return(s);
515 	}
516 }
517 
518 /*
519  *      Gets the argument at the current position of the string s, the
520  *      return value will a sar_position_struct.
521  *
522  *      Returns the pointer to the next argument or NULL if the end of
523  *      the string is reached.
524  */
GET_ARG_POS(const char * s,sar_position_struct * v)525 static const char *GET_ARG_POS(const char *s, sar_position_struct *v)
526 {
527 	if(STRISEMPTY(s))
528 	    return(s);
529 
530 	s = GET_ARG_F(s, &v->x);
531 	s = GET_ARG_F(s, &v->y);
532 	s = GET_ARG_F(s, &v->z);
533 
534 	return(s);
535 }
536 
537 /*
538  *      Gets the argument at the current position of the string s, the
539  *      return value will a sar_direction_struct.
540  *
541  *      Returns the pointer to the next argument or NULL if the end of
542  *      the string is reached.
543  */
GET_ARG_DIR(const char * s,sar_direction_struct * v)544 static const char *GET_ARG_DIR(const char *s, sar_direction_struct *v)
545 {
546 	float d = 0.0f;
547 
548 	if(STRISEMPTY(s))
549 	    return(s);
550 
551 	s = GET_ARG_F(s, &d);
552 	v->heading = (float)DEGTORAD(d);
553 	s = GET_ARG_F(s, &d);
554 	v->pitch = (float)DEGTORAD(d);
555 	s = GET_ARG_F(s, &d);
556 	v->bank = (float)DEGTORAD(d);
557 
558 	return(s);
559 }
560 
561 /*
562  *	Completes the specified path by checking of the object that
563  *	the path refers to exists locally or globally.
564  *
565  *	Returns the full path to the object, the returned pointer must
566  *	be deleted by the calling function.
567  */
COMPLETE_PATH(const char * path)568 char *COMPLETE_PATH(const char *path)
569 {
570 	char *full_path;
571 
572 	if(path == NULL)
573 	    return(NULL);
574 
575 	if(ISPATHABSOLUTE(path))
576 	{
577 	    full_path = STRDUP(path);
578 	}
579 	else
580 	{
581 	    const char *s = PrefixPaths(dname.local_data, path);
582 	    struct stat stat_buf;
583 	    if((s != NULL) ? stat(s, &stat_buf) : True)
584 		s = PrefixPaths(dname.global_data, path);
585 	    full_path = STRDUP(s);
586 	}
587 
588 	return(full_path);
589 }
590 
591 
592 /*
593  *	Object postload checking
594  *
595  *	Returns number of errors.
596  */
SARObjLoadPostLoadCheck(int obj_num,sar_object_struct * obj_ptr,const char * filename)597 static int SARObjLoadPostLoadCheck(
598 	int obj_num, sar_object_struct *obj_ptr,
599 	const char *filename
600 )
601 {
602 	int	warnings = 0,
603 		errors = 0;
604 	sar_object_aircraft_struct *aircraft;
605 	sar_object_ground_struct *ground;
606 
607 	if(obj_ptr == NULL)
608 	{
609 	    errors++;
610 	    return(errors);
611 	}
612 
613 	/* Invalid object type? */
614 	if(obj_ptr->type < 0)
615 	{
616 	    warnings++;
617 	    fprintf(
618 		stderr,
619  "%s: Warning: Invalid object type \"%i\".\n",
620 		filename, obj_ptr->type
621 	    );
622 	}
623 
624 	/* No type specific data allocated? */
625 	if(obj_ptr->data == NULL)
626 	{
627 	    /* Ignore */
628 	}
629 
630 	/* No standard visual model? */
631 	if(obj_ptr->visual_model == NULL)
632 	{
633 	    warnings++;
634 	    fprintf(
635 		stderr,
636  "%s: Warning: Visual model \"standard\" not defined.\n",
637 		filename
638 	    );
639 	}
640 
641 	/* Check object type specific values */
642 	switch(obj_ptr->type)
643 	{
644 	  case SAR_OBJ_TYPE_GARBAGE:
645 	  case SAR_OBJ_TYPE_STATIC:
646 	  case SAR_OBJ_TYPE_AUTOMOBILE:
647 	  case SAR_OBJ_TYPE_WATERCRAFT:
648 	    break;
649 	  case SAR_OBJ_TYPE_AIRCRAFT:
650 #define PTR	aircraft
651 	    PTR = SAR_OBJ_GET_AIRCRAFT(obj_ptr);
652 	    /* No flight dynamics model? */
653 	    if(PTR->fdm == NULL)
654 	    {
655 		errors++;
656 		fprintf(
657 		    stderr,
658  "%s: Error: Flight dynamics model was not created.\n",
659 		    filename
660 		);
661 	    }
662 	    /* No cockpit visual model? */
663 	    if(PTR->visual_model_cockpit == NULL)
664 	    {
665 		warnings++;
666 		fprintf(
667 		    stderr,
668  "%s: Warning: Visual model \"cockpit\" not defined.\n",
669 		    filename
670 		);
671 	    }
672 #undef PTR
673 	    break;
674 
675 	  case SAR_OBJ_TYPE_GROUND:
676 #define PTR	ground
677 	    PTR = SAR_OBJ_GET_GROUND(obj_ptr);
678 
679 #undef PTR
680 	    break;
681 	  case SAR_OBJ_TYPE_RUNWAY:
682 	  case SAR_OBJ_TYPE_HELIPAD:
683 	  case SAR_OBJ_TYPE_HUMAN:
684 	  case SAR_OBJ_TYPE_SMOKE:
685 	  case SAR_OBJ_TYPE_FIRE:
686 	  case SAR_OBJ_TYPE_EXPLOSION:
687 	  case SAR_OBJ_TYPE_CHEMICAL_SPRAY:
688 	  case SAR_OBJ_TYPE_FUELTANK:
689 	  case SAR_OBJ_TYPE_PREMODELED:
690 	    break;
691 	}
692 
693 
694 	return(errors);
695 }
696 
697 /*
698  *	Loads a visual model from the given X3D data.
699  */
SARObjLoadX3DDataVisualModel(sar_core_struct * core_ptr,sar_scene_struct * scene,char ** x3d_data,const sar_scale_struct * scale)700 sar_visual_model_struct *SARObjLoadX3DDataVisualModel(
701 	sar_core_struct *core_ptr, sar_scene_struct *scene,
702 	char **x3d_data,
703 	const sar_scale_struct *scale
704 )
705 {
706 	sar_visual_model_struct *vmodel;
707 	X3DInterpValues v;
708 
709 	if(x3d_data == NULL)
710 	    return(NULL);
711 
712 	/* Set up X3D data values */
713 	v.flags =	X3D_VALUE_FLAG_COORDINATE_SYSTEM |
714 			((scale != NULL) ? X3D_VALUE_FLAG_SCALE : 0) |
715 			X3D_VALUE_FLAG_SKIP_TRANSLATES |
716 			X3D_VALUE_FLAG_SKIP_ROTATES;
717 	v.coordinate_system = X3D_COORDINATE_SYSTEM_XYZ;
718 	if(scale != NULL)
719 	{
720 	    v.scale_x = scale->x;
721 	    v.scale_y = scale->y;
722 	    v.scale_z = scale->z;
723 	}
724 
725 	/* Create a new SAR visual model */
726 	vmodel = SARVisualModelNew(
727 	    scene,
728 	    NULL, NULL	/* No file or model name (so not shared) */
729 	);
730 	/* New visual model must not be shared */
731 	if(SARVisualModelGetRefCount(vmodel) == 1)
732 	{
733 	    GLuint list = (GLuint)SARVisualModelNewList(vmodel);
734 	    if(list != 0)
735 	    {
736 		vmodel->load_state = SAR_VISUAL_MODEL_LOADING;
737 		glNewList(list, GL_COMPILE);
738 		X3DOpenDataGLOutput(
739 		    x3d_data, &v, NULL, 0
740 		);
741 		glEndList();
742 		vmodel->load_state = SAR_VISUAL_MODEL_LOADED;
743 	    }
744 	}
745 
746 	return(vmodel);
747 }
748 
749 /*
750  *	Loads a SAR visual model displaying the string s in 3d.
751  *
752  *	Origin is in lower left corner of 3d string.
753  */
SARObjLoadTextVisualModel(sar_core_struct * core_ptr,sar_scene_struct * scene,float font_width,float font_height,float character_spacing,const char * s,float * string_width)754 sar_visual_model_struct *SARObjLoadTextVisualModel(
755 	sar_core_struct *core_ptr, sar_scene_struct *scene,
756 	float font_width, float font_height,	/* In meters */
757 	float character_spacing,		/* In meters */
758 	const char *s,
759 	float *string_width			/* In meters */
760 )
761 {
762 	sar_visual_model_struct *vmodel;
763 
764 	if(string_width != NULL)
765 	    *string_width = 0.0f;
766 
767 	if((scene == NULL) || STRISEMPTY(s))
768 	    return(NULL);
769 
770 	/* Create a new SAR visual model */
771 	vmodel = SARVisualModelNew(
772 	    scene,
773 	    NULL, NULL	/* No filename or model name, so never shared */
774 	);
775 	/* Really a new (not shared) visual model? */
776 	if(SARVisualModelGetRefCount(vmodel) == 1)
777 	{
778 	    GLuint list = (GLuint)SARVisualModelNewList(vmodel);
779 	    if(list != 0)
780 	    {
781 		vmodel->load_state = SAR_VISUAL_MODEL_LOADING;
782 		glNewList(list, GL_COMPILE);
783 		Text3DStringOutput(
784 		    font_width, font_height,
785 		    character_spacing,
786 		    s,
787 		    string_width
788 		);
789 		glEndList();
790 		vmodel->load_state = SAR_VISUAL_MODEL_LOADED;
791 	    }
792 	}
793 
794 	return(vmodel);
795 }
796 
797 /*
798  *	Handles a translate parameter with respect to the given inputs.
799  *
800  *	Returns non-zero on error.
801  */
SARObjLoadTranslate(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_object_struct * obj_ptr,sar_parm_translate_struct * p_translate)802 int SARObjLoadTranslate(
803 	sar_core_struct *core_ptr, sar_scene_struct *scene,
804 	sar_object_struct *obj_ptr, sar_parm_translate_struct *p_translate
805 )
806 {
807 	sar_position_struct new_pos;
808 
809 	if((obj_ptr == NULL) || (p_translate == NULL))
810 	    return(-1);
811 
812 	memcpy(&new_pos, &p_translate->translate, sizeof(sar_position_struct));
813 
814 	/* Check certain objects that need their position offsetted
815 	 * internally
816 	 */
817 	if(obj_ptr->type == SAR_OBJ_TYPE_HUMAN)
818 	{
819 	    /* Human objects fall at a constant rate, so we need to move
820 	     * it up twice the fall rate cycle to ensure that it
821 	     * `stays up' any hollow contact surfaces
822 	     */
823 	    new_pos.z -= (float)(2.0 * SAR_DEF_HUMAN_FALL_RATE);
824 	}
825 
826 
827 	/* Update the ground elevation (for more information on usage,
828 	 * see SARSimApplyArtificialForce() on how it uses
829 	 * SARSimFindGround()
830 	 */
831 
832 	/* Check objects of types that need to know about ground
833 	 * elevation
834 	 */
835 	if((obj_ptr->type == SAR_OBJ_TYPE_AIRCRAFT) ||
836 	   (obj_ptr->type == SAR_OBJ_TYPE_HUMAN) ||
837            (obj_ptr->type == SAR_OBJ_TYPE_FIRE) ||
838 	   (obj_ptr->type == SAR_OBJ_TYPE_FUELTANK)
839 	)
840 	{
841 	    float ground_elevation = 0.0f;
842 
843 	    ground_elevation += SARSimFindGround(
844 		scene,
845 		core_ptr->object, core_ptr->total_objects,
846 		&new_pos	/* Position of our object */
847 	    );
848             // if (obj_ptr->type == SAR_OBJ_TYPE_FIRE)
849             //  printf("Setting up fire at %f\n", ground_elevation);
850 
851 	    obj_ptr->ground_elevation_msl = ground_elevation;
852 	}
853 
854 	/* Realize the new position */
855 	SARSimWarpObject(
856 	    scene, obj_ptr,
857 	    &new_pos, NULL
858 	);
859 
860 	return(0);
861 }
862 
863 /*
864  *	Translates the object to a random position.
865  *
866  *	Returns non-zero on error.
867  */
SARObjLoadTranslateRandom(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_object_struct * obj_ptr,sar_parm_translate_random_struct * p_translate_random)868 int SARObjLoadTranslateRandom(
869 	sar_core_struct *core_ptr, sar_scene_struct *scene,
870 	sar_object_struct *obj_ptr,
871 	sar_parm_translate_random_struct *p_translate_random
872 )
873 {
874 	float rand_coeff = SARRandomCoeff(0);
875 	float r = p_translate_random->radius_bound;
876 	float z = p_translate_random->z_bound;
877 	sar_position_struct *pos;
878 
879 	/* Calculate a random direction based on the random
880 	 * coefficient
881 	 */
882 	float rand_theta = (float)SFMSanitizeRadians(rand_coeff * (2.0 * PI));
883 
884 	if((obj_ptr == NULL) || (p_translate_random == NULL))
885 	    return(-1);
886 
887 	pos = &obj_ptr->pos;
888 
889 	pos->x += (float)(sin(rand_theta) * rand_coeff * r);
890 	pos->y += (float)(cos(rand_theta) * rand_coeff * r);
891 	pos->z += (float)(rand_coeff * z);
892 
893 	/* Realize position */
894 	SARSimWarpObject(scene, obj_ptr, pos, NULL);
895 
896 	return(0);
897 }
898 
899 /*
900  *	Loads a sound source.
901  *
902  *	The string line should point to the argument portion.
903  */
SARObjLoadSoundSource(sar_scene_struct * scene,sar_sound_source_struct *** list,int * total,const char * line,const char * filename,int line_num)904 static int SARObjLoadSoundSource(
905 	sar_scene_struct *scene,
906 	sar_sound_source_struct ***list, int *total,
907 	const char *line, const char *filename, int line_num
908 )
909 {
910 	int i, sample_rate_limit;
911 	const char *arg;
912 	float range, range_far, cutoff;
913 	char	*s,
914 		*name = NULL,
915 		*sndobj_filename = NULL,
916 		*sndobj_filename_far = NULL;
917 	sar_position_struct pos;
918 	sar_direction_struct dir;
919 	sar_sound_source_struct *sndobj;
920 
921 #define FREE_ALL	{	\
922  free(name);			\
923  free(sndobj_filename);		\
924  free(sndobj_filename_far);	\
925 }
926 
927 	if(STRISEMPTY(line))
928 	{
929 	    FREE_ALL
930 	    return(-1);
931 	}
932 
933 	/* Format:
934 	 *
935 	 * <name>
936 	 * <range> <range_far>
937 	 * <x> <y> <z>
938 	 * <cutoff>
939 	 * <heading> <pitch> <bank>
940 	 * <sample_rate_limit>
941 	 * <sndobj_filename> <sndobj_filename_far>
942 	 */
943 
944 	/* Begin parsing argument */
945 	arg = line;
946 	while(ISBLANK(*arg))
947 	    arg++;
948 
949 	/* Name */
950 	arg = GET_ARG_S(arg, &name);
951 
952 	/* Range */
953 	arg = GET_ARG_F(arg, &range);
954 
955 	/* Range Far */
956 	arg = GET_ARG_F(arg, &range_far);
957 
958 	/* Position */
959 	arg = GET_ARG_POS(arg, &pos);
960 
961 
962 	/* Cutoff */
963 	arg = GET_ARG_F(arg, &cutoff);
964 	cutoff = (float)DEGTORAD(cutoff);
965 
966 	/* Direction */
967 	arg = GET_ARG_DIR(arg, &dir);
968 
969 	/* Sample Rate Limit */
970 	arg = GET_ARG_I(arg, &sample_rate_limit);
971 
972 	/* SndObj Filename */
973 	arg = GET_ARG_S(arg, &sndobj_filename);
974 
975 	/* SndObj Filename Far */
976 	arg = GET_ARG_S(arg, &sndobj_filename_far);
977 
978 
979 	/* Warn if sndobj_filename was not given */
980 	s = sndobj_filename;
981 	if(STRISEMPTY(s))
982 	    fprintf(stderr,
983  "%s: Line %i: Warning:\
984  Value for sound_source_new argument sndobj_filename was not given.\n",
985 		filename, line_num
986 	    );
987 	/* Do not warn for sndobj_filename_far */
988 
989 
990 	/* Complete file names */
991 	s = COMPLETE_PATH(sndobj_filename);
992 	free(sndobj_filename);
993 	sndobj_filename = s;
994 
995 	s = COMPLETE_PATH(sndobj_filename_far);
996 	free(sndobj_filename_far);
997 	sndobj_filename_far = s;
998 
999 
1000 	/* Check if the sound files exist */
1001 	s = sndobj_filename;
1002 	if(!STRISEMPTY(s))
1003 	{
1004 	    struct stat stat_buf;
1005 	    if(stat(s, &stat_buf))
1006 	    {
1007 		char *s2 = STRDUP(strerror(errno));
1008 		if(s2 == NULL)
1009 		    s2 = STRDUP("no such file");
1010 		*s2 = toupper(*s2);
1011 		fprintf(
1012 		    stderr,
1013  "%s: Line %i: Warning:\
1014  Sound source \"%s\" sndobj_filename \"%s\": %s.\n",
1015 		    filename, line_num,
1016 		    name, s, s2
1017 		);
1018 		free(s2);
1019 	    }
1020 	}
1021 	s = sndobj_filename_far;
1022 	if(!STRISEMPTY(s))
1023 	{
1024 	    struct stat stat_buf;
1025 	    if(stat(s, &stat_buf))
1026 	    {
1027 		char *s2 = STRDUP(strerror(errno));
1028 		if(s2 == NULL)
1029 		    s2 = STRDUP("no such file");
1030 		*s2 = toupper(*s2);
1031 		fprintf(
1032 		    stderr,
1033  "%s: Line %i: Warning:\
1034  Sound source \"%s\" sndobj_filename_far \"%s\": %s.\n",
1035 		    filename, line_num,
1036 		    name, s, s2
1037 		);
1038 		free(s2);
1039 	    }
1040 	}
1041 
1042 
1043 	/* Create new sound source */
1044 	i = MAX(*total, 0);
1045 	*total = i + 1;
1046 	*list = (sar_sound_source_struct **)realloc(
1047 	    *list,
1048 	    (*total) * sizeof(sar_sound_source_struct *)
1049 	);
1050 	if(*list == NULL)
1051 	{
1052 	    *total = 0;
1053 	    FREE_ALL
1054 	    return(-1);
1055 	}
1056 
1057 	(*list)[i] = sndobj = SARSoundSourceNew(
1058 	    name,
1059 	    sndobj_filename,
1060 	    sndobj_filename_far,
1061 	    range,
1062 	    range_far,
1063 	    &pos, cutoff, &dir,
1064 	    sample_rate_limit
1065 	);
1066 
1067 	FREE_ALL
1068 	return(0);
1069 #undef FREE_ALL
1070 }
1071 
1072 
1073 /*
1074  *	Loads a new texture specified by the values in p_texture_load
1075  *	and stores it on the scene structure.
1076  *
1077  *	If the scene structure already has a texture loaded that matches
1078  *	the texture reference name specified in p_texture_load then
1079  *	nothing will be done and 0 is returned.
1080  *
1081  *	If the texture reference name specified in p_texture_load is
1082  *	NULL or an empty string then a new texture will be loaded
1083  *	implicitly.
1084  *
1085  *	Returns non-zero on error.
1086  */
SARObjLoadTexture(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_parm_texture_load_struct * p_texture_load)1087 int SARObjLoadTexture(
1088 	sar_core_struct *core_ptr, sar_scene_struct *scene,
1089 	sar_parm_texture_load_struct *p_texture_load
1090 )
1091 {
1092 	char *name, *path, *full_path;
1093 	v3d_texture_ref_struct *t;
1094 
1095 	if((scene == NULL) || (p_texture_load == NULL))
1096 	    return(-1);
1097 
1098 	if(STRISEMPTY(p_texture_load->file))
1099 	    return(-1);
1100 
1101 	name = STRDUP(p_texture_load->name);
1102 	path = STRDUP(p_texture_load->file);
1103 
1104 	/* Check if a texture with he same name is already loaded */
1105 	if(STRISEMPTY(name))
1106 	    t = NULL;
1107 	else
1108 	    t = SARGetTextureRefByName(scene, name);
1109 	/* Is there a texture with the same name already loaded? */
1110 	if(t != NULL)
1111 	    return(0);
1112 
1113 	/* Get the full path to the texture file */
1114 	full_path = COMPLETE_PATH(path);
1115 
1116 	/* Load texture */
1117 	t = V3DTextureLoadFromFile2DPreempt(
1118 	    full_path, name, V3D_TEX_FORMAT_RGBA
1119 	);
1120 	if(t != NULL)
1121 	{
1122 	    /* Add this texture to the scene's list of textures */
1123 	    int i = MAX(scene->total_texture_refs, 0);
1124 	    scene->total_texture_refs = i + 1;
1125 
1126 	    scene->texture_ref = (v3d_texture_ref_struct **)realloc(
1127 		scene->texture_ref,
1128 		scene->total_texture_refs * sizeof(v3d_texture_ref_struct *)
1129 	    );
1130 	    if(scene->texture_ref == NULL)
1131 	    {
1132 		V3DTextureDestroy(t);
1133 		free(full_path);
1134 		free(path);
1135 		free(name);
1136 		return(-3);
1137 	    }
1138 	    scene->texture_ref[i] = t;
1139 
1140 	    /* Set texture priority */
1141 	    V3DTexturePriority(t, p_texture_load->priority);
1142 
1143 	    free(full_path);
1144 	    free(path);
1145 	    free(name);
1146 	    return(0);
1147 	}
1148 	else
1149 	{
1150 	    /* Could not load texture */
1151 	    free(full_path);
1152 	    free(path);
1153 	    free(name);
1154 	    return(-1);
1155 	}
1156 }
1157 
1158 /*
1159  *      Loads a helipad object from the given argument line string.
1160  *
1161  *      Returns the newly created human object number or -1 on error.
1162  */
SARObjLoadHelipad(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_parm_new_helipad_struct * p_new_helipad)1163 int SARObjLoadHelipad(
1164 	sar_core_struct *core_ptr, sar_scene_struct *scene,
1165 	sar_parm_new_helipad_struct *p_new_helipad
1166 )
1167 {
1168 	const char *s;
1169 	int obj_num = -1;
1170 	sar_object_struct *obj_ptr;
1171 	sar_object_helipad_struct *helipad;
1172 	const char *ref_obj_name;
1173 	const char *tex_name = SAR_STD_TEXNAME_HELIPAD_PAVED;
1174 	int ref_obj_num = -1;
1175 	sar_object_struct *ref_obj_ptr = NULL;
1176 
1177 
1178 	if((scene == NULL) || (p_new_helipad == NULL))
1179 	    return(obj_num);
1180 
1181 	/* Create a new Helipad */
1182 	obj_num = SARObjNew(
1183 	    scene, &core_ptr->object, &core_ptr->total_objects,
1184 	    SAR_OBJ_TYPE_HELIPAD
1185 	);
1186 	obj_ptr = (obj_num > -1) ? core_ptr->object[obj_num] : NULL;
1187 	if(obj_ptr == NULL)
1188 	{
1189 	    obj_num = -1;
1190 	    return(obj_num);
1191 	}
1192 	helipad = SAR_OBJ_GET_HELIPAD(obj_ptr);
1193 	if(helipad == NULL)
1194 	    return(obj_num);
1195 
1196 	/* Begin setting values */
1197 
1198 	/* Helipad flags */
1199 	helipad->flags = p_new_helipad->flags;
1200 
1201 	/* Range */
1202 	obj_ptr->range = SAR_HELIPAD_DEF_RANGE;
1203 
1204 	/* Style */
1205 	s = p_new_helipad->style;
1206 	if(!STRISEMPTY(s))
1207 	{
1208 	    if(!strcasecmp(s, "ground_paved") ||
1209 	       !strcasecmp(s, "standard") ||
1210 	       !strcasecmp(s, "default")
1211 	    )
1212 	    {
1213 		helipad->style = SAR_HELIPAD_STYLE_GROUND_PAVED;
1214 		tex_name = SAR_STD_TEXNAME_HELIPAD_PAVED;
1215 	    }
1216 	    else if(!strcasecmp(s, "ground_bare"))
1217 	    {
1218 		helipad->style = SAR_HELIPAD_STYLE_GROUND_BARE;
1219 		tex_name = SAR_STD_TEXNAME_HELIPAD_BARE;
1220 	    }
1221 	    else if(!strcasecmp(s, "building"))
1222 	    {
1223 		helipad->style = SAR_HELIPAD_STYLE_BUILDING;
1224 		tex_name = SAR_STD_TEXNAME_HELIPAD_BUILDING;
1225 	    }
1226 	    else if(!strcasecmp(s, "vehicle"))
1227 	    {
1228 		helipad->style = SAR_HELIPAD_STYLE_VEHICLE;
1229 		tex_name = SAR_STD_TEXNAME_HELIPAD_VEHICLE;
1230 	    }
1231 	    else
1232 	    {
1233 		fprintf(
1234 		    stderr,
1235 "Object #%i: Warning:\
1236  Unsupported helipad style \"%s\".\n",
1237 		    obj_num, s
1238 		);
1239 		helipad->style = SAR_HELIPAD_STYLE_GROUND_PAVED;
1240 		tex_name = SAR_STD_TEXNAME_HELIPAD_PAVED;
1241 	    }
1242 	}
1243 	else
1244 	{
1245 	    /* Set default style */
1246 	    helipad->style = SAR_HELIPAD_STYLE_GROUND_PAVED;
1247 	    tex_name = SAR_STD_TEXNAME_HELIPAD_PAVED;
1248 	}
1249 
1250 	/* Size */
1251 	helipad->length = (float)MAX(p_new_helipad->length, 1.0);
1252 	helipad->width = (float)MAX(p_new_helipad->width, 1.0);
1253 	helipad->recession = (float)MAX(p_new_helipad->recession, 0.0);
1254 
1255 	/* Specify contact bounds so that the helipad is landable */
1256 	SARObjAddContactBoundsRectangular(
1257 	    obj_ptr, SAR_CRASH_FLAG_SUPPORT_SURFACE, 0,
1258 	    -(helipad->width / 2),
1259 	    (helipad->width / 2),
1260 	    -(helipad->length / 2),
1261 	    (helipad->length / 2),
1262 	    0.0f, 0.0f
1263 	);
1264 
1265 	/* Label */
1266 	s = p_new_helipad->label;
1267 	helipad->label = STRDUP(s);
1268 	if(!STRISEMPTY(s))
1269 	{
1270 	    /* Generate visual model for label using 3d text */
1271 	    int len = MAX(STRLEN(s), 1);
1272 	    float font_height = (float)(
1273 		MIN(helipad->width, helipad->length) *
1274 		0.7 / len
1275 	    );
1276 	    float font_width = (float)(font_height * 0.8);
1277 	    helipad->label_vmodel = SARObjLoadTextVisualModel(
1278 		core_ptr, scene,
1279 		font_width, font_height,
1280 		(float)(font_width * 0.05),
1281 		s,
1282 		&helipad->label_width
1283 	    );
1284 	}
1285 	else
1286 	{
1287 	    /* No label */
1288 	    helipad->label_vmodel = NULL;
1289 	    helipad->label_width = 0;
1290 	}
1291 
1292 	/* Light spacing */
1293 	helipad->light_spacing = helipad->length / 10;
1294 
1295 	/* Texture reference number from scene */
1296 	helipad->tex_num = SARGetTextureRefNumberByName(
1297 	    scene, tex_name
1298 	);
1299 
1300 	/* Match reference object? */
1301 	ref_obj_name = p_new_helipad->ref_obj_name;
1302 	if((ref_obj_name != NULL) ? (*ref_obj_name != '\0') : False)
1303 	{
1304 	    ref_obj_ptr = SARObjMatchPointerByName(
1305 		scene, core_ptr->object, core_ptr->total_objects,
1306 		ref_obj_name, &ref_obj_num
1307 	    );
1308 	    if(ref_obj_ptr != NULL)
1309 	    {
1310 
1311 	    }
1312 	    else
1313 	    {
1314 		fprintf(
1315 		    stderr,
1316 "Object #%i: Warning:\
1317  Unable to match reference object \"%s\" for helipad.\n",
1318 		    obj_num, ref_obj_name
1319 		);
1320 	    }
1321 	}
1322 	/* Set reference object */
1323 	helipad->ref_object = ref_obj_num;
1324 	/* Make sure reference object is not this object */
1325 	if(ref_obj_ptr == obj_ptr)
1326 	{
1327 	    ref_obj_num = -1;
1328 	    ref_obj_ptr = NULL;
1329 
1330 	    helipad->flags &= ~SAR_HELIPAD_FLAG_REF_OBJECT;
1331 	    helipad->flags &= ~SAR_HELIPAD_FLAG_FOLLOW_REF_OBJECT;
1332 
1333 	    helipad->ref_object = -1;
1334 	}
1335 
1336 	/* Set reference offset relative to the reference object */
1337 	memcpy(
1338 	    &helipad->ref_offset,
1339 	    &p_new_helipad->ref_offset,
1340 	    sizeof(sar_position_struct)
1341 	);
1342 
1343 	/* Set reference direction relative to the reference object */
1344 	memcpy(
1345 	    &helipad->ref_dir,
1346 	    &p_new_helipad->ref_dir,
1347 	    sizeof(sar_direction_struct)
1348 	);
1349 
1350 	/* Realize new position if there is a reference object */
1351 	if((helipad->flags & SAR_HELIPAD_FLAG_REF_OBJECT) &&
1352 	   (helipad->flags & SAR_HELIPAD_FLAG_FOLLOW_REF_OBJECT)
1353 	)
1354 	    SARSimWarpObjectRelative(
1355 		scene, obj_ptr,
1356 		core_ptr->object, core_ptr->total_objects, ref_obj_num,
1357 		&helipad->ref_offset,
1358 		&helipad->ref_dir
1359 	    );
1360 
1361 	return(obj_num);
1362 }
1363 
1364 /*
1365  *	Loads a runway object from the given argument line string.
1366  *
1367  *      Returns the newly created human object number or -1 on error.
1368  */
SARObjLoadRunway(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_parm_new_runway_struct * p_new_runway)1369 int SARObjLoadRunway(
1370 	sar_core_struct *core_ptr, sar_scene_struct *scene,
1371 	sar_parm_new_runway_struct *p_new_runway
1372 )
1373 {
1374 	const char *s;
1375 	int obj_num = -1;
1376 	sar_object_struct *obj_ptr;
1377 	sar_object_runway_struct *runway;
1378 
1379 
1380 	if((scene == NULL) || (p_new_runway == NULL))
1381 	    return(obj_num);
1382 
1383 	/* Create a new runway object */
1384 	obj_num = SARObjNew(
1385 	    scene, &core_ptr->object, &core_ptr->total_objects,
1386 	    SAR_OBJ_TYPE_RUNWAY
1387 	);
1388 	obj_ptr = (obj_num > -1) ? core_ptr->object[obj_num] : NULL;
1389 	if(obj_ptr == NULL)
1390 	{
1391 	    obj_num = -1;
1392 	    return(obj_num);
1393 	}
1394 	runway = SAR_OBJ_GET_RUNWAY(obj_ptr);
1395 	if(runway == NULL)
1396 	    return(obj_num);
1397 
1398 	/* Begin setting values */
1399 
1400 	/* Flags */
1401 	runway->flags |= p_new_runway->flags;
1402 
1403 	/* Range */
1404 	obj_ptr->range = p_new_runway->range;
1405 
1406 	/* Size */
1407 	runway->length = p_new_runway->length;
1408 	runway->width = p_new_runway->width;
1409 
1410 	/* Surface type */
1411 	runway->surface_type = p_new_runway->surface_type;
1412 
1413 	/* Set contact bounds to ensure that the object is landable */
1414 	SARObjAddContactBoundsRectangular(
1415 	    obj_ptr,
1416 	    SAR_CRASH_FLAG_SUPPORT_SURFACE, 0,
1417 	    -(runway->width / 2),
1418 	    (runway->width / 2),
1419 	    -(runway->length / 2),
1420 	    (runway->length / 2),
1421 	    0.0f, 0.0f
1422 	);
1423 
1424 	/* Label */
1425 	runway->north_label = STRDUP(p_new_runway->north_label);
1426 	runway->south_label = STRDUP(p_new_runway->south_label);
1427 
1428 	/* Create visual models for labels using 3d text */
1429 	s = p_new_runway->north_label;
1430 	if(!STRISEMPTY(s))
1431 	{
1432 	    int len = MAX(STRLEN(s), 1);
1433 	    float font_height = (float)(runway->width * 0.8 / len);
1434  	    float font_width = (float)(font_height * 0.8);
1435 
1436 	    runway->north_label_vmodel = SARObjLoadTextVisualModel(
1437 		core_ptr, scene,
1438 		font_width, font_height,
1439 		(float)(font_width * 0.15),
1440 		s,
1441 		&runway->north_label_width
1442 	    );
1443 	}
1444 	else
1445 	{
1446 	    runway->north_label_vmodel = NULL;
1447 	    runway->north_label_width = 0;
1448 	}
1449 	s = p_new_runway->south_label;
1450 	if(!STRISEMPTY(s))
1451 	{
1452 	    int len = STRLEN(s);
1453 	    float font_height = (float)(runway->width * 0.8 / len);
1454 	    float font_width = (float)(font_height * 0.8);
1455 
1456 	    runway->south_label_vmodel = SARObjLoadTextVisualModel(
1457 		core_ptr, scene,
1458 		font_width, font_height,
1459 		(float)(font_width * 0.15),
1460 		s,
1461 		&runway->south_label_width
1462 	    );
1463 	}
1464 	else
1465 	{
1466 	    runway->south_label_vmodel = NULL;
1467 	    runway->south_label_width = 0;
1468 	}
1469 
1470 	/* Dashes */
1471 	runway->dashes = (p_new_runway->dashes > 0) ?
1472 	    p_new_runway->dashes : 10;
1473 
1474 	/* Displaced thresholds */
1475 	runway->north_displaced_threshold =
1476 	    p_new_runway->north_displaced_threshold;
1477 	runway->south_displaced_threshold =
1478 	    p_new_runway->south_displaced_threshold;
1479 
1480 	/* Edge lighting */
1481 	runway->edge_light_spacing = (float)(
1482 	    (p_new_runway->edge_light_spacing > 0.0f) ?
1483 		p_new_runway->edge_light_spacing :
1484 		(runway->length / 50)
1485 	);
1486 
1487 	/* Approach lighting */
1488 	runway->north_approach_lighting_flags = 0;
1489 	runway->south_approach_lighting_flags = 0;
1490 
1491 	runway->tracer_anim_pos = 0;
1492 	runway->tracer_anim_rate = (sar_grad_anim_t)(
1493 	    ((sar_grad_anim_t)-1) * 0.5
1494 	);
1495 
1496 	/* Get texture number from scene */
1497 	runway->tex_num = SARGetTextureRefNumberByName(
1498 	    scene, SAR_STD_TEXNAME_RUNWAY
1499 	);
1500 
1501 	/* Begin loading visual models for runway decorations */
1502 	/* Threshold */
1503 	if(runway->flags & SAR_RUNWAY_FLAG_THRESHOLDS)
1504 	{
1505 	    sar_scale_struct scale;
1506 	    scale.x = runway->width;
1507 	    scale.y = 50.0f;
1508 	    scale.z = 1.0f;
1509 	    runway->threshold_vmodel = SARObjLoadX3DDataVisualModel(
1510 		core_ptr, scene,
1511 		runway_threshold_x3d,
1512 		&scale
1513 	    );
1514 	}
1515 	/* Touchdown markers */
1516 	if(runway->flags & SAR_RUNWAY_FLAG_TD_MARKERS)
1517 	{
1518 	    sar_scale_struct scale;
1519 	    scale.x = runway->width;
1520 	    scale.y = 30.0f;
1521 	    scale.z = 1.0f;
1522 	    runway->td_marker_vmodel = SARObjLoadX3DDataVisualModel(
1523 		core_ptr, scene,
1524 		runway_td_markers_x3d,
1525 		&scale
1526 	    );
1527 	}
1528 	/* Midway markers */
1529 	if(runway->flags & SAR_RUNWAY_FLAG_MIDWAY_MARKERS)
1530 	{
1531 	    sar_scale_struct scale;
1532 	    scale.x = runway->width;
1533 	    scale.y = (float)(runway->length * 0.05);
1534 	    scale.z = 1.0f;
1535 	    runway->midway_marker_vmodel = SARObjLoadX3DDataVisualModel(
1536 		core_ptr, scene,
1537 		runway_midway_markers_x3d,
1538 		&scale
1539 	    );
1540 	}
1541 	/* North displaced threshold */
1542 	if(runway->north_displaced_threshold > 0.0f)
1543 	{
1544 	    sar_scale_struct scale;
1545 	    scale.x = runway->width;
1546 	    scale.y = runway->north_displaced_threshold;
1547 	    scale.z = 1.0f;
1548 	    runway->north_displaced_threshold_vmodel =
1549 		SARObjLoadX3DDataVisualModel(
1550 		    core_ptr, scene,
1551 		    runway_displaced_threshold_x3d,
1552 		    &scale
1553 		);
1554 	}
1555 	/* South displaced threshold */
1556 	if(runway->south_displaced_threshold > 0.0f)
1557 	{
1558 	    sar_scale_struct scale;
1559 	    scale.x = runway->width;
1560 	    scale.y = runway->south_displaced_threshold;
1561 	    scale.z = 1.0f;
1562 	    runway->south_displaced_threshold_vmodel =
1563 		SARObjLoadX3DDataVisualModel(
1564 		    core_ptr, scene,
1565 		    runway_displaced_threshold_x3d,
1566 		    &scale
1567 		);
1568 	}
1569 
1570 	return(obj_num);
1571 }
1572 
1573 /*
1574  *	Loads a human object from the given argument line string.
1575  *
1576  *	Returns the newly created human object number or -1 on error.
1577  */
SARObjLoadHuman(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_parm_new_human_struct * p_new_human)1578 int SARObjLoadHuman(
1579 	sar_core_struct *core_ptr, sar_scene_struct *scene,
1580 	sar_parm_new_human_struct *p_new_human
1581 )
1582 {
1583 	int obj_num = -1;
1584 
1585 	if((scene == NULL) || (p_new_human == NULL))
1586 	    return(obj_num);
1587 
1588 	/* Create new human object */
1589 	obj_num = SARHumanCreate(
1590 	    core_ptr->human_data,
1591 	    scene, &core_ptr->object, &core_ptr->total_objects,
1592 	    p_new_human->flags,
1593 	    p_new_human->type_name
1594 	);
1595 
1596 	return(obj_num);
1597 }
1598 
1599 /*
1600  *      Loads a fire object from the given argument line string.
1601  *
1602  *      Returns the newly created fire object number or -1 on error.
1603  */
SARObjLoadFire(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_parm_new_fire_struct * p_new_fire)1604 int SARObjLoadFire(
1605 	sar_core_struct *core_ptr, sar_scene_struct *scene,
1606 	sar_parm_new_fire_struct *p_new_fire
1607 )
1608 {
1609 	int obj_num = -1;
1610 	sar_object_struct *obj_ptr;
1611 	sar_object_fire_struct *fire;
1612 	sar_position_struct pos;
1613 
1614 
1615 	if((scene == NULL) || (p_new_fire == NULL))
1616 	    return(obj_num);
1617 
1618 	memset(&pos, 0x00, sizeof(sar_position_struct));
1619 
1620 	/* Create new fire object */
1621 	obj_num = FireCreate(
1622 	    core_ptr, scene,
1623 	    &core_ptr->object, &core_ptr->total_objects,
1624 	    &pos, p_new_fire->radius, p_new_fire->height,
1625 	    -1,
1626 	    SAR_STD_TEXNAME_FIRE, SAR_STD_TEXNAME_FIRE_IR
1627 	);
1628 	obj_ptr = (obj_num > -1) ? core_ptr->object[obj_num] : NULL;
1629 	if(obj_ptr == NULL)
1630 	{
1631 	    obj_num = -1;
1632 	    return(obj_num);
1633 	}
1634 	fire = SAR_OBJ_GET_FIRE(obj_ptr);
1635 	if(fire == NULL)
1636 	    return(obj_num);
1637 
1638 	/* Begin setting values */
1639 
1640 	/* Animation repeats */
1641 	fire->total_frame_repeats = -1;	/* Infinate */
1642 
1643 	obj_ptr->life_span = 0;
1644 
1645 	return(obj_num);
1646 }
1647 
1648 /*
1649  *      Loads a smoke object from the given argument line string.
1650  *
1651  *      Returns the newly created fire object number or -1 on error.
1652  */
SARObjLoadSmoke(sar_core_struct * core_ptr,sar_scene_struct * scene,sar_parm_new_smoke_struct * p_new_smoke)1653 int SARObjLoadSmoke(
1654 	sar_core_struct *core_ptr, sar_scene_struct *scene,
1655 	sar_parm_new_smoke_struct *p_new_smoke
1656 )
1657 {
1658 	int obj_num = -1;
1659 	sar_object_struct *obj_ptr;
1660 	sar_position_struct pos;
1661 	const char *tex_name = NULL;
1662 
1663 
1664 	if((scene == NULL) || (p_new_smoke == NULL))
1665 	    return(obj_num);
1666 
1667 	memset(&pos, 0x00, sizeof(sar_position_struct));
1668 
1669 	/* Get name of smoke texture to use from the color code */
1670 	switch(p_new_smoke->color_code)
1671 	{
1672 	  case 0:	/* Light */
1673 	    tex_name = SAR_STD_TEXNAME_SMOKE_LIGHT;
1674 	    break;
1675 	  case 1:	/* Medium */
1676 	    tex_name = SAR_STD_TEXNAME_SMOKE_MEDIUM;
1677 	    break;
1678 	  case 2:	/* Dark */
1679 	    tex_name = SAR_STD_TEXNAME_SMOKE_DARK;
1680 	    break;
1681 	}
1682 
1683 	/* Create new smoke trail object */
1684 	obj_num = SmokeCreate(
1685 	    scene, &core_ptr->object, &core_ptr->total_objects,
1686 	    SAR_SMOKE_TYPE_SMOKE,	/* Smoke type */
1687 	    &pos, &p_new_smoke->offset,	/* Position and unit spawn offset */
1688 	    p_new_smoke->radius_start,
1689 	    p_new_smoke->radius_max,
1690 	    p_new_smoke->radius_rate,	/* May be -1.0 for autocalc */
1691 	    p_new_smoke->hide_at_max,
1692 	    p_new_smoke->total_units,
1693 	    p_new_smoke->respawn_int,
1694 	    tex_name,
1695 	    -1,				/* No reference object */
1696 	    0				/* No limit on life span */
1697 	);
1698 	obj_ptr = (obj_num > -1) ? core_ptr->object[obj_num] : NULL;
1699 	if(obj_ptr == NULL)
1700 	{
1701 	    obj_num = -1;
1702 	    return(obj_num);
1703 	}
1704 
1705 	/* Begin setting values */
1706 
1707 
1708 
1709 	return(obj_num);
1710 }
1711 
1712 /*
1713  *	Loads heightfield primitive p for the specified object.
1714  *
1715  *	If the GL display list is 0 then no GL commands will be issued
1716  *	and only the z height points will be recorded on the object.
1717  */
SARObjLoadHeightField(sar_core_struct * core_ptr,int obj_num,sar_object_struct * obj_ptr,sar_visual_model_struct * vmodel,void * p,const char * filename,int line_num,GLuint list)1718 int SARObjLoadHeightField(
1719 	sar_core_struct *core_ptr,
1720 	int obj_num, sar_object_struct *obj_ptr,
1721 	sar_visual_model_struct *vmodel,
1722 	void *p, const char *filename, int line_num,
1723 	GLuint list			/* Can be 0 */
1724 )
1725 {
1726 	int status, grid_points_total;
1727 	float x_length, y_length, z_length;
1728 	int num_grids_x, num_grids_y;
1729 	double grid_space_x, grid_space_y;
1730 	double *zpoints = NULL;
1731 
1732 	char *full_path;
1733 	sar_object_ground_struct *ground = NULL;
1734 	mp_heightfield_load_struct *mp_heightfield_load =
1735 	    (mp_heightfield_load_struct *)p;
1736 	v3d_hf_options_struct hfopt;
1737 
1738 	if(STRISEMPTY(mp_heightfield_load->path))
1739 	    return(-1);
1740 
1741 	ground = SAR_OBJ_GET_GROUND(obj_ptr);
1742 
1743 	/* Get the full path to the heightfield file */
1744 	full_path = COMPLETE_PATH(mp_heightfield_load->path);
1745 
1746 	/* Begin loading the heightfield */
1747 
1748 	/* Get size of heightfield from heightfield load primitive */
1749 	x_length = (float)mp_heightfield_load->x_length;
1750 	y_length = (float)mp_heightfield_load->y_length;
1751 	z_length = (float)mp_heightfield_load->z_length;
1752 	if((x_length <= 0.0f) ||
1753 	   (y_length <= 0.0f) ||
1754 	   (z_length <= 0.0f)
1755 	)
1756 	{
1757 	    free(full_path);
1758 	    return(-2);
1759 	}
1760 
1761 	glPushMatrix();
1762 	{
1763 	    /* Translate */
1764 	    glTranslated(
1765 		mp_heightfield_load->x,
1766 		mp_heightfield_load->z,
1767 		-mp_heightfield_load->y
1768 	    );
1769 
1770 	    /* No rotations */
1771 
1772 	    /* Set up heightfield options */
1773 	    hfopt.flags = (V3D_HF_OPT_FLAG_WINDING |
1774 		V3D_HF_OPT_FLAG_SET_NORMAL | V3D_HF_OPT_FLAG_SET_TEXCOORD
1775 	    );
1776 	    hfopt.winding = V3D_HF_WIND_CCW;
1777 	    hfopt.set_normal = V3D_HF_SET_NORMAL_STREATCHED;
1778 	    hfopt.set_texcoord = V3D_HF_SET_TEXCOORD_ALWAYS;
1779 
1780 	    /* Load heightfield */
1781 	    status = V3DHFLoadFromFile(
1782 		full_path,
1783 		x_length, y_length, z_length,	/* Scaling in meters */
1784 		&num_grids_x, &num_grids_y,	/* Number of grids */
1785 		&grid_space_x, &grid_space_y,	/* Grid spacing in meters */
1786 		&zpoints,			/* Heightfield points return */
1787 		(void *)list,			/* GL display list */
1788 		&hfopt
1789 	    );
1790 	    if(status)
1791 	    {
1792 		/* Error loading heightfield */
1793 		free(zpoints);
1794 		free(full_path);
1795 		return(-1);
1796 	    }
1797 	}
1798 	glPopMatrix();
1799 
1800 	/* Calculate total number of points */
1801 	grid_points_total = num_grids_x * num_grids_y;
1802 
1803 	/* Calculate statistics */
1804 	if((vmodel != NULL) && (list != 0))
1805 	{
1806 	    vmodel->mem_size += grid_points_total * (
1807 		(2 * 3 * 8 * sizeof(GLfloat)) +
1808 		(3 * sizeof(GLuint))
1809 	    );
1810 	    vmodel->statements += (grid_points_total * 8) + 2;
1811 	    vmodel->primitives += grid_points_total * 2;
1812 	}
1813 
1814 	/* Set newly loaded values to object */
1815 	if(ground != NULL)
1816 	{
1817 	    ground->x_trans = (float)mp_heightfield_load->x;
1818 	    ground->y_trans = (float)mp_heightfield_load->y;
1819 	    ground->z_trans = (float)mp_heightfield_load->z;
1820 
1821 	    ground->x_len = (float)x_length;
1822 	    ground->y_len = (float)y_length;
1823 
1824 	    ground->grid_points_x = num_grids_x;
1825 	    ground->grid_points_y = num_grids_y;
1826 	    ground->grid_points_total = grid_points_total;
1827 
1828 	    ground->grid_x_spacing = (float)grid_space_x;
1829 	    ground->grid_y_spacing = (float)grid_space_y;
1830 	    ground->grid_z_spacing = (float)z_length;
1831 
1832 	    /* Replace old heightfield z point data on ground object
1833 	     * structure with the newly allocated one
1834 	     */
1835 	    free(ground->z_point_value);
1836 	    ground->z_point_value = zpoints;
1837 	    zpoints = NULL;	/* Reset zpoints to mark it as transfered */
1838 	}
1839 
1840 	free(zpoints);
1841 	free(full_path);
1842 
1843 	return(0);
1844 }
1845 
1846 /*
1847  *	Called by SARObjLoadProcessVisualModel().
1848  *
1849  *	Parses and handles the specified V3D primitive p with the
1850  *	specified values.
1851  */
SARObjLoadProcessVisualPrimitive(sar_core_struct * core_ptr,int obj_num,sar_object_struct * obj_ptr,sar_visual_model_struct * vmodel,void * p,const char * filename,int line_num)1852 static void SARObjLoadProcessVisualPrimitive(
1853 	sar_core_struct *core_ptr,
1854 	int obj_num, sar_object_struct *obj_ptr,
1855 	sar_visual_model_struct *vmodel,
1856 	void *p, const char *filename, int line_num
1857 )
1858 {
1859 	int i, ptype, v_total = 0;
1860 	Boolean need_end_primitive = False;
1861 	mp_vertex_struct *ns = NULL, **nd = NULL;
1862 	mp_vertex_struct *vs = NULL, **vd = NULL;
1863 	mp_vertex_struct *tcs = NULL, **tcd = NULL;
1864 
1865 	mp_point_struct *mp_point;
1866 	mp_line_struct *mp_line;
1867 	mp_line_strip_struct *mp_line_strip;
1868 	mp_line_loop_struct *mp_line_loop;
1869 	mp_triangle_struct *mp_triangle;
1870 	mp_triangle_strip_struct *mp_triangle_strip;
1871 	mp_triangle_fan_struct *mp_triangle_fan;
1872 	mp_quad_struct *mp_quad;
1873 	mp_quad_strip_struct *mp_quad_strip;
1874 	mp_polygon_struct *mp_polygon;
1875 	mp_vertex_struct *first_normal_ptr = NULL, *n_ptr;
1876 
1877 	float x = 0.0f, y = 0.0f, z = 0.0f;
1878 	float tx = 0.0f, ty = 0.0f;
1879 
1880 
1881 	/* Handle by primitive type */
1882 	ptype = V3DMPGetType(p);
1883 	switch(ptype)
1884 	{
1885 	  case V3DMP_TYPE_POINT:
1886 	    mp_point = (mp_point_struct *)p;
1887 	    if(last_begin_primitive_type != ptype)
1888 	    {
1889 		vmodel->statements++;
1890 		vmodel->mem_size += 2 * sizeof(GLuint);
1891 		glBegin(GL_POINTS);
1892 		last_begin_primitive_type = ptype;
1893 	    }
1894 	    ns = &mp_point->n[0];
1895 	    vs = &mp_point->v[0];
1896 	    tcs = &mp_point->tc[0];
1897 	    v_total = V3DMP_POINT_NVERTEX;
1898 	    break;
1899 
1900 	  case V3DMP_TYPE_LINE:
1901 	    mp_line = (mp_line_struct *)p;
1902 	    if(last_begin_primitive_type != ptype)
1903 	    {
1904 		vmodel->statements++;
1905 		vmodel->mem_size += 2 * sizeof(GLuint);
1906 		glBegin(GL_LINES);
1907 		last_begin_primitive_type = ptype;
1908 	    }
1909 	    ns = &mp_line->n[0];
1910 	    vs = &mp_line->v[0];
1911 	    tcs = &mp_line->tc[0];
1912 	    v_total = V3DMP_LINE_NVERTEX;
1913 	    break;
1914 
1915 	  case V3DMP_TYPE_LINE_STRIP:
1916 	    mp_line_strip = (mp_line_strip_struct *)p;
1917 	    vmodel->statements++;
1918 	    vmodel->mem_size += 2 * sizeof(GLuint);
1919 	    glBegin(GL_LINE_STRIP);
1920 	    need_end_primitive = True;
1921 	    nd = mp_line_strip->n;
1922 	    vd = mp_line_strip->v;
1923 	    tcd = mp_line_strip->tc;
1924 	    v_total = mp_line_strip->total;
1925 	    break;
1926 
1927 	  case V3DMP_TYPE_LINE_LOOP:
1928 	    mp_line_loop = (mp_line_loop_struct *)p;
1929 	    vmodel->statements++;
1930 	    vmodel->mem_size += 2 * sizeof(GLuint);
1931 	    glBegin(GL_LINE_LOOP);
1932 	    need_end_primitive = True;
1933 	    nd = mp_line_loop->n;
1934 	    vd = mp_line_loop->v;
1935 	    tcd = mp_line_loop->tc;
1936 	    v_total = mp_line_loop->total;
1937 	    break;
1938 
1939 	  case V3DMP_TYPE_TRIANGLE:
1940 	    mp_triangle = (mp_triangle_struct *)p;
1941 	    if(last_begin_primitive_type != ptype)
1942 	    {
1943 		vmodel->statements++;
1944 		vmodel->mem_size += 2 * sizeof(GLuint);
1945 		glBegin(GL_TRIANGLES);
1946 		last_begin_primitive_type = ptype;
1947 	    }
1948 	    ns = &mp_triangle->n[0];
1949 	    vs = &mp_triangle->v[0];
1950 	    tcs = &mp_triangle->tc[0];
1951 	    v_total = V3DMP_TRIANGLE_NVERTEX;
1952 	    break;
1953 
1954 	  case V3DMP_TYPE_TRIANGLE_STRIP:
1955 	    mp_triangle_strip = (mp_triangle_strip_struct *)p;
1956 	    vmodel->statements++;
1957 	    vmodel->mem_size += 2 * sizeof(GLuint);
1958 	    glBegin(GL_TRIANGLE_STRIP);
1959 	    need_end_primitive = True;
1960 	    nd = mp_triangle_strip->n;
1961 	    vd = mp_triangle_strip->v;
1962 	    tcd = mp_triangle_strip->tc;
1963 	    v_total = mp_triangle_strip->total;
1964 	    break;
1965 
1966 	  case V3DMP_TYPE_TRIANGLE_FAN:
1967 	    mp_triangle_fan = (mp_triangle_fan_struct *)p;
1968 	    vmodel->statements++;
1969 	    vmodel->mem_size += 2 * sizeof(GLuint);
1970 	    glBegin(GL_TRIANGLE_FAN);
1971 	    need_end_primitive = True;
1972 	    nd = mp_triangle_fan->n;
1973 	    vd = mp_triangle_fan->v;
1974 	    tcd = mp_triangle_fan->tc;
1975 	    v_total = mp_triangle_fan->total;
1976 	    break;
1977 
1978 	  case V3DMP_TYPE_QUAD:
1979 	    mp_quad = (mp_quad_struct *)p;
1980 	    if(last_begin_primitive_type != ptype)
1981 	    {
1982 		vmodel->statements++;
1983 		vmodel->mem_size += 2 * sizeof(GLuint);
1984 		glBegin(GL_QUADS);
1985 		last_begin_primitive_type = ptype;
1986 	    }
1987 	    ns = &mp_quad->n[0];
1988 	    vs = &mp_quad->v[0];
1989 	    tcs = &mp_quad->tc[0];
1990 	    v_total = V3DMP_QUAD_NVERTEX;
1991 	    break;
1992 
1993 	  case V3DMP_TYPE_QUAD_STRIP:
1994 	    mp_quad_strip = (mp_quad_strip_struct *)p;
1995 	    vmodel->statements++;
1996 	    vmodel->mem_size += 2 * sizeof(GLuint);
1997 	    glBegin(GL_QUAD_STRIP);
1998 	    need_end_primitive = True;
1999 	    nd = mp_quad_strip->n;
2000 	    vd = mp_quad_strip->v;
2001 	    tcd = mp_quad_strip->tc;
2002 	    v_total = mp_quad_strip->total;
2003 	    break;
2004 
2005 	  case V3DMP_TYPE_POLYGON:
2006 	    mp_polygon = (mp_polygon_struct *)p;
2007 	    vmodel->statements++;
2008 	    vmodel->mem_size += 2 * sizeof(GLuint);
2009 	    glBegin(GL_POLYGON);
2010 	    need_end_primitive = True;
2011 	    nd = mp_polygon->n;
2012 	    vd = mp_polygon->v;
2013 	    tcd = mp_polygon->tc;
2014 	    v_total = mp_polygon->total;
2015 	    break;
2016 	}
2017 
2018 
2019 	/* Get pointer to first normal */
2020 	i = 0;
2021 	if(nd != NULL)
2022 	    first_normal_ptr = nd[i];
2023 	else if(ns != NULL)
2024 	    first_normal_ptr = &ns[i];
2025 	else
2026 	    first_normal_ptr = NULL;
2027 
2028 	/* Iterate through each vertex start from last to first,
2029 	 * becuase the winding stored on V3D model file is clockwise
2030 	 * and we need to handle it counter-clockwise
2031 	 */
2032 	for(i = v_total - 1; i >= 0; i--)
2033 	{
2034 	    /* Get vertex values but do not set just yet, we only need
2035 	     * the coordinates for now in order to set the texcoord
2036 	     * first (in case the texture is being plane oriented)
2037 	     */
2038 	    if((vd != NULL) ? (vd[i] != NULL) : False)
2039 	    {
2040 		const mp_vertex_struct *v = vd[i];
2041 		x = (float)v->x;
2042 		y = (float)v->y;
2043 		z = (float)v->z;
2044 	    }
2045 	    else if(vs != NULL)
2046 	    {
2047 		const mp_vertex_struct *v = &vs[i];
2048 		x = (float)v->x;
2049 		y = (float)v->y;
2050 		z = (float)v->z;
2051 	    }
2052 
2053 	    /* Get normal and make sure it is not a zero vector, if it
2054 	     * is then use the first normal (if any).
2055 	     */
2056 	    if(nd != NULL)
2057 	    {
2058 		n_ptr = nd[i];
2059 		if((n_ptr->x == 0.0) && (n_ptr->y == 0.0) && (n_ptr->z == 0.0))
2060 		    n_ptr = first_normal_ptr;
2061 	    }
2062 	    else if(ns != NULL)
2063 	    {
2064 		n_ptr = &ns[i];
2065 		if((n_ptr->x == 0.0) && (n_ptr->y == 0.0) && (n_ptr->z == 0.0))
2066 		    n_ptr = first_normal_ptr;
2067 	    }
2068 	    else
2069 	    {
2070 		n_ptr = first_normal_ptr;	/* All elese use first normal */
2071 	    }
2072 
2073 	    /* Got valid normal? */
2074 	    if(n_ptr != NULL)
2075 	    {
2076 		vmodel->statements++;
2077 		vmodel->mem_size += sizeof(GLuint) + (3 * sizeof(GLfloat));
2078 		glNormal3d(n_ptr->x, n_ptr->z, -n_ptr->y);
2079 	    }
2080 
2081 	    /* Texture enabled? */
2082 	    if(tex_on)
2083 	    {
2084 		/* Set texture coordinates */
2085 		switch(tex_orient)
2086 		{
2087 		  case TEX_ORIENT_XY:
2088 		    if(tex_coord.w > 0.0f)
2089 			tx = (x - tex_coord.i) / tex_coord.w;
2090 		    else
2091 			tx = 0.0f;
2092 		    if(tex_coord.h > 0.0f)
2093 			ty = (y - tex_coord.j) / tex_coord.h;
2094 		    else
2095 			ty = 0.0f;
2096 		    vmodel->statements++;
2097 		    vmodel->mem_size += sizeof(GLuint) + (2 * sizeof(GLfloat));
2098 		    glTexCoord2f(tx, 1.0f - ty);
2099 		    break;
2100 
2101 		  case TEX_ORIENT_YZ:
2102 		    if(tex_coord.w > 0.0f)
2103 			tx = -(y - tex_coord.i) / tex_coord.w;
2104 		    else
2105 			tx = 0.0f;
2106 		    if(tex_coord.h > 0.0f)
2107 			ty = (z - tex_coord.j) / tex_coord.h;
2108 		    else
2109 			ty = 0.0f;
2110 		    vmodel->statements++;
2111 		    vmodel->mem_size += sizeof(GLuint) + (2 * sizeof(GLfloat));
2112 		    glTexCoord2f(tx, 1.0f - ty);
2113 		    break;
2114 
2115 		  case TEX_ORIENT_XZ:
2116 		    if(tex_coord.w > 0.0f)
2117 			tx = (x - tex_coord.i) / tex_coord.w;
2118 		    else
2119 			tx = 0.0f;
2120 		    if(tex_coord.h > 0.0f)
2121 			ty = (z - tex_coord.j) / tex_coord.h;
2122 		    else
2123 			ty = 0.0f;
2124 		    vmodel->statements++;
2125 		    vmodel->mem_size += sizeof(GLuint) + (2 * sizeof(GLfloat));
2126 		    glTexCoord2f(tx, 1.0f - ty);
2127 		    break;
2128 
2129 		  default:
2130 		    if(tcd != NULL)
2131 		    {
2132 			if(tcd[i] != NULL)
2133 			{
2134 			    tx = (float)tcd[i]->x;
2135 			    ty = (float)tcd[i]->y;
2136 			}
2137 		    }
2138 		    else if(tcs != NULL)
2139 		    {
2140 			tx = (float)tcs[i].x;
2141 			ty = (float)tcs[i].y;
2142 		    }
2143 
2144 		    vmodel->statements++;
2145 		    vmodel->mem_size += sizeof(GLuint) + (2 * sizeof(GLfloat));
2146 		    glTexCoord2f(tx, 1.0f - ty);
2147 		    break;
2148 		}
2149 	    }
2150 
2151 	    /* Set vertex */
2152 	    vmodel->statements++;
2153 	    vmodel->mem_size += sizeof(GLuint) + (3 * sizeof(GLfloat));
2154 	    glVertex3d(x, z, -y);
2155 	}
2156 
2157 	/* End primitive as needed */
2158 	if(need_end_primitive)
2159 	{
2160 	    vmodel->statements++;
2161 	    vmodel->mem_size += sizeof(GLuint);
2162 	    glEnd();
2163 	    need_end_primitive = False;
2164 	}
2165 }
2166 
2167 /*
2168  *	Called by SARObjLoadFromFile().
2169  *
2170  *	Processes the visual primitives in the V3D Visual Model into GL
2171  *	commands (suitable for GL list recording).
2172  *
2173  *	The vmodel's memory size statistics will be updated.
2174  */
SARObjLoadProcessVisualModel(sar_core_struct * core_ptr,int obj_num,sar_object_struct * obj_ptr,sar_visual_model_struct * vmodel,v3d_model_struct * v3d_model,Boolean process_as_ir,const char * filename,int line_num)2175 static void SARObjLoadProcessVisualModel(
2176 	sar_core_struct *core_ptr,
2177 	int obj_num, sar_object_struct *obj_ptr,
2178 	sar_visual_model_struct *vmodel,
2179 	v3d_model_struct *v3d_model,
2180 	Boolean process_as_ir,
2181 	const char *filename, int line_num
2182 )
2183 {
2184 	int pn, ptype;
2185 	void *p;
2186 
2187 	StateGLBoolean blend_state = False;
2188 
2189 	GLuint list = (GLuint)vmodel->data;
2190 	gw_display_struct *display = core_ptr->display;
2191 	state_gl_struct *state_gl = &display->state_gl;
2192 	sar_scene_struct *scene = core_ptr->scene;
2193 
2194 
2195 	/* Reset GL states */
2196 	V3DTextureSelect(NULL);
2197 	tex_on = False;
2198 	tex_orient = TEX_ORIENT_NONE;
2199 /* Do not reset color, model file should set it, if not then it means
2200  * it relys on other code to set it
2201  *
2202 	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
2203  */
2204 
2205 	/* Reset last begin primitive type */
2206 	last_begin_primitive_type = -1;
2207 
2208 	/* Set initial GL states for IR processing? */
2209 	if(process_as_ir)
2210 	{
2211 
2212 	}
2213 
2214 	/* Iterate through each V3D model primitive */
2215 	for(pn = 0; pn < v3d_model->total_primitives; pn++)
2216 	{
2217 	    p = v3d_model->primitive[pn];
2218 	    if(p == NULL)
2219 		continue;
2220 
2221 	    /* Get primitive type */
2222 	    ptype = V3DMPGetType(p);
2223 
2224 	    /* Check if last begin primitive differs from this one
2225 	     *
2226 	     * If it does then glEnd() needs to be called to end the
2227 	     * first glBegin() that was not ended
2228 	     */
2229 	    if((last_begin_primitive_type != ptype) &&
2230 	       (last_begin_primitive_type > -1)
2231 	    )
2232 	    {
2233 		glEnd();
2234 		vmodel->mem_size += sizeof(GLuint);
2235 		vmodel->statements++;
2236 		last_begin_primitive_type = -1;
2237 	    }
2238 
2239 	    switch(ptype)
2240 	    {
2241 	        const mp_comment_struct *mp_comment;
2242 		const mp_color_struct *mp_color;
2243 		const mp_texture_select_struct *mp_texture_select;
2244 		const mp_texture_orient_xy_struct *mp_texture_xy;
2245 		const mp_texture_orient_yz_struct *mp_texture_yz;
2246 		const mp_texture_orient_xz_struct *mp_texture_xz;
2247 		const mp_heightfield_load_struct *mp_heightfield_load;
2248 
2249 	      case V3DMP_TYPE_COMMENT:
2250 		mp_comment = (mp_comment_struct *)p;
2251 		/* Ignore comment lines in V3D visual models */
2252 		break;
2253 
2254 	      case V3DMP_TYPE_POINT:
2255 	      case V3DMP_TYPE_LINE:
2256 	      case V3DMP_TYPE_LINE_STRIP:
2257 	      case V3DMP_TYPE_LINE_LOOP:
2258 	      case V3DMP_TYPE_TRIANGLE:
2259 	      case V3DMP_TYPE_TRIANGLE_STRIP:
2260 	      case V3DMP_TYPE_TRIANGLE_FAN:
2261 	      case V3DMP_TYPE_QUAD:
2262 	      case V3DMP_TYPE_QUAD_STRIP:
2263 	      case V3DMP_TYPE_POLYGON:
2264 		vmodel->primitives++;
2265 		SARObjLoadProcessVisualPrimitive(
2266 		    core_ptr, obj_num, obj_ptr, vmodel,
2267 		    p, filename, 0
2268 		);
2269 		break;
2270 
2271 	      case V3DMP_TYPE_COLOR:
2272 		mp_color = (mp_color_struct *)p;
2273 		if(process_as_ir)
2274 		    break;
2275 		vmodel->mem_size += 4 * sizeof(GLfloat);
2276 		vmodel->statements++;
2277 		glColor4f(
2278 		    (GLfloat)mp_color->r,
2279 		    (GLfloat)mp_color->g,
2280 		    (GLfloat)mp_color->b,
2281 		    (GLfloat)mp_color->a
2282 		);
2283 		/* Enable GL_BLEND if alpha is less than 1.0 */
2284 		if(mp_color->a < 1.0f)
2285 		{
2286 		    if(!blend_state)
2287 		    {
2288 			vmodel->mem_size += (3 * sizeof(GLuint)) +
2289 			    (2 * sizeof(GLuint));
2290 			vmodel->statements += 3;
2291 			StateGLEnableF(state_gl, GL_BLEND, GL_TRUE);
2292 			glBlendFunc(
2293 			    GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
2294 			);
2295 			StateGLDisableF(
2296 			    state_gl, GL_ALPHA_TEST, GL_TRUE
2297 			);
2298 			blend_state = True;
2299 		    }
2300 		}
2301 		else
2302 		{
2303 		    if(blend_state)
2304 		    {
2305 			vmodel->mem_size += 2 * sizeof(GLuint);
2306 			vmodel->statements += 2;
2307 			StateGLDisableF(state_gl, GL_BLEND, GL_TRUE);
2308 			StateGLEnableF(
2309 			    state_gl, GL_ALPHA_TEST, GL_TRUE
2310 			);
2311 			blend_state = False;
2312 		    }
2313 		}
2314 		break;
2315 
2316 	      case V3DMP_TYPE_TEXTURE_SELECT:
2317 		mp_texture_select = (mp_texture_select_struct *)p;
2318 		if(!process_as_ir)
2319 		{
2320 		    const char *texture_name = mp_texture_select->name;
2321 		    vmodel->mem_size += 2 * sizeof(GLuint);
2322 		    vmodel->statements++;
2323 		    if(STRISEMPTY(texture_name))
2324 		    {
2325 			/* Empty string implies unselect texture */
2326 			V3DTextureSelect(NULL);
2327 			tex_on = False;
2328 			tex_orient = TEX_ORIENT_NONE;
2329 		    }
2330 		    else
2331 		    {
2332 			/* Select texture */
2333 			v3d_texture_ref_struct *t = SARGetTextureRefByName(
2334 			    scene, texture_name
2335 			);
2336 			if(t == NULL)
2337 			{
2338 			    fprintf(
2339 				stderr,
2340  "%s: Warning: Texture \"%s\" not defined (on the model or globally).\n",
2341 				filename, texture_name
2342 			    );
2343 			    tex_on = False;
2344 			    tex_orient = TEX_ORIENT_NONE;
2345 			}
2346 			else
2347 			{
2348 			    tex_on = True;
2349 			    V3DTextureSelect(t);
2350 			}
2351 		    }
2352 		}
2353 		break;
2354 
2355 	      case V3DMP_TYPE_TEXTURE_ORIENT_XY:
2356 		mp_texture_xy = (mp_texture_orient_xy_struct *)p;
2357 		if(process_as_ir)
2358 		    break;
2359 		tex_orient = TEX_ORIENT_XY;
2360 		tex_coord.i = (float)mp_texture_xy->x;
2361 		tex_coord.j = (float)mp_texture_xy->y;
2362 		tex_coord.w = (float)mp_texture_xy->dx;
2363 		tex_coord.h = (float)mp_texture_xy->dy;
2364 		break;
2365 
2366 	      case V3DMP_TYPE_TEXTURE_ORIENT_YZ:
2367 		mp_texture_yz = (mp_texture_orient_yz_struct *)p;
2368 		if(process_as_ir)
2369 		    break;
2370 		tex_orient = TEX_ORIENT_YZ;
2371 		tex_coord.i = (float)mp_texture_yz->y;
2372 		tex_coord.j = (float)mp_texture_yz->z;
2373 		tex_coord.w = (float)mp_texture_yz->dy;
2374 		tex_coord.h = (float)mp_texture_yz->dz;
2375 		break;
2376 
2377 	      case V3DMP_TYPE_TEXTURE_ORIENT_XZ:
2378 		mp_texture_xz = (mp_texture_orient_xz_struct *)p;
2379 		if(process_as_ir)
2380 		    break;
2381 		tex_orient = TEX_ORIENT_XZ;
2382 		tex_coord.i = (float)mp_texture_xz->x;
2383 		tex_coord.j = (float)mp_texture_xz->z;
2384 		tex_coord.w = (float)mp_texture_xz->dx;
2385 		tex_coord.h = (float)mp_texture_xz->dz;
2386 		break;
2387 
2388 	      case V3DMP_TYPE_TEXTURE_OFF:
2389 		if(process_as_ir)
2390 		    break;
2391 		vmodel->mem_size += 2 * sizeof(GLuint);
2392 		vmodel->statements++;
2393 		V3DTextureSelect(NULL);
2394 		tex_on = False;
2395 		tex_orient = TEX_ORIENT_NONE;
2396 		break;
2397 
2398 	      case V3DMP_TYPE_HEIGHTFIELD_LOAD:
2399 		mp_heightfield_load = (mp_heightfield_load_struct *)p;
2400 		SARObjLoadHeightField(
2401 		    core_ptr, obj_num, obj_ptr, vmodel,
2402 		    p, filename, 0,
2403 		    list
2404 		);
2405 		break;
2406 	    }
2407 	}	/* Iterate through each V3D model primitive */
2408 
2409 	/* Check if last_begin_primitive_type is valid, if it is then
2410 	 * that means we need to call glEnd() to end a previous
2411 	 * glBegin()
2412 	 */
2413 	if(last_begin_primitive_type > -1)
2414 	{
2415 	    vmodel->mem_size += sizeof(GLuint);
2416 	    vmodel->statements++;
2417 	    glEnd();
2418 	    last_begin_primitive_type = -1;
2419 	}
2420 
2421 	/* Check if blending is still enabled */
2422 	if(blend_state)
2423 	{
2424 	    vmodel->mem_size += 2 * sizeof(GLuint);
2425 	    vmodel->statements += 2;
2426 	    StateGLDisableF(state_gl, GL_BLEND, GL_TRUE);
2427 	    StateGLEnableF(state_gl, GL_ALPHA_TEST, GL_TRUE);
2428 	    blend_state = False;
2429 	}
2430 
2431 	/* End GL display list */
2432 	glEndList();
2433 
2434 	/* Mark this SAR Visual Model as finished loading */
2435 	vmodel->load_state = SAR_VISUAL_MODEL_LOADED;
2436 
2437 }
2438 
2439 
2440 /*
2441  *	Called by SARObjLoadFromFile().
2442  *
2443  *	Handles "other data" lines from V3D Models.
2444  */
SARObjLoadLine(sar_core_struct * core_ptr,int obj_num,sar_object_struct * obj_ptr,const char * line,const char * filename,int line_num)2445 static void SARObjLoadLine(
2446 	sar_core_struct *core_ptr,
2447 	int obj_num, sar_object_struct *obj_ptr,
2448 	const char *line, const char *filename, int line_num
2449 )
2450 {
2451 	char *s;
2452 	char parm[256];
2453 	const char *arg;
2454 
2455 	sar_object_aircraft_struct	*aircraft = NULL;
2456 	sar_object_ground_struct	*ground = NULL;
2457 	sar_obj_rotor_struct	*rotor = NULL;
2458 	sar_obj_part_struct	*aileron_left_ptr = NULL,
2459 				*aileron_right_ptr = NULL,
2460 				*rudder_top_ptr = NULL,
2461 				*rudder_bottom_ptr = NULL,
2462 				*elevator_ptr = NULL,
2463 				*cannard_ptr = NULL,
2464 				*aileron_elevator_left_ptr = NULL,
2465 				*aileron_elevator_right_ptr = NULL,
2466 				*flap_ptr = NULL,
2467 				*abrake_ptr = NULL,
2468 				*door_ptr = NULL,
2469 				*lgear_ptr = NULL;
2470 	sar_external_fueltank_struct *eft_ptr = NULL;
2471 	sar_scene_struct *scene = core_ptr->scene;
2472 	Boolean is_player = (scene->player_obj_ptr == obj_ptr) ? True : False;
2473 
2474 	if(STRISEMPTY(line))
2475 	    return;
2476 
2477 	/* Seek past spaces */
2478 	while(ISBLANK(*line))
2479 	    line++;
2480 
2481 	/* Skip comments */
2482 	if(ISCOMMENT(*line))
2483 	    return;
2484 
2485 	/* Get object type specific values */
2486 	switch(obj_ptr->type)
2487 	{
2488 	  case SAR_OBJ_TYPE_GARBAGE:
2489 	  case SAR_OBJ_TYPE_STATIC:
2490 	  case SAR_OBJ_TYPE_AUTOMOBILE:
2491 	  case SAR_OBJ_TYPE_WATERCRAFT:
2492 	    break;
2493 	  case SAR_OBJ_TYPE_AIRCRAFT:
2494 	    aircraft = SAR_OBJ_GET_AIRCRAFT(obj_ptr);
2495 	    if(aircraft != NULL)
2496 	    {
2497 		int i = aircraft->total_rotors;
2498 		if(i > 0)
2499 		    rotor = aircraft->rotor[i - 1];
2500 	    }
2501 	    break;
2502 	  case SAR_OBJ_TYPE_GROUND:
2503 	    ground = SAR_OBJ_GET_GROUND(obj_ptr);
2504 	    break;
2505 	  case SAR_OBJ_TYPE_RUNWAY:
2506 	  case SAR_OBJ_TYPE_HELIPAD:
2507 	  case SAR_OBJ_TYPE_HUMAN:
2508 	  case SAR_OBJ_TYPE_SMOKE:
2509 	  case SAR_OBJ_TYPE_FIRE:
2510 	  case SAR_OBJ_TYPE_EXPLOSION:
2511 	  case SAR_OBJ_TYPE_CHEMICAL_SPRAY:
2512 	  case SAR_OBJ_TYPE_FUELTANK:
2513 	  case SAR_OBJ_TYPE_PREMODELED:
2514 	    break;
2515 	}
2516 	/* Get pointers to last (newest) object parts of each type */
2517 	if(True)
2518 	{
2519 	    sar_obj_part_struct *part;
2520 
2521 #define GET_PART(_type_)	{		\
2522  int i = 0;					\
2523  sar_obj_part_struct *p;			\
2524  part = NULL;					\
2525  while(True) {					\
2526   p = SARObjGetPartPtr(obj_ptr, (_type_), i);	\
2527   if(p != NULL) {				\
2528    part = p; i++;				\
2529   } else {					\
2530    break;					\
2531   }						\
2532  }						\
2533 }
2534 	    GET_PART(SAR_OBJ_PART_TYPE_AILERON_LEFT);
2535 	    aileron_left_ptr = part;
2536 
2537 	    GET_PART(SAR_OBJ_PART_TYPE_AILERON_RIGHT);
2538 	    aileron_right_ptr = part;
2539 
2540 	    GET_PART(SAR_OBJ_PART_TYPE_RUDDER_TOP);
2541 	    rudder_top_ptr = part;
2542 
2543 	    GET_PART(SAR_OBJ_PART_TYPE_RUDDER_BOTTOM);
2544 	    rudder_bottom_ptr = part;
2545 
2546 	    GET_PART(SAR_OBJ_PART_TYPE_ELEVATOR);
2547 	    elevator_ptr = part;
2548 
2549 	    GET_PART(SAR_OBJ_PART_TYPE_CANNARD);
2550 	    cannard_ptr = part;
2551 
2552 	    GET_PART(SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_LEFT);
2553 	    aileron_elevator_left_ptr = part;
2554 
2555 	    GET_PART(SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_RIGHT);
2556 	    aileron_elevator_right_ptr = part;
2557 
2558 	    GET_PART(SAR_OBJ_PART_TYPE_FLAP);
2559 	    flap_ptr = part;
2560 
2561 	    GET_PART(SAR_OBJ_PART_TYPE_LANDING_GEAR);
2562 	    lgear_ptr = part;
2563 
2564 	    GET_PART(SAR_OBJ_PART_TYPE_DOOR_RESCUE);
2565 	    door_ptr = part;
2566 
2567 #undef GET_PART
2568 	}
2569 
2570 
2571 	/* Begin parsing line */
2572 
2573 	/* Get parameter */
2574 	strncpy(parm, line, 256);
2575 	parm[256 - 1] = '\0';
2576 	s = parm;
2577 	while(!ISBLANK(*s) && (*s != '\0'))
2578 	    s++;
2579 	*s = '\0';
2580 
2581 	/* Seek to start of argument(s) in the line */
2582 	arg = line;
2583 	while(!ISBLANK(*arg) && (*arg != '\0'))
2584 	    arg++;
2585 	while(ISBLANK(*arg))
2586 	    arg++;
2587 
2588 	/* Begin handling parameter */
2589 	if(True)
2590 	{
2591 	    /* Texture Base Directory */
2592 	    if(!strcasecmp(parm, "texture_base_directory") ||
2593 	       !strcasecmp(parm, "texture_base_dir")
2594 	    )
2595 	    {
2596 		/* Ignore since it is loaded from the V3D header */
2597 	    }
2598 	    /* Texture Load */
2599 	    else if(!strcasecmp(parm, "texture_load"))
2600 	    {
2601 		/* Ignore since it is loaded from the V3D header */
2602 	    }
2603 	    /* Version */
2604 	    else if(!strcasecmp(parm, "version"))
2605 	    {
2606 		/* Arguments:
2607 		 *
2608 		 * <major> <minor> <release>
2609 		 */
2610 		int	major = 0,
2611 			minor = 0,
2612 			release = 0;
2613 
2614 		arg = GET_ARG_I(arg, &major);
2615 		arg = GET_ARG_I(arg, &minor);
2616 		arg = GET_ARG_I(arg, &release);
2617 
2618 		if((major > PROG_VERSION_MAJOR) ||
2619 		   (minor > PROG_VERSION_MINOR) ||
2620 		   (release > PROG_VERSION_RELEASE)
2621 		)
2622 		{
2623 		    Boolean need_warn = False;
2624 		    if(major > PROG_VERSION_MAJOR)
2625 			need_warn = True;
2626 		    else if((major == PROG_VERSION_MAJOR) &&
2627 			    (minor > PROG_VERSION_MINOR)
2628 		    )
2629 			need_warn = True;
2630 		    else if((major == PROG_VERSION_MAJOR) &&
2631 			    (minor == PROG_VERSION_MINOR) &&
2632 			    (release == PROG_VERSION_RELEASE)
2633 		    )
2634 			need_warn = True;
2635 		    if(need_warn)
2636 			fprintf(
2637 			    stderr,
2638  "%s: Line %i: Warning:\
2639  File format version %i.%i.%i is newer than\
2640  program version %i.%i.%i.\n",
2641 			    filename, line_num,
2642 			    major, minor, release,
2643 			    PROG_VERSION_MAJOR, PROG_VERSION_MINOR, PROG_VERSION_RELEASE
2644 			);
2645 		}
2646 	    }
2647 	    /* Name */
2648 	    else if(!strcasecmp(parm, "name"))
2649 	    {
2650 		/* Arguments:
2651 		 *
2652 		 * <name>
2653 		 */
2654 		char *name = NULL;
2655 
2656 		arg = GET_ARG_S(arg, &name);
2657 
2658 		free(obj_ptr->name);
2659 		obj_ptr->name = name;
2660 	    }
2661 	    /* Description */
2662 	    else if(!strcasecmp(parm, "desc") ||
2663 		    !strcasecmp(parm, "description")
2664 	    )
2665 	    {
2666 		/* Ignore */
2667 	    }
2668 	    /* Type */
2669 	    else if(!strcasecmp(parm, "type"))
2670 	    {
2671 		/* Ignore since the object type should already be set
2672 		 * prior to calling this function
2673 		 */
2674 	    }
2675 	    /* Range */
2676 	    else if(!strcasecmp(parm, "range"))
2677 	    {
2678 		/* Arguments:
2679 		 *
2680 		 * <range>
2681 		 */
2682 		float range = 0.0f;
2683 
2684 		arg = GET_ARG_F(arg, &range);
2685 
2686 		if(range < 0.0f)
2687 		    fprintf(stderr,
2688  "%s: Line %i: Warning:\
2689  Value for %s argument range=%f should not be negative.\n",
2690 			filename, line_num,
2691 			parm, range
2692 		    );
2693 
2694 		obj_ptr->range = (float)MAX(range, 0.0);
2695 	    }
2696 	    /* Range Far */
2697 	    else if(!strcasecmp(parm, "range_far"))
2698 	    {
2699 		/* Arguments:
2700 		 *
2701 		 * <range_far>
2702 		 */
2703 		float range_far = 0.0f;
2704 
2705 		arg = GET_ARG_F(arg, &range_far);
2706 
2707 		if(range_far < 0.0f)
2708 		    fprintf(stderr,
2709  "%s: Line %i: Warning:\
2710  Value for %s argument range_far=%f should not be negative.\n",
2711 			filename, line_num,
2712 			parm, range_far
2713 		    );
2714 
2715 		obj_ptr->range_far = (float)MAX(range_far, 0.0);
2716 	    }
2717 	    /* No Depth Test */
2718 	    else if(!strcasecmp(parm, "no_depth_test"))
2719 	    {
2720 		obj_ptr->flags |= SAR_OBJ_FLAG_NO_DEPTH_TEST;
2721 	    }
2722 	    /* Smooth Shading */
2723 	    else if(!strcasecmp(parm, "shade_model_smooth"))
2724 	    {
2725 		obj_ptr->flags |= SAR_OBJ_FLAG_SHADE_MODEL_SMOOTH;
2726 	    }
2727 	    /* Flat Shading */
2728 	    else if(!strcasecmp(parm, "shade_model_flat"))
2729 	    {
2730 		obj_ptr->flags &= ~SAR_OBJ_FLAG_SHADE_MODEL_SMOOTH;
2731 	    }
2732 	    /* Offset Polygons */
2733 	    else if(!strcasecmp(parm, "offset_polygons"))
2734 	    {
2735 		obj_ptr->flags |= SAR_OBJ_FLAG_POLYGON_OFFSET;
2736 	    }
2737 	    /* Show Night Model At Dawn */
2738 	    else if(!strcasecmp(parm, "show_night_model_at_dawn"))
2739 	    {
2740 		obj_ptr->flags |= SAR_OBJ_FLAG_NIGHT_MODEL_AT_DAWN;
2741 	    }
2742 	    /* Show Night Model At Dusk */
2743 	    else if(!strcasecmp(parm, "show_night_model_at_dusk"))
2744 	    {
2745 		obj_ptr->flags |= SAR_OBJ_FLAG_NIGHT_MODEL_AT_DUSK;
2746 	    }
2747 	    /* Show Far Model Day Only */
2748 	    else if(!strcasecmp(parm, "show_far_day_only"))
2749 	    {
2750 		obj_ptr->flags |= SAR_OBJ_FLAG_FAR_MODEL_DAY_ONLY;
2751 	    }
2752 	    /* Crash Flags */
2753 	    else if(!strcasecmp(parm, "crash_flags"))
2754 	    {
2755 		/* Arguments:
2756 		 *
2757 		 * <can_crash?> <causes_crash?> <support_surface?>
2758 		 * <crash_type>
2759 		 */
2760 		sar_contact_bounds_struct *cb = obj_ptr->contact_bounds;
2761 		Boolean	can_crash = False,
2762 			causes_crash = False,
2763 			support_surface = False;
2764 		int	crash_type;
2765 
2766 		arg = GET_ARG_B(arg, &can_crash);
2767 		arg = GET_ARG_B(arg, &causes_crash);
2768 		arg = GET_ARG_B(arg, &support_surface);
2769 		arg = GET_ARG_I(arg, &crash_type);
2770 
2771 		/* Create contact bounds as needed */
2772 		if(cb == NULL)
2773 		    obj_ptr->contact_bounds = cb = SAR_CONTACT_BOUNDS(
2774 			calloc(1, sizeof(sar_contact_bounds_struct))
2775 		    );
2776 
2777 		if(cb != NULL)
2778 		{
2779 		    cb->crash_flags = 0;	/* Must reset flags */
2780 
2781 		    /* Crash into other objects? */
2782 		    if(can_crash)
2783 			cb->crash_flags |= SAR_CRASH_FLAG_CRASH_OTHER;
2784 
2785 		    /* Causes crash? */
2786 		    if(causes_crash)
2787 			cb->crash_flags |= SAR_CRASH_FLAG_CRASH_CAUSE;
2788 
2789 		    /* Support surface? */
2790 		    if(support_surface)
2791 			cb->crash_flags |= SAR_CRASH_FLAG_SUPPORT_SURFACE;
2792 
2793 		    /* Crash type code */
2794 		    cb->crash_type = crash_type;
2795 		}
2796 	    }
2797 	    /* Contact Bounds Spherical */
2798 	    else if(!strcasecmp(parm, "contact_spherical"))
2799 	    {
2800 		/* Arguments:
2801 		 *
2802 		 * <radius>
2803 		 */
2804 		sar_contact_bounds_struct *cb = obj_ptr->contact_bounds;
2805 		float radius = 0.0f;
2806 
2807 		arg = GET_ARG_F(arg, &radius);
2808 
2809 		if(radius < 0.0f)
2810 		    fprintf(stderr,
2811  "%s: Line %i: Warning:\
2812  Value for %s argument radius=%f should not be negative.\n",
2813 			filename, line_num,
2814 			parm, radius
2815 		    );
2816 
2817 		SARObjAddContactBoundsSpherical(
2818 		    obj_ptr,
2819 		    (cb != NULL) ? cb->crash_flags : 0,
2820 		    (cb != NULL) ? cb->crash_type : 0,
2821 		    radius
2822 		);
2823 	    }
2824 	    /* Contact Bounds Cylendical */
2825 	    else if(!strcasecmp(parm, "contact_cylendrical"))
2826 	    {
2827 		/* Arguments:
2828 		 *
2829 		 * <radius> <height_min> <height_max>
2830 		 */
2831 		sar_contact_bounds_struct *cb = obj_ptr->contact_bounds;
2832 		float	radius = 0.0f,
2833 			height_min = 0.0f,
2834 			height_max = 0.0f;
2835 
2836 		arg = GET_ARG_F(arg, &radius);
2837 		arg = GET_ARG_F(arg, &height_min);
2838 		arg = GET_ARG_F(arg, &height_max);
2839 
2840 		if(radius < 0.0f)
2841 		    fprintf(stderr,
2842  "%s: Line %i: Warning:\
2843  Value for %s argument radius=%f should not be negative.\n",
2844 			filename, line_num,
2845 			parm, radius
2846 		    );
2847 		if(height_min > height_max)
2848 		    fprintf(stderr,
2849  "%s: Line %i: Warning:\
2850  Value for %s argument height_min=%f is greater than height_max=%f.\n",
2851 			filename, line_num,
2852 			parm, height_min, height_max
2853 		    );
2854 
2855 		SARObjAddContactBoundsCylendrical(
2856 		    obj_ptr,
2857 		    (cb != NULL) ? cb->crash_flags : 0,
2858 		    (cb != NULL) ? cb->crash_type : 0,
2859 		    radius, height_min, height_max
2860 		);
2861 	    }
2862 	    /* Contact Bounds Rectangular */
2863 	    else if(!strcasecmp(parm, "contact_rectangular"))
2864 	    {
2865 		/* Arguments:
2866 		 *
2867 		 * <x_min> <x_max>
2868 		 * <y_min> <y_max>
2869 		 * <z_min> <z_max>
2870 		 */
2871 		sar_contact_bounds_struct *cb = obj_ptr->contact_bounds;
2872 		float	x_min = 0.0f,
2873 			x_max = 0.0f,
2874 			y_min = 0.0f,
2875 			y_max = 0.0f,
2876 			z_min = 0.0f,
2877 			z_max = 0.0f;
2878 
2879 		arg = GET_ARG_F(arg, &x_min);
2880 		arg = GET_ARG_F(arg, &x_max);
2881 		arg = GET_ARG_F(arg, &y_min);
2882 		arg = GET_ARG_F(arg, &y_max);
2883 		arg = GET_ARG_F(arg, &z_min);
2884 		arg = GET_ARG_F(arg, &z_max);
2885 
2886 		if(x_min > x_max)
2887 		    fprintf(stderr,
2888  "%s: Line %i: Warning:\
2889  Value for %s argument x_min=%f is greater than x_max=%f.\n",
2890 			filename, line_num,
2891 			parm, x_min, x_max
2892 		    );
2893 		if(y_min > y_max)
2894 		    fprintf(stderr,
2895  "%s: Line %i: Warning:\
2896  Value for %s argument y_min=%f is greater than y_max=%f.\n",
2897 			filename, line_num,
2898 			parm, y_min, y_max
2899 		    );
2900 		if(z_min > z_max)
2901 		    fprintf(stderr,
2902  "%s: Line %i: Warning:\
2903  Value for %s argument z_min=%f is greater than z_max=%f.\n",
2904 			filename, line_num,
2905 			parm, z_min, z_max
2906 		    );
2907 
2908 		SARObjAddContactBoundsRectangular(
2909 		    obj_ptr,
2910 		    (cb != NULL) ? cb->crash_flags : 0,
2911 		    (cb != NULL) ? cb->crash_type : 0,
2912 		    x_min, x_max,
2913 		    y_min, y_max,
2914 		    z_min, z_max
2915 		);
2916 	    }
2917 	    /* Temperature */
2918 	    else if(!strcasecmp(parm, "temperature"))
2919 	    {
2920 		/* Arguments:
2921 		 *
2922 		 * <temperature_day> <temperature_dusk/dawn>
2923 		 * <temperature_night>
2924 		 */
2925 		float	temperature_day = SAR_DEF_TEMPERATURE,
2926 			temperature_dusk = SAR_DEF_TEMPERATURE,
2927 			temperature_night = SAR_DEF_TEMPERATURE;
2928 		arg = GET_ARG_F(arg, &temperature_day);
2929 		arg = GET_ARG_F(arg, &temperature_dusk);
2930 		arg = GET_ARG_F(arg, &temperature_night);
2931 
2932 		if((temperature_day < 0.0f) || (temperature_day > 1.0f))
2933 		    fprintf(stderr,
2934  "%s: Line %i: Warning:\
2935  Value for %s argument temperature_day=%f is out of range (0.0 to 1.0).\n",
2936 			filename, line_num,
2937 			parm, temperature_day
2938 		    );
2939 		if((temperature_dusk < 0.0f) || (temperature_dusk > 1.0f))
2940 		    fprintf(stderr,
2941  "%s: Line %i: Warning:\
2942  Value for %s argument temperature_dusk=%f is out of range (0.0 to 1.0).\n",
2943 			filename, line_num,
2944 			parm, temperature_dusk
2945 		    );
2946 		if((temperature_night < 0.0f) || (temperature_night > 1.0f))
2947 		    fprintf(stderr,
2948  "%s: Line %i: Warning:\
2949  Value for %s argument temperature_night=%f is out of range (0.0 to 1.0).\n",
2950 			filename, line_num,
2951 			parm, temperature_night
2952 		    );
2953 
2954 		obj_ptr->temperature = CLIP(temperature_day, 0.0f, 1.0f);
2955 	    }
2956 	    /* Speed */
2957 	    else if(!strcasecmp(parm, "speed"))
2958 	    {
2959 		/* Arguments:
2960 		 *
2961 		 * <speed_stall> <speed_max>
2962 		 * <min_drag>
2963 		 * <overspeed_expected> <overspeed>
2964 		 */
2965 		float	speed_stall = 0.0f,
2966 			speed_max = 0.0f,
2967 			min_drag = 0.0f,
2968 			overspeed_expected = 0.0f,
2969 			overspeed = 0.0f;
2970 
2971 		arg = GET_ARG_F(arg, &speed_stall);
2972 		arg = GET_ARG_F(arg, &speed_max);
2973 		arg = GET_ARG_F(arg, &min_drag);
2974 		arg = GET_ARG_F(arg, &overspeed_expected);
2975 		arg = GET_ARG_F(arg, &overspeed);
2976 
2977 		if(speed_max < 0.0f)
2978 		    fprintf(stderr,
2979  "%s: Line %i: Warning:\
2980  Value for %s argument speed_max=%f should not be negative.\n",
2981 			filename, line_num,
2982 			parm, speed_max
2983 		    );
2984 		if(min_drag < 0.0f)
2985 		    fprintf(stderr,
2986  "%s: Line %i: Warning:\
2987  Value for %s argument min_drag=%f should not be negative.\n",
2988 			filename, line_num,
2989 			parm, min_drag
2990 		    );
2991 		if(overspeed_expected > overspeed)
2992 		    fprintf(stderr,
2993  "%s: Line %i: Warning:\
2994  Value for %s argument overspeed_expected=%f should not be\
2995  greater than the value for speed argument overspeed=%f.\n",
2996 			filename, line_num,
2997 			parm, overspeed_expected, overspeed
2998 		    );
2999 
3000 		if(aircraft != NULL)
3001 		{
3002 		    aircraft->speed_stall = (float)SFMMPHToMPC(speed_stall);
3003 		    aircraft->speed_max = (float)SFMMPHToMPC(speed_max);
3004 		    aircraft->min_drag = (float)SFMMPHToMPC(min_drag);
3005 		    aircraft->overspeed_expected = (float)SFMMPHToMPC(overspeed_expected);
3006 		    aircraft->overspeed = (float)SFMMPHToMPC(overspeed);
3007 		}
3008 	    }
3009 	    /* Air Brakes */
3010 	    else if(!strcasecmp(parm, "air_brakes"))
3011 	    {
3012 		/* Arguments:
3013 		 *
3014 		 * <drag_rate>
3015 		 */
3016 		float	drag_rate = 0.0f;
3017 
3018 		arg = GET_ARG_F(arg, &drag_rate);
3019 
3020 		if(aircraft != NULL)
3021 		{
3022 		    aircraft->air_brakes_rate = (float)SFMMPHToMPC(drag_rate);
3023 		    if(aircraft->air_brakes_rate <= 0.0f)
3024 			aircraft->air_brakes_state = -1;
3025 		    else
3026 			aircraft->air_brakes_state = 0;
3027 		}
3028 	    }
3029 
3030 	    /* Helicopter Acceleration Responsiveness */
3031 	    else if(!strcasecmp(parm, "helicopter_accelresp") ||
3032 		    !strcasecmp(parm, "helicopter_acceleration_responsiveness")
3033 	    )
3034 	    {
3035 		/* Arguments:
3036 		 *
3037 		 * <i> <j> <k>
3038 		 */
3039 		sar_position_struct	a;
3040 
3041 		arg = GET_ARG_POS(arg, &a);
3042 
3043 		if(a.x <= 0.0f)
3044 		    fprintf(stderr,
3045  "%s: Line %i: Warning:\
3046  Value for %s argument i=%f is not positive.\n",
3047 			filename, line_num,
3048 			parm, a.x
3049 		    );
3050 		if(a.y <= 0.0f)
3051 		    fprintf(stderr,
3052  "%s: Line %i: Warning:\
3053  Value for %s argument j=%f is not positive.\n",
3054 			filename, line_num,
3055 			parm, a.y
3056 		    );
3057 		if(a.z <= 0.0f)
3058 		    fprintf(stderr,
3059  "%s: Line %i: Warning:\
3060  Value for %s argument k=%f is not positive.\n",
3061 			filename, line_num,
3062 			parm, a.z
3063 		    );
3064 
3065 		if(aircraft != NULL)
3066 		    memcpy(
3067 			&aircraft->accel_responsiveness,
3068 			&a,
3069 			sizeof(sar_position_struct)
3070 		    );
3071 	    }
3072 	    /* Airplane Acceleration Responsiveness */
3073 	    else if(!strcasecmp(parm, "airplane_accelresp") ||
3074 		    !strcasecmp(parm, "airplane_acceleration_responsiveness")
3075 	    )
3076 	    {
3077 		/* Arguments:
3078 		 *
3079 		 * <i> <j> <k>
3080 		 */
3081 		sar_position_struct	a;
3082 
3083 		arg = GET_ARG_POS(arg, &a);
3084 
3085 		if(a.x <= 0.0f)
3086 		    fprintf(stderr,
3087  "%s: Line %i: Warning:\
3088  Value for %s argument i=%f is not positive.\n",
3089 			filename, line_num,
3090 			parm, a.x
3091 		    );
3092 		if(a.y <= 0.0f)
3093 		    fprintf(stderr,
3094  "%s: Line %i: Warning:\
3095  Value for %s argument j=%f is not positive.\n",
3096 			filename, line_num,
3097 			parm, a.y
3098 		    );
3099 		if(a.z <= 0.0f)
3100 		    fprintf(stderr,
3101  "%s: Line %i: Warning:\
3102  Value for %s argument k=%f is not positive.\n",
3103 			filename, line_num,
3104 			parm, a.z
3105 		    );
3106 
3107 		if(aircraft != NULL)
3108 		    memcpy(
3109 			&aircraft->airplane_accel_responsiveness,
3110 			&a,
3111 			sizeof(sar_position_struct)
3112 		    );
3113 	    }
3114 	    /* Cockpit Offset */
3115 	    else if(!strcasecmp(parm, "cockpit_offset"))
3116 	    {
3117 		/* Arguments:
3118 		 *
3119 		 * <x> <y> <z>
3120 		 */
3121 		sar_position_struct	pos;
3122 
3123 		arg = GET_ARG_POS(arg, &pos);
3124 
3125 		if(aircraft != NULL)
3126 		    memcpy(
3127 			&aircraft->cockpit_offset_pos,
3128 			&pos,
3129 			sizeof(sar_position_struct)
3130 		    );
3131 	    }
3132 	    /* Control Panel */
3133 	    else if(!strcasecmp(parm, "control_panel") &&
3134 		    is_player
3135 	    )
3136 	    {
3137 		/* Arguments:
3138 		 *
3139 		 * <x> <y> <z>
3140 		 * <heading> <pitch> <bank>
3141 		 * <width> <height>
3142 		 * <path>
3143 		 *
3144 		 * Note that position and size units are in centimeters
3145 		 * The <path> specifies the control panel directory
3146 		 * (not the instruments file)
3147 		 */
3148 		sar_position_struct	pos;
3149 		sar_direction_struct	dir;
3150 		float	width = 0.0f,
3151 			height = 0.0f;
3152 		char	*path = NULL;
3153 		arg = GET_ARG_POS(arg, &pos);
3154 		arg = GET_ARG_DIR(arg, &dir);
3155 		arg = GET_ARG_F(arg, &width);
3156 		arg = GET_ARG_F(arg, &height);
3157 		arg = GET_ARG_S(arg, &path);
3158 
3159 		if(!STRISEMPTY(path))
3160 		{
3161 		    ControlPanel *cp = CPNew(core_ptr->display);
3162 		    CPDelete((ControlPanel *)scene->player_control_panel);
3163 		    scene->player_control_panel = cp;
3164 		    if(cp != NULL)
3165 		    {
3166 			CPLoadFromFile(cp, path);
3167 			CPSetPosition(cp, pos.x, pos.y, pos.z);
3168 			CPSetDirection(cp, dir.heading, dir.pitch, dir.bank);
3169 			CPSetSize(cp, width, height);
3170 		    }
3171 		}
3172 		else
3173 		{
3174 		    fprintf(stderr,
3175  "%s: Line %i: Warning:\
3176  Value for %s argument path is not given.\n",
3177 			filename, line_num,
3178 			parm
3179 		    );
3180 		}
3181 
3182 		free(path);
3183 	    }
3184 	    /* Belly Height */
3185 	    else if(!strcasecmp(parm, "belly_height"))
3186 	    {
3187 		/* Arguments:
3188 		 *
3189 		 * <height>
3190 		 */
3191 		float	height = 0.0f;
3192 
3193 		arg = GET_ARG_F(arg, &height);
3194 
3195 		if(aircraft != NULL)
3196 		    aircraft->belly_height = height;
3197 	    }
3198 	    /* Landing Gear Height */
3199 	    else if(!strcasecmp(parm, "gear_height"))
3200 	    {
3201 		/* Arguments:
3202 		 *
3203 		 * <height>
3204 		 */
3205 		float	height = 0.0f;
3206 
3207 		arg = GET_ARG_F(arg, &height);
3208 
3209 		if(height < 0.0f)
3210 		    fprintf(stderr,
3211  "%s: Line %i: Warning:\
3212  Value for %s argument height=%f should not be negative.\n",
3213 			filename, line_num,
3214 			parm, height
3215 		    );
3216 
3217 		if(aircraft != NULL)
3218 		    aircraft->gear_height = height;
3219 	    }
3220 	    /* Ground Turning */
3221 	    else if(!strcasecmp(parm, "ground_turning"))
3222 	    {
3223 		/* Arguments:
3224 		 *
3225 		 * <turn_rad> <turn_vel_opt> <turn_vel_max>
3226 		 *
3227 		 * All units are in miles per hour
3228 		 */
3229 		float	turn_rad = 0.0f,
3230 			turn_vel_opt = 0.0f,
3231 			turn_vel_max = 0.0f;
3232 
3233 		arg = GET_ARG_F(arg, &turn_rad);
3234 		arg = GET_ARG_F(arg, &turn_vel_opt);
3235 		arg = GET_ARG_F(arg, &turn_vel_max);
3236 
3237 		if(aircraft != NULL)
3238 		{
3239 		    /* Turn radius in meters, distance from farthest
3240 		     * non-turnable wheel to turnable wheel
3241 		     *
3242 		     * Can be negative
3243 		     *
3244 		     * A value of 0 specifies no turning
3245 		     */
3246 		    aircraft->gturn_radius = turn_rad;
3247 
3248 		    /* Optimul ground turning velocity along
3249 		     * aircraft's y axis in meters per cycle
3250 		     */
3251 		    aircraft->gturn_vel_opt =
3252 			(float)SFMMPHToMPC(turn_vel_opt);
3253 
3254 		    /* Maximum ground turning velocity along
3255 		     * aircraft's y axis in meters per cycle
3256 		     */
3257 		    aircraft->gturn_vel_max =
3258 			(float)SFMMPHToMPC(turn_vel_max);
3259 		}
3260 	    }
3261 	    /* Dry Mass */
3262 	    else if(!strcasecmp(parm, "dry_mass"))
3263 	    {
3264 		/* Arguments:
3265 		 *
3266 		 * <mass>
3267 		 */
3268 		float	mass = 0.0f;
3269 
3270 		arg = GET_ARG_F(arg, &mass);
3271 
3272 		if(mass < 0)
3273 		    fprintf(stderr,
3274  "%s: Line %i: Warning:\
3275  Value for %s argument mass=%f should not be negative.\n",
3276 			filename, line_num,
3277 			parm, mass
3278 		    );
3279 
3280 		if(aircraft != NULL)
3281 		    aircraft->dry_mass = mass;
3282 	    }
3283 	    /* Fuel */
3284 	    else if(!strcasecmp(parm, "fuel"))
3285 	    {
3286 		/* Arguments:
3287 		 *
3288 		 * <consumption_rate>
3289 		 * <initial> <max>
3290 		 *
3291 		 * Units for <consumption_rate> are in kg per second
3292 		 * Units for <fuel_init> and <fuel_max> are in kg.
3293 		 */
3294 		float	consumption_rate = 0.0f,
3295 			initial = 0.0f,
3296 			max = 0.0f;
3297 
3298 		arg = GET_ARG_F(arg, &consumption_rate);
3299 		arg = GET_ARG_F(arg, &initial);
3300 		arg = GET_ARG_F(arg, &max);
3301 
3302 		if(consumption_rate < 0.0f)
3303 		    fprintf(stderr,
3304  "%s: Line %i: Warning:\
3305  Value for %s argument consumption_rate=%f should not be negative.\n",
3306 			filename, line_num,
3307 			parm, consumption_rate
3308 		    );
3309 		if(initial < 0.0f)
3310 		    fprintf(stderr,
3311  "%s: Line %i: Warning:\
3312  Value for %s argument initial=%f should not be negative.\n",
3313 			filename, line_num,
3314 			parm, initial
3315 		    );
3316 		if(max < 0.0f)
3317 		    fprintf(stderr,
3318  "%s: Line %i: Warning:\
3319  Value for %s argument max=%f should not be negative.\n",
3320 			filename, line_num,
3321 			parm, max
3322 		    );
3323 
3324 		if(aircraft != NULL)
3325 		{
3326 		    /* Convert fuel rate from (kg / sec) to (kg / cycle) */
3327 		    aircraft->fuel_rate = (float)MAX(
3328 			consumption_rate * SAR_SEC_TO_CYCLE_COEFF, 0.0
3329 		    );
3330 
3331 		    /* Fuel in kg */
3332 		    aircraft->fuel = (float)MAX(initial, 0.0);
3333 		    aircraft->fuel_max = (float)MAX(max, 0.0);
3334 		}
3335 	    }
3336 	    /* Crew */
3337 	    else if(!strcasecmp(parm, "crew"))
3338 	    {
3339 		/* Arguments:
3340 		 *
3341 		 * <crew>
3342 		 * <passengers> <passengers_max>
3343 		 */
3344 		int	crew = 0,
3345 			passengers = 0,
3346 			passengers_max = 0;
3347 
3348 		arg = GET_ARG_I(arg, &crew);
3349 		arg = GET_ARG_I(arg, &passengers);
3350 		arg = GET_ARG_I(arg, &passengers_max);
3351 
3352 		if(crew < 0)
3353 		    fprintf(stderr,
3354  "%s: Line %i: Warning:\
3355  Value for %s argument crew=%i should not be negative.\n",
3356 			filename, line_num,
3357 			parm, crew
3358 		    );
3359 		if(passengers < 0)
3360 		    fprintf(stderr,
3361  "%s: Line %i: Warning:\
3362  Value for %s argument passengers=%i should not be negative.\n",
3363 			filename, line_num,
3364 			parm, passengers
3365 		    );
3366 		if(passengers_max < 0)
3367 		    fprintf(stderr,
3368  "%s: Line %i: Warning:\
3369  Value for %s argument passengers_max=%i should not be negative.\n",
3370 			filename, line_num,
3371 			parm, passengers_max
3372 		    );
3373 
3374 		if(aircraft != NULL)
3375 		{
3376 		    aircraft->crew = crew;
3377 		    aircraft->passengers = passengers;
3378 		    aircraft->passengers_max = passengers_max;
3379 		}
3380 	    }
3381 	    /* Engine */
3382 	    else if(!strcasecmp(parm, "engine"))
3383 	    {
3384 		/* Arguments:
3385 		 *
3386 		 * <can_pitch?> <initial_pitch>
3387 		 * <power>
3388 		 * <collective_range>
3389 		 *
3390 		 * Power from units of kg * m / cycle^2.
3391 		 */
3392 		Boolean	can_pitch = False;
3393 		int	initial_pitch = 0;
3394 		float	power = 0.0f,
3395 			collective_range = 0.0f;
3396 
3397 		arg = GET_ARG_B(arg, &can_pitch);
3398 		arg = GET_ARG_I(arg, &initial_pitch);
3399 		arg = GET_ARG_F(arg, &power);
3400 		arg = GET_ARG_F(arg, &collective_range);
3401 
3402 		if(power < 0.0f)
3403 		    fprintf(stderr,
3404  "%s: Line %i: Warning:\
3405  Value for %s argument power=%f should not be negative.\n",
3406 			filename, line_num,
3407 			parm, power
3408 		    );
3409 		if((collective_range < 0.0f) || (collective_range > 1.0f))
3410 		    fprintf(stderr,
3411  "%s: Line %i: Warning:\
3412  Value for %s argument collective_range=%f is out of range [0.0, 1.0].\n",
3413 			filename, line_num,
3414 			parm, collective_range
3415 		    );
3416 
3417 		if(aircraft != NULL)
3418 		{
3419 		    /* Can pitch? */
3420 		    aircraft->engine_can_pitch = can_pitch;
3421 
3422 		    /* Initial rotor pitch state, this determines the
3423 		     * the value for member flight_model_type
3424 		     */
3425 		    switch(initial_pitch)
3426 		    {
3427 		      case 1:
3428 			aircraft->flight_model_type =
3429 			    SAR_FLIGHT_MODEL_AIRPLANE;
3430 			break;
3431 		      case 0:
3432 			aircraft->flight_model_type =
3433 			    SAR_FLIGHT_MODEL_HELICOPTER;
3434 			break;
3435 		      default:
3436 			fprintf(stderr,
3437  "%s: Line %i: Warning:\
3438  Value for %s argument init_pitch=%i is not supported.\n",
3439 			    filename, line_num,
3440 			    parm, initial_pitch
3441 			);
3442 			break;
3443 		    }
3444 
3445 		    /* Explicitly set previous flight model type to the
3446 		     * same type as the current one since this is the
3447 		     * first time this is being set for this object
3448 		     */
3449 		    aircraft->last_flight_model_type =
3450 			aircraft->flight_model_type;
3451 
3452 		    /* Engine power (in kg * m / cycle^2) */
3453 		    aircraft->engine_power = (float)MAX(
3454 			power, 0.0
3455 		    );
3456 
3457 		    /* Collective range coefficient (from 0.0 to 1.0) */
3458 		    aircraft->collective_range = (float)CLIP(
3459 			collective_range, 0.0, 1.0
3460 		    );
3461 		}
3462 	    }
3463 	    /* Service Ceiling */
3464 	    else if(!strcasecmp(parm, "service_ceiling"))
3465 	    {
3466 		/* Arguments:
3467 		 *
3468 		 * <altitude>
3469 		 */
3470 		float	altitude = 0.0f;
3471 
3472 		arg = GET_ARG_F(arg, &altitude);
3473 
3474 		if(aircraft != NULL)
3475 		    aircraft->service_ceiling =
3476 			(float)SFMFeetToMeters(altitude);
3477 	    }
3478 	    /* Attitude Change Rates */
3479 	    else if(!strcasecmp(parm, "attitude_change_rate"))
3480 	    {
3481 		/* Arguments:
3482 		 *
3483 		 * <heading> <pitch> <bank>
3484 		 *
3485 		 * Units are in degrees per second
3486 		 */
3487 		sar_direction_struct	a;
3488 
3489 		arg = GET_ARG_DIR(arg, &a);
3490 
3491 		a.heading *= (float)SAR_SEC_TO_CYCLE_COEFF;
3492 		a.pitch *= (float)SAR_SEC_TO_CYCLE_COEFF;
3493 		a.bank *= (float)SAR_SEC_TO_CYCLE_COEFF;
3494 
3495 		if(aircraft != NULL)
3496 		    memcpy(
3497 			&aircraft->attitude_change_rate,
3498 			&a,
3499 			sizeof(sar_direction_struct)
3500 		    );
3501 	    }
3502 	    /* Attitude Leveling */
3503 	    else if(!strcasecmp(parm, "attitude_leveling"))
3504 	    {
3505 		/* Arguments:
3506 		 *
3507 		 * <heading> <pitch> <bank>
3508 		 *
3509 		 * Units are in degrees per second
3510 		 */
3511 		sar_direction_struct	a;
3512 
3513 		arg = GET_ARG_DIR(arg, &a);
3514 
3515 		a.heading *= (float)SAR_SEC_TO_CYCLE_COEFF;
3516 		a.pitch *= (float)SAR_SEC_TO_CYCLE_COEFF;
3517 		a.bank *= (float)SAR_SEC_TO_CYCLE_COEFF;
3518 
3519 		if(aircraft != NULL)
3520 		{
3521 		    aircraft->pitch_leveling = a.pitch;
3522 		    aircraft->bank_leveling = a.bank;
3523 		}
3524 	    }
3525 	    /* Ground Pitch Offset */
3526 	    else if(!strcasecmp(parm, "ground_pitch_offset"))
3527 	    {
3528 		/* Arguments:
3529 		 *
3530 		 * <ground_pitch_offset>
3531 		 */
3532 		float	ground_pitch_offset;
3533 
3534 		arg = GET_ARG_F(arg, &ground_pitch_offset);
3535 		ground_pitch_offset = (float)DEGTORAD(ground_pitch_offset);
3536 
3537 		if(aircraft != NULL)
3538 		    aircraft->ground_pitch_offset = ground_pitch_offset;
3539 	    }
3540 	    /* New Light */
3541 	    else if(!strcasecmp(parm, "light_new"))
3542 	    {
3543 		/* Arguments:
3544 		 *
3545 		 * <x> <y> <z>
3546 		 * <r> <g> <b> <a>
3547 		 * <radius>
3548 		 * <init_on?> <type> <int_on> <int_off>
3549 		 * <int_on_delay>
3550 		 *
3551 		 * Units for <radius> are in pixels
3552 		 * Units for <int_*> are in milliseconds
3553 		 */
3554 		sar_light_struct	*light;
3555 		sar_position_struct	pos;
3556 		sar_color_struct	c;
3557 		int	radius = 0;
3558 		Boolean init_on = False;
3559 		int	type = 0,
3560 			int_on = 0,
3561 			int_off = 0,
3562 			int_on_delay = 0;
3563 
3564 		arg = GET_ARG_POS(arg, &pos);
3565 		arg = GET_ARG_RGBA(arg, &c);
3566 		arg = GET_ARG_I(arg, &radius);
3567 		arg = GET_ARG_B(arg, &init_on);
3568 		arg = GET_ARG_I(arg, &type);
3569 		arg = GET_ARG_I(arg, &int_on);
3570 		arg = GET_ARG_I(arg, &int_off);
3571 		arg = GET_ARG_I(arg, &int_on_delay);
3572 
3573 		if(radius < 0)
3574 		    fprintf(stderr,
3575  "%s: Line %i: Warning:\
3576  Value for %s argument radius=%i should not be negative (use 0 for default).\n",
3577 			filename, line_num,
3578 			parm, radius
3579 		    );
3580 		if(int_on < 0)
3581 		    fprintf(stderr,
3582  "%s: Line %i: Warning:\
3583  Value for %s argument int_on=%i should not be negative (use 0 for none).\n",
3584 			filename, line_num,
3585 			parm, int_on
3586 		    );
3587 		if(int_off < 0)
3588 		    fprintf(stderr,
3589  "%s: Line %i: Warning:\
3590  Value for %s argument int_off=%i should not be negative (use 0 for none).\n",
3591 			filename, line_num,
3592 			parm, int_off
3593 		    );
3594 		if(int_on_delay < 0)
3595 		    fprintf(stderr,
3596  "%s: Line %i: Warning:\
3597  Value for %s argument int_on_delay=%i should not be negative (use 0 for none).\n",
3598 			filename, line_num,
3599 			parm, int_on_delay
3600 		    );
3601 
3602 		/* Create new light */
3603 		light = SARObjLightNew(
3604 		    scene,
3605 		    &obj_ptr->light, &obj_ptr->total_lights
3606 		);
3607 		if(light != NULL)
3608 		{
3609 		    /* Position */
3610 		    memcpy(&light->pos, &pos, sizeof(sar_position_struct));
3611 
3612 		    /* Color */
3613 		    memcpy(&light->color, &c, sizeof(sar_color_struct));
3614 
3615 		    /* Radius */
3616 		    light->radius = (int)MAX(radius, 0);
3617 
3618 		    /* Initially on? */
3619 		    if(init_on)
3620 			light->flags |= SAR_LIGHT_FLAG_ON;
3621 
3622 		    /* Type of light */
3623 		    switch(type)
3624 		    {
3625 		      case 2:	/* Spot light */
3626 			light->flags |= SAR_LIGHT_FLAG_ATTENUATE;
3627 			break;
3628 		      case 1:	/* Strobe */
3629 			light->flags |= SAR_LIGHT_FLAG_STROBE;
3630 			break;
3631 		      case 0:
3632 			break;
3633 		      default:
3634 			fprintf(stderr,
3635  "%s: Line %i: Warning:\
3636  Value for %s argument type=%i is not supported [0 | 1 | 2].\n",
3637 			    filename, line_num,
3638 			    parm, type
3639 			);
3640 			break;
3641 		    }
3642 
3643 		    /* Strobe on and off intervals */
3644 		    light->int_on = (time_t)int_on;
3645 		    light->int_off = (time_t)int_off;
3646 		    light->int_delay_on = (time_t)int_on_delay;
3647 		}
3648 	    }
3649 	    /* New Sound Source */
3650 	    else if(!strcasecmp(parm, "sound_source_new"))
3651 	    {
3652 		SARObjLoadSoundSource(
3653 		    scene,
3654 		    &obj_ptr->sndsrc, &obj_ptr->total_sndsrcs,
3655 		    arg, filename, line_num
3656 		);
3657 	    }
3658 	    /* New Rotor */
3659 	    else if(!strcasecmp(parm, "rotor_new") ||
3660 		    !strcasecmp(parm, "propellar_new")
3661 	    )
3662 	    {
3663 		/* Arguments:
3664 		 *
3665 		 * <nblades>
3666 		 * <x> <y> <z>
3667 		 * <heading> <pitch> <bank>
3668 		 * <radius> <has_prop_wash?> <follow_controls?>
3669 		 * <blur_criteria>
3670 		 * <can_pitch?> <no_pitch_landed?> <no_rotate?>
3671 		 * <blades_offset>
3672 		 */
3673 		int	nblades = 0;
3674 		sar_position_struct	pos;
3675 		sar_direction_struct	dir;
3676 		float	radius = 0.0f;
3677 		Boolean	has_prop_wash = False,
3678 			follow_controls = False;
3679 		int	blur_criteria = 0;
3680 		Boolean	can_pitch = False,
3681 			no_pitch_landed = False,
3682 			no_rotate = False;
3683 		float	blades_offset = 0.0f;
3684 
3685 		arg = GET_ARG_I(arg, &nblades);
3686 		arg = GET_ARG_POS(arg, &pos);
3687 		arg = GET_ARG_DIR(arg, &dir);
3688 		arg = GET_ARG_F(arg, &radius);
3689 		arg = GET_ARG_B(arg, &has_prop_wash);
3690 		arg = GET_ARG_B(arg, &follow_controls);
3691 		arg = GET_ARG_I(arg, &blur_criteria);
3692 		arg = GET_ARG_B(arg, &can_pitch);
3693 		arg = GET_ARG_B(arg, &no_pitch_landed);
3694 		arg = GET_ARG_B(arg, &no_rotate);
3695 		arg = GET_ARG_F(arg, &blades_offset);
3696 
3697 		if(radius < 0.0f)
3698 		    fprintf(stderr,
3699  "%s: Line %i: Warning:\
3700  Value for %s argument radius=%f should not be negative.\n",
3701 			filename, line_num,
3702 			parm, radius
3703 		    );
3704 
3705 		rotor = NULL;
3706 		if(aircraft != NULL)
3707 		{
3708 		    int n = SARObjRotorNew(
3709 			scene,
3710 			&aircraft->rotor,
3711 			&aircraft->total_rotors
3712 		    );
3713 		    rotor = (n > -1) ?
3714 			aircraft->rotor[n] : NULL;
3715 		}
3716 		if(rotor != NULL)
3717 		{
3718 		    /* Total blades */
3719 		    rotor->total_blades = nblades;
3720 
3721 		    /* Position */
3722 		    memcpy(&rotor->pos, &pos, sizeof(sar_position_struct));
3723 
3724 		    /* Direction */
3725 		    memcpy(&rotor->dir, &dir, sizeof(sar_direction_struct));
3726 
3727 		    /* Radius of blades */
3728 		    rotor->radius = (float)MAX(radius, 0.0f);
3729 
3730 		    /* Has rotor wash? */
3731 		    if(has_prop_wash)
3732 			rotor->wash_tex_num = SARGetTextureRefNumberByName(
3733 			    scene, SAR_STD_TEXNAME_ROTOR_WASH
3734 			);
3735 		    else
3736 			rotor->wash_tex_num = -1;
3737 
3738 		    /* Follow controls for pitch and bank? */
3739 		    if(follow_controls)
3740 			rotor->flags |= SAR_ROTOR_FLAG_FOLLOW_CONTROLS;
3741 
3742 		    /* Blur criteria */
3743 		    switch(blur_criteria)
3744 		    {
3745 		      case 2:	/* Blur always */
3746 			rotor->flags |= SAR_ROTOR_FLAG_BLUR_ALWAYS;
3747 			break;
3748 		      case 1:	/* Blur when spinning fast */
3749 			rotor->flags |= SAR_ROTOR_FLAG_BLUR_WHEN_FAST;
3750 			break;
3751 		      case 0:	/* Never blur */
3752 			break;
3753 		      default:
3754 			fprintf(stderr,
3755  "%s: Line %i: Warning:\
3756  Value for %s argument blur_criteria=%i is not supported [0 | 1 | 2].\n",
3757 			    filename, line_num,
3758 			    parm, blur_criteria
3759 			);
3760 			break;
3761 		    }
3762 
3763 		    /* Can pitch? */
3764 		    if(can_pitch)
3765 			rotor->flags |= SAR_ROTOR_FLAG_CAN_PITCH;
3766 
3767 		    /* No pitching of rotors when landed? */
3768 		    if(no_pitch_landed)
3769 			rotor->flags |= SAR_ROTOR_FLAG_NO_PITCH_LANDED;
3770 
3771 		    /* No rotate? */
3772 		    if(!no_rotate)
3773 			rotor->flags |= SAR_ROTOR_FLAG_SPINS;
3774 
3775 		    /* Blades offset */
3776 		    rotor->blades_offset = blades_offset;
3777 
3778 		    /* Rotor blur texture */
3779 		    rotor->blade_blur_tex_num = SARGetTextureRefNumberByName(
3780 			scene, SAR_STD_TEXNAME_ROTOR_BLADE_BLUR
3781 		    );
3782 		}
3783 		else
3784 		{
3785 		    fprintf(stderr,
3786  "%s: Line %i: Error: Unable to create rotor (check object type).\n",
3787 			filename, line_num
3788 		    );
3789 		}
3790 	    }
3791 	    /* Rotor Blur Color */
3792 	    else if(!strcasecmp(parm, "rotor_blur_color") ||
3793 		    !strcasecmp(parm, "propellar_blur_color")
3794 	    )
3795 	    {
3796 		/* Arguments:
3797 		 *
3798 		 * <r> <b> <g> <a>
3799 		 */
3800 		sar_color_struct	c;
3801 
3802 		arg = GET_ARG_RGBA(arg, &c);
3803 
3804 		if(rotor != NULL)
3805 		{
3806 		    if((rotor->flags & SAR_ROTOR_FLAG_BLUR_WHEN_FAST) ||
3807 		       (rotor->flags & SAR_ROTOR_FLAG_BLUR_ALWAYS)
3808 		    )
3809 		    {
3810 			memcpy(
3811 			    &rotor->blades_blur_color,
3812 			    &c,
3813 			    sizeof(sar_color_struct)
3814 			);
3815 		    }
3816 		    else
3817 		    {
3818 			fprintf(
3819 			    stderr,
3820  "%s: Line %i: Warning:\
3821  Rotor blur_criteria is set to never blur (rotor_blur_color ignored).\n",
3822 			    filename, line_num
3823 			);
3824 		    }
3825 		}
3826 	    }
3827 	    /* Rotor Blade Blur Texture */
3828 	    else if(!strcasecmp(parm, "rotor_blade_blur_texture"))
3829 	    {
3830 		/* Arguments:
3831 		 *
3832 		 * <name>
3833 		 */
3834 		char *name = NULL;
3835 
3836 		arg = GET_ARG_S(arg, &name);
3837 
3838 		if(rotor != NULL)
3839 		{
3840 		    rotor->blade_blur_tex_num = SARGetTextureRefNumberByName(
3841 			scene, name
3842 		    );
3843 		}
3844 
3845 		free(name);
3846 	    }
3847 	    /* New Air Brake */
3848 	    else if(!strcasecmp(parm, "air_brake_new"))
3849 	    {
3850 		/* Arguments:
3851 		 *
3852 		 * <x> <y> <z>
3853 		 * <heading> <pitch> <bank>
3854 		 * <dep_dheading> <dep_dpitch> <dep_dbank>
3855 		 * <deployed?> <anim_rate>
3856 		 * <visible_when_retracted?>
3857 		 */
3858 		sar_position_struct	pos;
3859 		sar_direction_struct	dir,
3860 					dep_dir;
3861 		Boolean	deployed = False;
3862 		int	anim_rate = 0;
3863 		Boolean visible_when_retracted = False;
3864 
3865 		arg = GET_ARG_POS(arg, &pos);
3866 		arg = GET_ARG_DIR(arg, &dir);
3867 		arg = GET_ARG_DIR(arg, &dep_dir);
3868 		arg = GET_ARG_B(arg, &deployed);
3869 		arg = GET_ARG_I(arg, &anim_rate);
3870 		arg = GET_ARG_B(arg, &visible_when_retracted);
3871 
3872 		abrake_ptr = NULL;
3873 		if(aircraft != NULL)
3874 		    abrake_ptr = SARObjAirBrakeNew(
3875 			scene,
3876 			&aircraft->part,
3877 			&aircraft->total_parts
3878 		    );
3879 		if(abrake_ptr != NULL)
3880 		{
3881 		    abrake_ptr->flags = 0;	/* Must reset flags */
3882 
3883 		    /* Position */
3884 		    memcpy(
3885 			&abrake_ptr->pos_min,
3886 			&pos,
3887 			sizeof(sar_position_struct)
3888 		    );
3889 		    memcpy(
3890 			&abrake_ptr->pos_cen,
3891 			&pos,
3892 			sizeof(sar_position_struct)
3893 		    );
3894 		    memcpy(
3895 			&abrake_ptr->pos_max,
3896 			&pos,
3897 			sizeof(sar_position_struct)
3898 		    );
3899 
3900 		    /* Direction */
3901 		    memcpy(
3902 			&abrake_ptr->dir_min,
3903 			&dir,
3904 			sizeof(sar_direction_struct)
3905 		    );
3906 		    memcpy(
3907 			&abrake_ptr->dir_cen,
3908 			&dir,
3909 			sizeof(sar_direction_struct)
3910 		    );
3911 
3912 		    /* Deployed direction */
3913 		    memcpy(
3914 			&abrake_ptr->dir_max,
3915 			&dep_dir,
3916 			sizeof(sar_direction_struct)
3917 		    );
3918 
3919 		    /* Air brake initially deployed? */
3920 		    if(deployed)
3921 		    {
3922 			/* Air brake initially deployed */
3923 			abrake_ptr->anim_pos = (sar_grad_anim_t)-1;
3924 			abrake_ptr->flags |= SAR_OBJ_PART_FLAG_STATE;
3925 		    }
3926 		    else
3927 		    {
3928 			/* Air brake initially retracted */
3929 			abrake_ptr->anim_pos = 0;
3930 			abrake_ptr->flags &= ~SAR_OBJ_PART_FLAG_STATE;
3931 		    }
3932 
3933 		    /* Animation rate */
3934 		    abrake_ptr->anim_rate = (sar_grad_anim_t)anim_rate;
3935 
3936 		    /* Air brake visible when retracted? */
3937 		    if(!visible_when_retracted)
3938 			abrake_ptr->flags |= SAR_OBJ_PART_FLAG_HIDE_MIN;
3939 
3940 		    abrake_ptr->temperature = obj_ptr->temperature * 0.5f;
3941 		}
3942 		else
3943 		{
3944 		    fprintf(stderr,
3945  "%s: Line %i: Error:\
3946  Unable to create air brake (check object type).\n",
3947 			filename, line_num
3948 		    );
3949 		}
3950 	    }
3951 	    /* New Landing Gear */
3952 	    else if(!strcasecmp(parm, "landing_gear_new"))
3953 	    {
3954 		/* Arguments:
3955 		 *
3956 		 * <x> <y> <z>
3957 		 * <heading> <pitch> <bank>
3958 		 * <anim_rate> <up?> <fixed?> <skis?> <floats?>
3959 		 */
3960 		sar_position_struct	pos;
3961 		sar_direction_struct	dir;
3962 		int	anim_rate = 0;
3963 		Boolean	up = False,
3964 			fixed = False,
3965 			skis = False,
3966 			floats = False;
3967 
3968 		arg = GET_ARG_POS(arg, &pos);
3969 		arg = GET_ARG_DIR(arg, &dir);
3970 		arg = GET_ARG_I(arg, &anim_rate);
3971 		arg = GET_ARG_B(arg, &up);
3972 		arg = GET_ARG_B(arg, &fixed);
3973 		arg = GET_ARG_B(arg, &skis);
3974 		arg = GET_ARG_B(arg, &floats);
3975 
3976 		lgear_ptr = NULL;
3977 		if(aircraft != NULL)
3978 		    lgear_ptr = SARObjLandingGearNew(
3979 			scene,
3980 			&aircraft->part,
3981 			&aircraft->total_parts
3982 		    );
3983 		if(lgear_ptr != NULL)
3984 		{
3985 		    lgear_ptr->flags = 0;	/* Must reset flags */
3986 
3987 		    memcpy(&lgear_ptr->pos_min, &pos, sizeof(sar_position_struct));
3988 		    memcpy(&lgear_ptr->pos_cen, &pos, sizeof(sar_position_struct));
3989 		    memcpy(&lgear_ptr->pos_max, &pos, sizeof(sar_position_struct));
3990 
3991 		    memset(&lgear_ptr->dir_min, 0x00, sizeof(sar_direction_struct));
3992 		    memset(&lgear_ptr->dir_cen, 0x00, sizeof(sar_direction_struct));
3993 		    memcpy(&lgear_ptr->dir_max, &dir, sizeof(sar_direction_struct));
3994 
3995 		    lgear_ptr->anim_rate = (sar_grad_anim_t)anim_rate;
3996 
3997 		    /* Landing gear up or down? */
3998 		    if(up)
3999 		    {
4000 			/* Landing gear initially up */
4001 			lgear_ptr->anim_pos = (sar_grad_anim_t)-1;
4002 			lgear_ptr->flags &= ~SAR_OBJ_PART_FLAG_STATE;
4003 		    }
4004 		    else
4005 		    {
4006 			/* Landing gear initially down */
4007 			lgear_ptr->anim_pos = 0;
4008 			lgear_ptr->flags |= SAR_OBJ_PART_FLAG_STATE;
4009 		    }
4010 
4011 		    /* Landing gear fixed? */
4012 		    if(fixed)
4013 		    {
4014 			/* Fixed */
4015 			lgear_ptr->anim_pos = 0;
4016 			lgear_ptr->flags |= SAR_OBJ_PART_FLAG_STATE;
4017 			lgear_ptr->flags |= SAR_OBJ_PART_FLAG_LGEAR_FIXED;
4018 		    }
4019 		    else
4020 		    {
4021 			/* Retractable */
4022 			lgear_ptr->flags &= ~SAR_OBJ_PART_FLAG_LGEAR_FIXED;
4023 		    }
4024 
4025 		    /* Skis? */
4026 		    if(skis)
4027 			lgear_ptr->flags |= SAR_OBJ_PART_FLAG_LGEAR_SKI;
4028 		    else
4029 			lgear_ptr->flags &= ~SAR_OBJ_PART_FLAG_LGEAR_SKI;
4030 
4031 		    /* Floats? */
4032 		    if(floats)
4033 			lgear_ptr->flags |= SAR_OBJ_PART_FLAG_LGEAR_FLOATS;
4034 		    else
4035 			lgear_ptr->flags &= ~SAR_OBJ_PART_FLAG_LGEAR_FLOATS;
4036 
4037 		    /* Landing gears are always hidden when retracted
4038 		     * Maximum position is considered retracted
4039 		     */
4040 		    lgear_ptr->flags |= SAR_OBJ_PART_FLAG_HIDE_MAX;
4041 
4042 		    /* Need to update landing gear state on aircraft */
4043 		    if(lgear_ptr->flags & SAR_OBJ_PART_FLAG_LGEAR_FIXED)
4044 			aircraft->landing_gear_state = 2;
4045 		    else
4046 			aircraft->landing_gear_state = 1;
4047 		    if(!(lgear_ptr->flags & SAR_OBJ_PART_FLAG_LGEAR_SKI))
4048 			aircraft->wheel_brakes_state = 0;
4049 
4050 		    lgear_ptr->temperature = obj_ptr->temperature * 0.5f;
4051 		}
4052 		else
4053 		{
4054 		    fprintf(
4055 			stderr,
4056  "%s: Line %i: Error:\
4057  Unable to create landing gear (check object type).\n",
4058 			filename, line_num
4059 		    );
4060 		}
4061 	    }
4062 	    /* Aileron left, aileron right, rudder top, rudder bottom,
4063 	     * elevator, cannard, aileron/elevator left, or
4064 	     * aileron/elevator right
4065 	     */
4066 	    else if(!strcasecmp(parm, "aileron_left_new") ||
4067 	            !strcasecmp(parm, "aileron_right_new") ||
4068 		    !strcasecmp(parm, "rudder_top_new") ||
4069 		    !strcasecmp(parm, "rudder_bottom_new") ||
4070 		    !strcasecmp(parm, "elevator_new") ||
4071 		    !strcasecmp(parm, "cannard_new") ||
4072 		    !strcasecmp(parm, "aileron_elevator_left_new") ||
4073 		    !strcasecmp(parm, "aileron_elevator_right_new") ||
4074 		    !strcasecmp(parm, "flap_new")
4075 	    )
4076 	    {
4077 		sar_obj_part_type part_type = -1;
4078 		sar_obj_part_struct **part_ptr = NULL;
4079 
4080 		/* Arguments:
4081 		 *
4082 		 * <x> <y> <z>
4083 		 * <heading> <pitch> <bank>
4084 		 * <t_min> <t_max>
4085 		 */
4086 		sar_position_struct	pos;
4087 		sar_direction_struct	dir;
4088 		float	t_min = 0.0f,
4089 			t_max = 0.0f;
4090 
4091 		arg = GET_ARG_POS(arg, &pos);
4092 		arg = GET_ARG_DIR(arg, &dir);
4093 		arg = GET_ARG_F(arg, &t_min);
4094 		arg = GET_ARG_F(arg, &t_max);
4095 
4096 		/* Determine the part type and get part pointer */
4097 		if(!strcasecmp(parm, "aileron_left_new"))
4098 		{
4099 		    part_type = SAR_OBJ_PART_TYPE_AILERON_LEFT;
4100 		    part_ptr = &aileron_left_ptr;
4101 		}
4102 		else if(!strcasecmp(parm, "aileron_right_new"))
4103 		{
4104 		    part_type = SAR_OBJ_PART_TYPE_AILERON_RIGHT;
4105 		    part_ptr = &aileron_right_ptr;
4106 		}
4107 		else if(!strcasecmp(parm, "rudder_top_new"))
4108 		{
4109 		    part_type = SAR_OBJ_PART_TYPE_RUDDER_TOP;
4110 		    part_ptr = &rudder_top_ptr;
4111 		}
4112 		else if(!strcasecmp(parm, "rudder_bottom_new"))
4113 		{
4114 		    part_type = SAR_OBJ_PART_TYPE_RUDDER_BOTTOM;
4115 		    part_ptr = &rudder_bottom_ptr;
4116 		}
4117 		else if(!strcasecmp(parm, "elevator_new"))
4118 		{
4119 		    part_type = SAR_OBJ_PART_TYPE_ELEVATOR;
4120 		    part_ptr = &elevator_ptr;
4121 		}
4122 		else if(!strcasecmp(parm, "cannard_new"))
4123 		{
4124 		    part_type = SAR_OBJ_PART_TYPE_CANNARD;
4125 		    part_ptr = &cannard_ptr;
4126 		}
4127 		else if(!strcasecmp(parm, "aileron_elevator_left_new"))
4128 		{
4129 		    part_type = SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_LEFT;
4130 		    part_ptr = &aileron_elevator_left_ptr;
4131 		}
4132 		else if(!strcasecmp(parm, "aileron_elevator_right_new"))
4133 		{
4134 		    part_type = SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_RIGHT;
4135 		    part_ptr = &aileron_elevator_right_ptr;
4136 		}
4137 		else if(!strcasecmp(parm, "flap_new"))
4138 		{
4139 		    part_type = SAR_OBJ_PART_TYPE_FLAP;
4140 		    part_ptr = &flap_ptr;
4141 		}
4142 
4143 		/* Got valid part type? */
4144 		if(part_ptr != NULL)
4145 		{
4146 		    /* Create new part */
4147 		    if(aircraft != NULL)
4148 			*part_ptr = SARObjPartNew(
4149 			    scene,
4150 			    &aircraft->part,
4151 			    &aircraft->total_parts,
4152 			    part_type
4153 			);
4154 		    if(*part_ptr != NULL)
4155 		    {
4156 			sar_obj_part_struct	*part = *part_ptr;
4157 			sar_direction_struct	*dir_min = &part->dir_min,
4158 						*dir_cen = &part->dir_cen,
4159 						*dir_max = &part->dir_max;
4160 
4161 			/* Minimum position */
4162 			memset(&part->pos_min, 0x00, sizeof(sar_position_struct));
4163 
4164 			/* Center position */
4165 			memcpy(&part->pos_cen, &pos, sizeof(sar_position_struct));
4166 
4167 			/* Maximum position */
4168 			memset(&part->pos_max, 0x00, sizeof(sar_position_struct));
4169 
4170 
4171 			/* Minimum direction */
4172 			memset(dir_min, 0x00, sizeof(sar_direction_struct));
4173 
4174 			/* Center direction */
4175 			memcpy(dir_cen, &dir, sizeof(sar_direction_struct));
4176 
4177 			/* Maximum direction */
4178 			memset(dir_max, 0x00, sizeof(sar_direction_struct));
4179 
4180 			/* Set values specific to the part's type */
4181 			switch(part_type)
4182 			{
4183 			  case SAR_OBJ_PART_TYPE_AILERON_LEFT:
4184 			  case SAR_OBJ_PART_TYPE_AILERON_RIGHT:
4185 			    dir_min->pitch += (float)DEGTORAD(t_min);
4186 			    dir_max->pitch += (float)DEGTORAD(t_max);
4187 			    break;
4188 			  case SAR_OBJ_PART_TYPE_RUDDER_TOP:
4189 			  case SAR_OBJ_PART_TYPE_RUDDER_BOTTOM:
4190 			    dir_min->heading += (float)DEGTORAD(t_min);
4191 			    dir_max->heading += (float)DEGTORAD(t_max);
4192 			    break;
4193 			  case SAR_OBJ_PART_TYPE_ELEVATOR:
4194 			  case SAR_OBJ_PART_TYPE_CANNARD:
4195 			    dir_min->pitch += (float)DEGTORAD(t_min);
4196 			    dir_max->pitch += (float)DEGTORAD(t_max);
4197 			    break;
4198 			  case SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_LEFT:
4199 			  case SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_RIGHT:
4200 			    dir_min->pitch += (float)DEGTORAD(t_min);
4201 			    dir_max->pitch += (float)DEGTORAD(t_max);
4202 			    break;
4203 			  case SAR_OBJ_PART_TYPE_FLAP:
4204 			    dir_min->pitch += (float)DEGTORAD(t_min);
4205 			    dir_max->pitch += (float)DEGTORAD(t_max);
4206 			    break;
4207 			  case SAR_OBJ_PART_TYPE_AIR_BRAKE:
4208 			    break;
4209 			  case SAR_OBJ_PART_TYPE_DOOR:
4210 			    break;
4211 			  case SAR_OBJ_PART_TYPE_DOOR_RESCUE:
4212 			    break;
4213 			  case SAR_OBJ_PART_TYPE_CANOPY:
4214 			    break;
4215 			  case SAR_OBJ_PART_TYPE_LANDING_GEAR:
4216 			    break;
4217 			}
4218 
4219 			part->temperature = obj_ptr->temperature * 0.5f;
4220 
4221 		    }
4222 		    else
4223 		    {
4224 			fprintf(
4225 			    stderr,
4226  "%s: Line %i: Error:\
4227  Unable to create part type %i (check object type).\n",
4228 			    filename, line_num,
4229 			    (int)part_type
4230 			);
4231 		    }
4232 		}
4233 	    }
4234 
4235 	    /* New External Fuel Tank */
4236 	    else if(!strcasecmp(parm, "fueltank_new") ||
4237 		    !strcasecmp(parm, "fuel_tank_new")
4238 	    )
4239 	    {
4240 		/* Arguments:
4241 		 *
4242 		 * <x> <y> <z>
4243 		 * <radius>
4244 		 * <dry_mass> <fuel> <fuel_max>
4245 		 * <droppable?>
4246 		 */
4247 		sar_position_struct pos;
4248 		float	radius = 0.0f,
4249 			belly_to_center_height = 0.0f,
4250 			dry_mass = 0.0f,
4251 			fuel = 0.0f,
4252 			fuel_max = 0.0f;
4253 		Boolean	droppable = False;
4254 
4255 		arg = GET_ARG_POS(arg, &pos);
4256 		arg = GET_ARG_F(arg, &radius);
4257 		arg = GET_ARG_F(arg, &belly_to_center_height);
4258 		arg = GET_ARG_F(arg, &dry_mass);
4259 		arg = GET_ARG_F(arg, &fuel);
4260 		arg = GET_ARG_F(arg, &fuel_max);
4261 		arg = GET_ARG_B(arg, &droppable);
4262 
4263 		if(radius < 0.0f)
4264 		    fprintf(stderr,
4265  "%s: Line %i: Warning:\
4266  Value for %s argument radius=%f should not be negative.\n",
4267 			filename, line_num,
4268 			parm, radius
4269 		    );
4270 		if(dry_mass < 0.0f)
4271 		    fprintf(stderr,
4272  "%s: Line %i: Warning:\
4273  Value for %s argument dry_mass=%f should not be negative.\n",
4274 			filename, line_num,
4275 			parm, dry_mass
4276 		    );
4277 		if(fuel < 0.0f)
4278 		    fprintf(stderr,
4279  "%s: Line %i: Warning:\
4280  Value for %s argument fuel=%f should not be negative.\n",
4281 			filename, line_num,
4282 			parm, fuel
4283 		    );
4284 		if(fuel_max < 0.0f)
4285 		    fprintf(stderr,
4286  "%s: Line %i: Warning:\
4287  Value for %s argument fuel_max=%f should not be negative.\n",
4288 			filename, line_num,
4289 			parm, fuel_max
4290 		    );
4291 		if(fuel > fuel_max)
4292 		    fprintf(stderr,
4293  "%s: Line %i: Warning:\
4294  Value for %s argument fuel=%f is greater than fuel_max=%f.\n",
4295 			filename, line_num,
4296 			parm, fuel, fuel_max
4297 		    );
4298 
4299 		eft_ptr = NULL;
4300 		if(aircraft != NULL)
4301 		{
4302 		    int n = SARObjExternalFuelTankNew(
4303 			scene,
4304 			&aircraft->external_fueltank,
4305 			&aircraft->total_external_fueltanks
4306 		    );
4307 		    eft_ptr = (n > -1) ?
4308 			aircraft->external_fueltank[n] : NULL;
4309 		}
4310 		if(eft_ptr != NULL)
4311 		{
4312 		    /* Position */
4313 		    memcpy(&eft_ptr->offset_pos, &pos, sizeof(sar_position_struct));
4314 
4315 		    /* Size */
4316 		    eft_ptr->radius = (float)MAX(radius, 0.0f);
4317 
4318 		    /* Belly to center height */
4319 		    eft_ptr->belly_to_center_height = belly_to_center_height;
4320 
4321 		    /* Mass, fuel, and fuel max */
4322 		    eft_ptr->dry_mass = (float)MAX(dry_mass, 0.0f);
4323 		    eft_ptr->fuel_max = (float)MAX(fuel_max, 0.0f);
4324 		    eft_ptr->fuel = (float)CLIP(fuel, 0.0f, eft_ptr->fuel_max);
4325 
4326 		    /* Dropable? */
4327 		    if(droppable)
4328 			eft_ptr->flags |= SAR_EXTERNAL_FUELTANK_FLAG_FIXED;
4329 		    else
4330 			eft_ptr->flags &= ~SAR_EXTERNAL_FUELTANK_FLAG_FIXED;
4331 
4332 		    /* Mark as initially on board */
4333 		    eft_ptr->flags |= SAR_EXTERNAL_FUELTANK_FLAG_ONBOARD;
4334 
4335 		    eft_ptr->temperature = obj_ptr->temperature * 0.5f;
4336 		}
4337 		else
4338 		{
4339 		    fprintf(
4340 			stderr,
4341  "%s: Line %i: Error:\
4342  Unable to create fuel tank (check object type).\n",
4343 			filename, line_num
4344 		    );
4345 		}
4346 	    }
4347 	    /* Rescue Door */
4348 	    else if(!strcasecmp(parm, "rescue_door_new"))
4349 	    {
4350 		/* Arguments:
4351 		 *
4352 		 * <x_closed> <y_closed> <z_closed>
4353 		 * <x_opened> <y_opened> <z_opened>
4354 		 * <h_opened> <p_opened> <b_opened>
4355 		 * <x_thres> <y_thres> <z_thres>
4356 		 * <anim_rate> <opened?>
4357 		 */
4358 		sar_position_struct	pos_closed,
4359 					pos_opened;
4360 		sar_direction_struct	dir_opened;
4361 		sar_position_struct	pos_thres;
4362 		int	anim_rate = 0;
4363 		Boolean	opened = False;
4364 
4365 		arg = GET_ARG_POS(arg, &pos_closed);
4366 		arg = GET_ARG_POS(arg, &pos_opened);
4367 		arg = GET_ARG_DIR(arg, &dir_opened);
4368 		arg = GET_ARG_POS(arg, &pos_thres);
4369 		arg = GET_ARG_I(arg, &anim_rate);
4370 		arg = GET_ARG_B(arg, &opened);
4371 
4372 		door_ptr = NULL;
4373 		if(aircraft != NULL)
4374 		    door_ptr = SARObjDoorRescueNew(
4375 			scene,
4376 			&aircraft->part,
4377 			&aircraft->total_parts
4378 		    );
4379 		if(door_ptr != NULL)
4380 		{
4381 		    door_ptr->flags = 0;	/* Must reset flags */
4382 
4383 		    /* Closed position */
4384 		    memcpy(&door_ptr->pos_min, &pos_closed, sizeof(sar_position_struct));
4385 
4386 		    /* Opened position */
4387 		    memcpy(&door_ptr->pos_max, &pos_opened, sizeof(sar_position_struct));
4388 
4389 		    /* Opened direction */
4390 		    memcpy(&door_ptr->dir_max, &dir_opened, sizeof(sar_direction_struct));
4391 
4392 		    /* Threshold position */
4393 		    memcpy(&door_ptr->pos_cen, &pos_thres, sizeof(sar_position_struct));
4394 
4395 		    /* Animation rate */
4396 		    door_ptr->anim_rate = (sar_grad_anim_t)anim_rate;
4397 
4398 		    /* Initially opened? */
4399 		    if(opened)
4400 		    {
4401 			/* Door initially open */
4402 			door_ptr->anim_pos = (sar_grad_anim_t)-1;
4403 			door_ptr->flags |= SAR_OBJ_PART_FLAG_STATE;
4404 			door_ptr->flags |= SAR_OBJ_PART_FLAG_DOOR_STAY_OPEN;
4405 		    }
4406 		    else
4407 		    {
4408 			/* Door initially closed */
4409 			door_ptr->anim_pos = 0;
4410 			door_ptr->flags &= ~SAR_OBJ_PART_FLAG_STATE;
4411 		    }
4412 
4413 		    door_ptr->temperature = obj_ptr->temperature;
4414 		}
4415 		else
4416 		{
4417 		    fprintf(stderr,
4418  "%s: Line %i: Error:\
4419  Unable to create rescue door (check object type).\n",
4420 			filename, line_num
4421 		    );
4422 		}
4423 	    }
4424 	    /* Hoist */
4425 	    else if(!strcasecmp(parm, "hoist"))
4426 	    {
4427 		/* Arguments:
4428 		 *
4429 		 * <x> <y> <z>
4430 		 * <rope_max> <rope_rate> <capacity>
4431 		 * <radius> <z_min> <z_max>
4432 		 * <basket?> <diver?> <hook?>
4433 		 */
4434 		sar_obj_hoist_struct *hoist;
4435 		sar_position_struct	pos;
4436 		float	rope_max = 0.0f,
4437 			rope_rate = 0.0f,
4438 			capacity = 0.0f,
4439 			radius = 0.0f,
4440 			z_min = 0.0f,
4441 			z_max = 0.0f;
4442 		Boolean	basket = False,
4443 			diver = False,
4444 			hook = False;
4445 
4446 		arg = GET_ARG_POS(arg, &pos);
4447 		arg = GET_ARG_F(arg, &rope_max);
4448 		arg = GET_ARG_F(arg, &rope_rate);
4449 		arg = GET_ARG_F(arg, &capacity);
4450 		arg = GET_ARG_F(arg, &radius);
4451 		arg = GET_ARG_F(arg, &z_min);
4452 		arg = GET_ARG_F(arg, &z_max);
4453 		arg = GET_ARG_B(arg, &basket);
4454 		arg = GET_ARG_B(arg, &diver);
4455 		arg = GET_ARG_B(arg, &hook);
4456 
4457 		if(rope_max < 0.0f)
4458 		    fprintf(stderr,
4459  "%s: Line %i: Warning:\
4460  Value for %s argument rope_max=%f should not be negative.\n",
4461 			filename, line_num,
4462 			parm, rope_max
4463 		    );
4464 		if(rope_rate < 0.0f)
4465 		    fprintf(stderr,
4466  "%s: Line %i: Warning:\
4467  Value for %s argument rope_rate=%f should not be negative.\n",
4468 			filename, line_num,
4469 			parm, rope_rate
4470 		    );
4471 		if(capacity < 0.0f)
4472 		    fprintf(stderr,
4473  "%s: Line %i: Warning:\
4474  Value for %s argument capacity=%f should not be negative.\n",
4475 			filename, line_num,
4476 			parm, capacity
4477 		    );
4478 		if(radius < 0.0f)
4479 		    fprintf(stderr,
4480  "%s: Line %i: Warning:\
4481  Value for %s argument radius=%f should not be negative.\n",
4482 			filename, line_num,
4483 			parm, radius
4484 		    );
4485 		if(z_min > z_max)
4486 		    fprintf(stderr,
4487  "%s: Line %i: Warning:\
4488  Value for %s argument z_min=%f is greater than z_max=%f.\n",
4489 			filename, line_num,
4490 			parm, z_min, z_max
4491 		    );
4492 
4493 		/* Create hoist as needed */
4494 		if(aircraft != NULL)
4495 		{
4496 		    if(aircraft->hoist == NULL)
4497 			aircraft->hoist = SAR_OBJ_HOIST(calloc(
4498 			    1, sizeof(sar_obj_hoist_struct)
4499 			));
4500 		}
4501 		hoist = SARObjGetHoistPtr(obj_ptr, 0, NULL);
4502 		if(hoist != NULL)
4503 		{
4504 		    const sar_human_data_entry_struct *human_data;
4505 
4506 		    /* Position */
4507 		    memcpy(&hoist->offset, &pos, sizeof(sar_position_struct));
4508 
4509 		    /* Rope length and movement rate */
4510 		    hoist->rope_max = rope_max;
4511 		    hoist->rope_rate = (float)(rope_rate * SAR_SEC_TO_CYCLE_COEFF);
4512 
4513 		    /* Capacity, in kg */
4514 		    hoist->capacity = capacity;
4515 
4516 		    /* Cylendrical contact size */
4517 		    hoist->contact_radius = radius;
4518 		    hoist->contact_z_min = z_min;
4519 		    hoist->contact_z_max = z_max;
4520 
4521 		    /* Deployment type */
4522 		    hoist->deployments = (basket ? SAR_HOIST_DEPLOYMENT_BASKET : 0) |
4523 			(diver ? SAR_HOIST_DEPLOYMENT_DIVER : 0) |
4524 			(hook ? SAR_HOIST_DEPLOYMENT_HOOK : 0);
4525 		    /* Set initial deployment */
4526 		    if(hoist->deployments & SAR_HOIST_DEPLOYMENT_BASKET)
4527 			hoist->cur_deployment = SAR_HOIST_DEPLOYMENT_BASKET;
4528 		    else if(hoist->deployments & SAR_HOIST_DEPLOYMENT_DIVER)
4529 			hoist->cur_deployment = SAR_HOIST_DEPLOYMENT_DIVER;
4530 		    else if(hoist->deployments & SAR_HOIST_DEPLOYMENT_HOOK)
4531 			hoist->cur_deployment = SAR_HOIST_DEPLOYMENT_HOOK;
4532 
4533 		    /* Get hoist texture reference numbers from scene */
4534 		    hoist->side_tex_num =
4535 			SARGetTextureRefNumberByName(
4536 			    scene, SAR_STD_TEXNAME_BASKET_SIDE
4537 			);
4538 		    hoist->end_tex_num =
4539 			SARGetTextureRefNumberByName(
4540 			    scene, SAR_STD_TEXNAME_BASKET_END
4541 			);
4542 		    hoist->bottom_tex_num =
4543 			SARGetTextureRefNumberByName(
4544 			    scene, SAR_STD_TEXNAME_BASKET_BOTTOM
4545 			);
4546 		    hoist->water_ripple_tex_num =
4547 			SARGetTextureRefNumberByName(
4548 			    scene, SAR_STD_TEXNAME_WATER_RIPPLE
4549 			);
4550 
4551 		    /* Diver color */
4552 		    human_data = SARHumanMatchEntryByName(
4553 			core_ptr->human_data, SAR_HUMAN_PRESET_NAME_DIVER
4554 		    );
4555 		    if(human_data == NULL)
4556 			human_data = SARHumanMatchEntryByName(
4557 			    core_ptr->human_data, SAR_HUMAN_PRESET_NAME_STANDARD
4558 			);
4559 		    if(human_data != NULL)
4560 			memcpy(
4561 			    hoist->diver_color,
4562 			    human_data->color,
4563 			    SAR_HUMAN_COLORS_MAX * sizeof(sar_color_struct)
4564 			);
4565 
4566 		    /* Animation position */
4567 		    hoist->anim_pos = 0;
4568 		    hoist->anim_rate = SAR_HUMAN_ANIM_RATE;
4569 /* TODO (using numan animation rate for now) */
4570 		}
4571 		else
4572 		{
4573 		    fprintf(stderr,
4574  "%s: Line %i: Error:\
4575  Unable to create hoist (check object type).\n",
4576 			filename, line_num
4577 		    );
4578 		}
4579 	    }
4580 	    /* Ground Elevation */
4581 	    else if(!strcasecmp(parm, "ground_elevation"))
4582 	    {
4583 		/* Arguments:
4584 		 *
4585 		 * <altitude>
4586 		 */
4587 		float	altitude = 0.0f;
4588 
4589 		arg = GET_ARG_F(arg, &altitude);
4590 
4591 		if(altitude < 0.0f)
4592 		    fprintf(
4593 			stderr,
4594  "%s: Line %i: Warning:\
4595  Value for %s argument altitude=%f should not be negative.\n",
4596 			filename, line_num,
4597 			parm, altitude
4598 		    );
4599 
4600 		if(ground != NULL)
4601 		{
4602 		    ground->elevation =
4603 			(float)SFMFeetToMeters(altitude);
4604 		}
4605 		else
4606 		{
4607 		    fprintf(
4608 			stderr,
4609  "%s: Line %i: Warning:\
4610  Unable to set %s for object type %i (must be type %i).\n",
4611 			filename, line_num,
4612 			parm, obj_ptr->type, SAR_OBJ_TYPE_GROUND
4613 		    );
4614 		}
4615 	    }
4616 
4617 	    /* Unsupported Parameter */
4618 	    else
4619 	    {
4620 		fprintf(
4621 		    stderr,
4622  "%s: Line %i: Warning:\
4623  Unsupported parameter \"%s\".\n",
4624 		    filename, line_num,
4625 		    parm
4626 		);
4627 	    }
4628 	}
4629 }
4630 
4631 /*
4632  *	Loads a V3D model file specified by filename and applies the
4633  *	loaded data to the object specified by obj_num.
4634  */
SARObjLoadFromFile(sar_core_struct * core_ptr,int obj_num,const char * filename)4635 int SARObjLoadFromFile(
4636 	sar_core_struct *core_ptr, int obj_num, const char *filename
4637 )
4638 {
4639 	int status, line_num, *total;
4640 	char *full_path;
4641 	const char *v3d_model_name;
4642 	FILE *fp;
4643 
4644 	void **v3d_h = NULL;
4645 	void *h;
4646 	int hn, htype, total_v3d_h = 0;
4647 
4648 	v3d_model_struct *v3d_model_ptr, **v3d_model = NULL;
4649 	int v3d_model_num, total_v3d_models = 0;
4650 
4651 	gw_display_struct *display = core_ptr->display;
4652 	sar_scene_struct *scene = core_ptr->scene;
4653 	sar_object_struct ***ptr, *obj_ptr;
4654 	sar_object_aircraft_struct *aircraft = NULL;
4655 	sar_object_ground_struct *ground = NULL;
4656 
4657 	int total_rotors = 0;
4658 	sar_obj_rotor_struct *rotor = NULL;
4659 
4660 	int total_aileron_lefts = 0;
4661 	sar_obj_part_struct *aileron_left_ptr = NULL;
4662 	int total_aileron_rights = 0;
4663 	sar_obj_part_struct *aileron_right_ptr = NULL;
4664 	int total_rudder_tops = 0;
4665 	sar_obj_part_struct *rudder_top_ptr = NULL;
4666 	int total_rudder_bottoms = 0;
4667 	sar_obj_part_struct *rudder_bottom_ptr = NULL;
4668 	int total_elevators = 0;
4669 	sar_obj_part_struct *elevator_ptr = NULL;
4670 	int total_cannards = 0;
4671 	sar_obj_part_struct *cannard_ptr = NULL;
4672 	int total_aileron_elevator_lefts = 0;
4673 	sar_obj_part_struct *aileron_elevator_left_ptr = NULL;
4674 	int total_aileron_elevator_rights = 0;
4675 	sar_obj_part_struct *aileron_elevator_right_ptr = NULL;
4676 	int total_flaps = 0;
4677 	sar_obj_part_struct *flap_ptr = NULL;
4678 	int total_abrakes = 0;
4679 	sar_obj_part_struct *abrake_ptr = NULL;
4680 	int total_lgears = 0;
4681 	sar_obj_part_struct *lgear_ptr = NULL;
4682 	int total_doors = 0;
4683 	sar_obj_part_struct *door_ptr = NULL;
4684 	int total_external_fueltanks = 0;
4685 	sar_external_fueltank_struct *eft_ptr = NULL;
4686 
4687 	sar_direction_struct *dir;
4688 	sar_position_struct *pos;
4689 	char *sar_vmodel_name;
4690 	sar_visual_model_struct **sar_vmodel, **sar_vmodel_ir;
4691 
4692 	struct stat stat_buf;
4693 
4694 
4695 	if(STRISEMPTY(filename) || (display == NULL) || (scene == NULL))
4696 	    return(-1);
4697 
4698 	ptr = &core_ptr->object;
4699 	total = &core_ptr->total_objects;
4700 
4701 	/* Get pointer to object */
4702 	obj_ptr = (SARObjIsAllocated(*ptr, *total, obj_num)) ?
4703 	    (*ptr)[obj_num] : NULL;
4704 	if(obj_ptr == NULL)
4705 	    return(-1);
4706 
4707 	pos = &obj_ptr->pos;
4708 	dir = &obj_ptr->dir;
4709 
4710 	/* Get the full path to the object file */
4711 	full_path = COMPLETE_PATH(filename);
4712 	if(full_path == NULL)
4713 	{
4714 	    fprintf(
4715 		stderr,
4716 		"%s: Unable to complete path.\n",
4717 		filename
4718 	    );
4719 	    return(-2);
4720 	}
4721 
4722 	/* Set filename as the full path */
4723 	filename = full_path;
4724 
4725 	/* Check if file exists and get file stats */
4726 	if(stat(filename, &stat_buf))
4727 	{
4728 	    char *s = STRDUP(strerror(errno));
4729 	    if(s == NULL)
4730 		s = STRDUP("no such file");
4731 	    *s = toupper(*s);
4732 	    fprintf(
4733 		stderr,
4734 		"%s: %s.\n",
4735 		filename, s
4736 	    );
4737 	    free(s);
4738 
4739 	    free(full_path);
4740 	    return(-1);
4741 	}
4742 #ifdef S_ISDIR
4743 	/* File is a directory? */
4744 	if(S_ISDIR(stat_buf.st_mode))
4745 	{
4746 	    fprintf(
4747 		stderr,
4748 		"%s: Is a directory.\n",
4749 		filename
4750 	    );
4751 
4752 	    free(full_path);
4753 	    return(-2);
4754 	}
4755 #endif	/* S_ISDIR */
4756 
4757 	/* Open the V3D model file for reading */
4758 	fp = FOpen(filename, "rb");
4759 	if(fp == NULL)
4760 	{
4761 	    fprintf(
4762 		stderr,
4763 		"%s: Unable to open the V3D Model file for reading.\n",
4764 		filename
4765 	    );
4766 
4767 	    free(full_path);
4768 	    return(-1);
4769 	}
4770 
4771 
4772 	/* Do preload procedure */
4773 
4774 	/* Reset object values */
4775 	if(True)
4776 	{
4777 	    /* Flags */
4778 	    obj_ptr->flags |=	SAR_OBJ_FLAG_HIDE_DAWN_MODEL |
4779 				SAR_OBJ_FLAG_HIDE_DUSK_MODEL |
4780 				SAR_OBJ_FLAG_HIDE_NIGHT_MODEL;
4781 
4782 
4783 	    /* Begin resetting object type specific values */
4784 
4785 	    /* Aircraft */
4786 	    aircraft = SAR_OBJ_GET_AIRCRAFT(obj_ptr);
4787 	    if(aircraft != NULL)
4788 	    {
4789 		sar_direction_struct *dir;
4790 
4791 		aircraft->z_accel = 0.0f;
4792 
4793 		aircraft->air_worthy_state = SAR_AIR_WORTHY_FLYABLE;
4794 		aircraft->engine_state = SAR_ENGINE_ON;
4795 		aircraft->next_engine_on = 0;
4796 
4797 		aircraft->ground_pitch_offset = (float)(0.0 * PI);
4798 
4799 		dir = &aircraft->spotlight_dir;
4800 		dir->heading = (float)SFMDegreesToRadians(
4801 		    SAR_DEF_SPOTLIGHT_HEADING
4802 		);
4803 		dir->pitch = (float)SFMDegreesToRadians(
4804 		    SAR_DEF_SPOTLIGHT_PITCH
4805 		);
4806 		dir->bank = (float)SFMDegreesToRadians(
4807 		    SAR_DEF_SPOTLIGHT_BANK
4808 		);
4809 
4810 		aircraft->landed = 1;
4811 
4812 		aircraft->landing_gear_state = -1;
4813 		aircraft->air_brakes_state = -1;
4814 		aircraft->wheel_brakes_state = -1;
4815 
4816 		obj_ptr->temperature = SAR_DEF_TEMPERATURE_AIRCRAFT;
4817 	    }
4818 	    /* Ground */
4819 	    ground = SAR_OBJ_GET_GROUND(obj_ptr);
4820 	    if(ground != NULL)
4821 	    {
4822 
4823 
4824 	    }
4825 	}
4826 
4827 	/* Begin reading V3D model file */
4828 	status = V3DLoadModel(
4829 	    NULL, fp,
4830 	    &v3d_h, &total_v3d_h,
4831 	    &v3d_model, &total_v3d_models,
4832 	    NULL, NULL
4833 	);
4834 
4835 	/* Close the V3D model file, it is no longer needed */
4836 	FClose(fp);
4837 	fp = NULL;
4838 
4839 	/* Error loading V3D model file? */
4840 	if(status)
4841 	{
4842 	    /* Error encountered while loading the visual model file,
4843 	     * delete V3D header items, model primitives, and return
4844 	     * indicating error
4845 	     */
4846 	    V3DMHListDeleteAll(&v3d_h, &total_v3d_h);
4847 	    V3DModelListDeleteAll(&v3d_model, &total_v3d_models);
4848 	    free(full_path);
4849 	    return(-1);
4850 	}
4851 
4852 
4853 	/* Iterate through V3D header items */
4854 	for(hn = 0; hn < total_v3d_h; hn++)
4855 	{
4856 	    h = v3d_h[hn];
4857 	    if(h == NULL)
4858 		continue;
4859 
4860 	    /* Handle by header item type */
4861 	    htype = V3DMHGetType(h);
4862 	    switch(htype)
4863 	    {
4864 		const mh_comment_struct *mh_comment;
4865 		const mh_texture_base_directory_struct *mh_tbd;
4866 		const mh_texture_load_struct *mh_texture_load;
4867 		const mh_color_specification_struct *mh_color_spec;
4868 		sar_parm_texture_load_struct *p_texture_load;
4869 
4870 	      case V3DMH_TYPE_COMMENT:
4871 		mh_comment = (mh_comment_struct *)h;
4872 		/* Ignore comments */
4873 		break;
4874 
4875 	      case V3DMH_TYPE_TEXTURE_BASE_DIRECTORY:
4876 		mh_tbd = (mh_texture_base_directory_struct *)h;
4877 		/* Ignore */
4878 		break;
4879 
4880 	      case V3DMH_TYPE_TEXTURE_LOAD:
4881 		mh_texture_load = (mh_texture_load_struct *)h;
4882 		p_texture_load = (sar_parm_texture_load_struct *)SARParmNew(
4883 		    SAR_PARM_TEXTURE_LOAD
4884 		);
4885 		if(p_texture_load != NULL)
4886 		{
4887 		    /* Copy data from model header item to sar parm */
4888 		    free(p_texture_load->name);
4889 		    p_texture_load->name = STRDUP(mh_texture_load->name);
4890 		    free(p_texture_load->file);
4891 		    p_texture_load->file = STRDUP(mh_texture_load->path);
4892 		    p_texture_load->priority = (float)mh_texture_load->priority;
4893 		    /* Load texture using the sar parm */
4894 		    SARObjLoadTexture(
4895 			core_ptr, scene,
4896 			p_texture_load
4897 		    );
4898 		    /* Deallocate sar parm for loading texture, not needed
4899 		     * anymore.
4900 		     */
4901 		    SARParmDelete(p_texture_load);
4902 		}
4903 		break;
4904 
4905 	      case V3DMH_TYPE_COLOR_SPECIFICATION:
4906 		mh_color_spec = (mh_color_specification_struct *)h;
4907 		/* Ignore */
4908 		break;
4909 	    }
4910 	}
4911 
4912 
4913 	/* Iterate through V3D models */
4914 	for(v3d_model_num = 0; v3d_model_num < total_v3d_models; v3d_model_num++)
4915 	{
4916 	    v3d_model_ptr = v3d_model[v3d_model_num];
4917 	    if(v3d_model_ptr == NULL)
4918 		continue;
4919 
4920 	    /* Get pointers to the object's parts for use with this
4921 	     * V3D model, the pointers must be obtained for each
4922 	     * V3D model since each V3D model may create a new object
4923 	     * part
4924 	     */
4925 	    if(aircraft != NULL)
4926 	    {
4927 		/* Rotors */
4928 		total_rotors = aircraft->total_rotors;
4929 		rotor = ((total_rotors > 0) ?
4930 		    aircraft->rotor[total_rotors - 1] : NULL
4931 		);
4932 
4933 		/* External fuel tanks */
4934 		total_external_fueltanks = aircraft->total_external_fueltanks;
4935 		eft_ptr = ((total_external_fueltanks > 0) ?
4936 		    aircraft->external_fueltank[total_external_fueltanks - 1]
4937 		    : NULL
4938 		);
4939 	    }
4940 /* Add fetching of substructure pointers support for other object
4941  * types here
4942  */
4943 	    else
4944 	    {
4945 		/* Rotors */
4946 		total_rotors = 0;
4947 		rotor = NULL;
4948 
4949 		/* External fuel tanks */
4950 		total_external_fueltanks = 0;
4951 		eft_ptr = NULL;
4952 	    }
4953 
4954 	    /* Get pointers to the object's parts */
4955 	    if(True)
4956 	    {
4957 		int i;
4958 		sar_obj_part_struct *part;
4959 
4960 #define GET_PART(_type_) {			\
4961  sar_obj_part_struct *p;			\
4962  i = 0;						\
4963  part = NULL;					\
4964  while(True) {					\
4965   p = SARObjGetPartPtr(obj_ptr, (_type_), i);	\
4966   if(p != NULL) {				\
4967    part = p; i++;				\
4968   } else {					\
4969    break; 					\
4970   }						\
4971  }						\
4972 }
4973 		GET_PART(SAR_OBJ_PART_TYPE_AILERON_LEFT);
4974 		total_aileron_lefts = i;
4975 		aileron_left_ptr = part;
4976 
4977 		GET_PART(SAR_OBJ_PART_TYPE_AILERON_RIGHT);
4978 		total_aileron_rights = i;
4979 		aileron_right_ptr = part;
4980 
4981 		GET_PART(SAR_OBJ_PART_TYPE_RUDDER_TOP);
4982 		total_rudder_tops = i;
4983 		rudder_top_ptr = part;
4984 
4985 		GET_PART(SAR_OBJ_PART_TYPE_RUDDER_BOTTOM);
4986 		total_rudder_bottoms = i;
4987 		rudder_bottom_ptr = part;
4988 
4989 		GET_PART(SAR_OBJ_PART_TYPE_ELEVATOR);
4990 		total_elevators = i;
4991 		elevator_ptr = part;
4992 
4993 		GET_PART(SAR_OBJ_PART_TYPE_CANNARD);
4994 		total_cannards = i;
4995 		cannard_ptr = part;
4996 
4997 		GET_PART(SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_LEFT);
4998 		total_aileron_elevator_lefts = i;
4999 		aileron_elevator_left_ptr = part;
5000 
5001 		GET_PART(SAR_OBJ_PART_TYPE_AILERON_ELEVATOR_RIGHT);
5002 		total_aileron_elevator_rights = i;
5003 		aileron_elevator_right_ptr = part;
5004 
5005 		GET_PART(SAR_OBJ_PART_TYPE_FLAP);
5006 		total_flaps = i;
5007 		flap_ptr = part;
5008 
5009 		GET_PART(SAR_OBJ_PART_TYPE_AIR_BRAKE);
5010 		total_abrakes = i;
5011 		abrake_ptr = part;
5012 
5013 		GET_PART(SAR_OBJ_PART_TYPE_LANDING_GEAR);
5014 		total_lgears = i;
5015 		lgear_ptr = part;
5016 
5017 		GET_PART(SAR_OBJ_PART_TYPE_DOOR_RESCUE);
5018 		total_doors = i;
5019 		door_ptr = part;
5020 #undef GET_PART
5021 	    }
5022 
5023 
5024 	    /* Iterate through each "other data" line on this V3D
5025 	     * model, these "other data" lines will specify values that
5026 	     * are specific to SAR objects
5027 	     */
5028  	    for(line_num = 0; line_num < v3d_model_ptr->total_other_data_lines; line_num++)
5029 	    {
5030 		const char *s = v3d_model_ptr->other_data_line[line_num];
5031 		if(s == NULL)
5032 		    continue;
5033 
5034 		SARObjLoadLine(
5035 		    core_ptr, obj_num, obj_ptr,
5036 		    s, filename, line_num
5037 		);
5038 	    }
5039 
5040 	    /* Do not continue the handling of this V3D model if it
5041 	     * does not contain primitives (if this V3D model is not of
5042 	     * type V3D_MODEL_TYPE_STANDARD)
5043 	     */
5044 	    if(v3d_model_ptr->type != V3D_MODEL_TYPE_STANDARD)
5045 		continue;
5046 
5047 	    /* Create model by the V3D model's name, the name must
5048 	     * be defined or else we will not be able to know what
5049 	     * this V3D model is suppose to represent
5050 	     */
5051 	    v3d_model_name = v3d_model_ptr->name;
5052 	    if(STRISEMPTY(v3d_model_name))
5053 		continue;
5054 
5055 	    /* Reset the SAR visual model & name pointers */
5056 	    sar_vmodel = NULL;
5057 	    sar_vmodel_ir = NULL;
5058 	    sar_vmodel_name = NULL;
5059 
5060 	    /* Get the sar_vmodel & sar_vmodel_name by the V3D model's
5061 	     * name
5062 	     */
5063 	    /* Standard */
5064 	    if(!strcasecmp(v3d_model_name, "standard"))
5065 	    {
5066 		sar_vmodel = &obj_ptr->visual_model;
5067 		sar_vmodel_ir = &obj_ptr->visual_model_ir;
5068 		free(sar_vmodel_name);
5069 		sar_vmodel_name = STRDUP("standard");
5070 	    }
5071 	    /* Standard Far */
5072 	    else if(!strcasecmp(v3d_model_name, "standard_far"))
5073 	    {
5074 		sar_vmodel = &obj_ptr->visual_model_far;
5075 		free(sar_vmodel_name);
5076 		sar_vmodel_name = STRDUP("standard_far");
5077 	    }
5078 	    /* Standard Dusk */
5079 	    else if(!strcasecmp(v3d_model_name, "standard_dusk"))
5080 	    {
5081 		sar_vmodel = &obj_ptr->visual_model_dusk;
5082 		free(sar_vmodel_name);
5083 		sar_vmodel_name = STRDUP("standard_dusk");
5084 	    }
5085 	    /* Standard Night */
5086 	    else if(!strcasecmp(v3d_model_name, "standard_night"))
5087 	    {
5088 		sar_vmodel = &obj_ptr->visual_model_night;
5089 		free(sar_vmodel_name);
5090 		sar_vmodel_name = STRDUP("standard_night");
5091 	    }
5092 	    /* Standard Dawn */
5093 	    else if(!strcasecmp(v3d_model_name, "standard_dawn"))
5094 	    {
5095 		sar_vmodel = &obj_ptr->visual_model_dawn;
5096 		free(sar_vmodel_name);
5097 		sar_vmodel_name = STRDUP("standard_dawn");
5098 	    }
5099 	    /* Rotor */
5100 	    else if(!strcasecmp(v3d_model_name, "rotor") ||
5101 		    !strcasecmp(v3d_model_name, "propellar")
5102 	    )
5103 	    {
5104 		if(rotor != NULL)
5105 		{
5106 		    char s[80];
5107 		    sprintf(s, "rotor%i", total_rotors - 1);
5108 
5109 		    sar_vmodel = &rotor->visual_model;
5110 		    sar_vmodel_ir = &rotor->visual_model_ir;
5111 		    free(sar_vmodel_name);
5112 		    sar_vmodel_name = STRDUP(s);
5113 		}
5114 	    }
5115 	    /* Aileron Left */
5116 	    else if(!strcasecmp(v3d_model_name, "aileron_left"))
5117 	    {
5118 		if(aileron_left_ptr != NULL)
5119 		{
5120 		    char s[80];
5121 		    sprintf(s, "aileron_left%i", total_aileron_lefts - 1);
5122 
5123 		    sar_vmodel = &aileron_left_ptr->visual_model;
5124 		    sar_vmodel_ir = &aileron_left_ptr->visual_model_ir;
5125 		    free(sar_vmodel_name);
5126 		    sar_vmodel_name = STRDUP(s);
5127 		}
5128 	    }
5129 	    /* Aileron Right */
5130 	    else if(!strcasecmp(v3d_model_name, "aileron_right"))
5131 	    {
5132 		if(aileron_right_ptr != NULL)
5133 		{
5134 		    char s[80];
5135 		    sprintf(s, "aileron_right%i", total_aileron_rights - 1);
5136 
5137 		    sar_vmodel = &aileron_right_ptr->visual_model;
5138 		    sar_vmodel_ir = &aileron_right_ptr->visual_model_ir;
5139 		    free(sar_vmodel_name);
5140 		    sar_vmodel_name = STRDUP(s);
5141 		}
5142 	    }
5143 	    /* Rudder Top */
5144 	    else if(!strcasecmp(v3d_model_name, "rudder_top"))
5145 	    {
5146 		if(rudder_top_ptr != NULL)
5147 		{
5148 		    char s[80];
5149 		    sprintf(s, "rudder_top%i", total_rudder_tops - 1);
5150 
5151 		    sar_vmodel = &rudder_top_ptr->visual_model;
5152 		    sar_vmodel_ir = &rudder_top_ptr->visual_model_ir;
5153 		    free(sar_vmodel_name);
5154 		    sar_vmodel_name = STRDUP(s);
5155 		}
5156 	    }
5157 	    /* Rudder Bottom */
5158 	    else if(!strcasecmp(v3d_model_name, "rudder_bottom"))
5159 	    {
5160 		if(rudder_bottom_ptr != NULL)
5161 		{
5162 		    char s[80];
5163 		    sprintf(s, "rudder_bottom%i", total_rudder_bottoms - 1);
5164 
5165 		    sar_vmodel = &rudder_bottom_ptr->visual_model;
5166 		    sar_vmodel_ir = &rudder_bottom_ptr->visual_model_ir;
5167 		    free(sar_vmodel_name);
5168 		    sar_vmodel_name = STRDUP(s);
5169 		}
5170 	    }
5171 	    /* Elevator */
5172 	    else if(!strcasecmp(v3d_model_name, "elevator"))
5173 	    {
5174 		if(elevator_ptr != NULL)
5175 		{
5176 		    char s[80];
5177 		    sprintf(s, "elevator%i", total_elevators - 1);
5178 
5179 		    sar_vmodel = &elevator_ptr->visual_model;
5180 		    sar_vmodel_ir = &elevator_ptr->visual_model_ir;
5181 		    free(sar_vmodel_name);
5182 		    sar_vmodel_name = STRDUP(s);
5183 		}
5184 	    }
5185 	    /* Cannard */
5186 	    else if(!strcasecmp(v3d_model_name, "cannard"))
5187 	    {
5188 		if(cannard_ptr != NULL)
5189 		{
5190 		    char s[80];
5191 		    sprintf(s, "cannard%i", total_cannards - 1);
5192 
5193 		    sar_vmodel = &cannard_ptr->visual_model;
5194 		    sar_vmodel_ir = &cannard_ptr->visual_model_ir;
5195 		    free(sar_vmodel_name);
5196 		    sar_vmodel_name = STRDUP(s);
5197 		}
5198 	    }
5199 	    /* Aileron Elevator Left */
5200 	    else if(!strcasecmp(v3d_model_name, "aileron_elevator_left"))
5201 	    {
5202 		if(aileron_elevator_left_ptr != NULL)
5203 		{
5204 		    char s[80];
5205 		    sprintf(s, "aileron_elevator_left%i", total_aileron_elevator_lefts - 1);
5206 
5207 		    sar_vmodel = &aileron_elevator_left_ptr->visual_model;
5208 		    sar_vmodel_ir = &aileron_elevator_left_ptr->visual_model_ir;
5209 		    free(sar_vmodel_name);
5210 		    sar_vmodel_name = STRDUP(s);
5211 		}
5212 	    }
5213 	    /* Aileron Elevator Right */
5214 	    else if(!strcasecmp(v3d_model_name, "aileron_elevator_right"))
5215 	    {
5216 		if(aileron_elevator_right_ptr != NULL)
5217 		{
5218 		    char s[80];
5219 		    sprintf(s, "aileron_elevator_right%i", total_aileron_elevator_rights - 1);
5220 
5221 		    sar_vmodel = &aileron_elevator_right_ptr->visual_model;
5222 		    sar_vmodel_ir = &aileron_elevator_right_ptr->visual_model_ir;
5223 		    free(sar_vmodel_name);
5224 		    sar_vmodel_name = STRDUP(s);
5225 		}
5226 	    }
5227 	    /* Flap */
5228 	    else if(!strcasecmp(v3d_model_name, "flap"))
5229 	    {
5230 		if(flap_ptr != NULL)
5231 		{
5232 		    char s[80];
5233 		    sprintf(s, "flap%i", total_flaps - 1);
5234 
5235 		    sar_vmodel = &flap_ptr->visual_model;
5236 		    sar_vmodel_ir = &flap_ptr->visual_model_ir;
5237 		    free(sar_vmodel_name);
5238 		    sar_vmodel_name = STRDUP(s);
5239 		}
5240 	    }
5241 	    /* Air Brake */
5242 	    else if(!strcasecmp(v3d_model_name, "air_brake"))
5243 	    {
5244 		if(abrake_ptr != NULL)
5245 		{
5246 		    char s[80];
5247 		    sprintf(s, "air_brake%i", total_abrakes - 1);
5248 
5249 		    sar_vmodel = &abrake_ptr->visual_model;
5250 		    sar_vmodel_ir = &abrake_ptr->visual_model_ir;
5251 		    free(sar_vmodel_name);
5252 		    sar_vmodel_name = STRDUP(s);
5253 		}
5254 	    }
5255 	    /* Landing Gear */
5256 	    else if(!strcasecmp(v3d_model_name, "landing_gear"))
5257 	    {
5258 		if(lgear_ptr != NULL)
5259 		{
5260 		    char s[80];
5261 		    sprintf(s, "landing_gear%i", total_lgears - 1);
5262 
5263 		    sar_vmodel = &lgear_ptr->visual_model;
5264 		    sar_vmodel_ir = &lgear_ptr->visual_model_ir;
5265 		    free(sar_vmodel_name);
5266 		    sar_vmodel_name = STRDUP(s);
5267 		}
5268 	    }
5269 	    /* Door */
5270 	    else if(!strcasecmp(v3d_model_name, "door"))
5271 	    {
5272 		if(door_ptr != NULL)
5273 		{
5274 		    char s[80];
5275 		    sprintf(s, "door%i", total_doors - 1);
5276 
5277 		    sar_vmodel = &door_ptr->visual_model;
5278 		    sar_vmodel_ir = &door_ptr->visual_model_ir;
5279 		    free(sar_vmodel_name);
5280 		    sar_vmodel_name = STRDUP(s);
5281 		}
5282 	    }
5283 	    /* External Fuel Tank */
5284 	    else if(!strcasecmp(v3d_model_name, "fueltank") ||
5285 		    !strcasecmp(v3d_model_name, "fuel_tank")
5286 	    )
5287 	    {
5288 		if(eft_ptr != NULL)
5289 		{
5290 		    char s[80];
5291 		    sprintf(s, "fueltank%i", total_external_fueltanks - 1);
5292 
5293 		    sar_vmodel = &eft_ptr->visual_model;
5294 		    sar_vmodel_ir = &eft_ptr->visual_model_ir;
5295 		    free(sar_vmodel_name);
5296 		    sar_vmodel_name = STRDUP(s);
5297 		}
5298 	    }
5299 	    /* Cockpit */
5300 	    else if(!strcasecmp(v3d_model_name, "cockpit"))
5301 	    {
5302 		if(aircraft != NULL)
5303 		{
5304 		    sar_vmodel = &aircraft->visual_model_cockpit;
5305 		    free(sar_vmodel_name);
5306 		    sar_vmodel_name = STRDUP("cockpit");
5307 		}
5308 	    }
5309 	    /* Shadow */
5310 	    else if(!strcasecmp(v3d_model_name, "shadow"))
5311 	    {
5312 		sar_vmodel = &obj_ptr->visual_model_shadow;
5313 		free(sar_vmodel_name);
5314 		sar_vmodel_name = STRDUP("shadow");
5315 	    }
5316 
5317 	    /* If sar_vmodel is NULL then it means that the V3D model's
5318 	     * name did not match a SAR visual model that is supported
5319 	     */
5320 	    if(sar_vmodel == NULL)
5321 	    {
5322 		/* The V3D model's name is not one that we support */
5323 		fprintf(
5324 		    stderr,
5325  "%s: Warning:\
5326  V3D model type \"%s\" is not supported on this object (check object type).\n",
5327 		    filename, v3d_model_name
5328 		);
5329 
5330 		/* Delete SAR visual model name */
5331 		free(sar_vmodel_name);
5332 		sar_vmodel_name = NULL;
5333 
5334 		continue;	/* Do not create this SAR Visual Model */
5335 	    }
5336 
5337 	    /* Is this SAR Visual Model already created? */
5338 	    if(*sar_vmodel != NULL)
5339 	    {
5340 		/* Warn about redefining this SAR Visual Model and then
5341 		 * unref it
5342 		 */
5343 		fprintf(
5344 		    stderr,
5345  "%s: Warning:\
5346  V3D model type \"%s\" redefined.\n",
5347 		    filename, v3d_model_name
5348 		);
5349 		SARVisualModelUnref(scene, *sar_vmodel);
5350 		*sar_vmodel = NULL;
5351 	    }
5352 	    /* Is this IR SAR Visual Model already created? */
5353 	    if((sar_vmodel_ir != NULL) ? (*sar_vmodel_ir != NULL) : False)
5354 	    {
5355 		/* Unref this IR SAR Visual Model */
5356 		SARVisualModelUnref(scene, *sar_vmodel_ir);
5357 		*sar_vmodel_ir = NULL;
5358 	    }
5359 
5360 	    /* Create a new or return existing SAR Visual Model */
5361 	    *sar_vmodel = SARVisualModelNew(
5362 		scene, filename, sar_vmodel_name
5363 	    );
5364 	    /* Is this a "new" Visual Model? */
5365 	    if(SARVisualModelGetRefCount(*sar_vmodel) == 1)
5366 	    {
5367 		/* Create GL display list on the Visual Model */
5368 		sar_visual_model_struct *vmodel = *sar_vmodel;
5369 		GLuint list = (GLuint)SARVisualModelNewList(vmodel);
5370 		if(list != 0)
5371 		{
5372 		    /* Mark SAR Visual Model as loading and begin
5373 		     * recording the GL display list
5374 		     */
5375 		    vmodel->load_state = SAR_VISUAL_MODEL_LOADING;
5376 		    glNewList(list, GL_COMPILE);
5377 
5378 		    /* Process this V3D model into GL commands */
5379 		    SARObjLoadProcessVisualModel(
5380 			core_ptr, obj_num, obj_ptr,
5381 			vmodel,
5382 			v3d_model_ptr,
5383 			False,		/* Not not process as IR */
5384 			filename, 0
5385 		    );
5386 
5387 		    /* End recording GL display list and mark the SAR
5388 		     * Visual Model as finished loading
5389 		     */
5390 	            glEndList();
5391 		    vmodel->load_state = SAR_VISUAL_MODEL_LOADED;
5392 		}
5393 	    }
5394 	    else
5395 	    {
5396 		/* This SAR Visual Model already has more than one ref
5397 		 * count which implies it is shared
5398 		 *
5399 		 * Check for V3D Model primitives that still need to be
5400 		 * loaded, all of the primitives handled here should
5401 		 * not produce any GL commands
5402 		 */
5403 		int pn, ptype;
5404 		void *p;
5405 		sar_visual_model_struct *vmodel = *sar_vmodel;
5406 
5407 		for(pn = 0; pn < v3d_model_ptr->total_primitives; pn++)
5408 		{
5409 		    p = v3d_model_ptr->primitive[pn];
5410 		    if(p == NULL)
5411 			continue;
5412 
5413 		    ptype = V3DMPGetType(p);
5414 		    switch(ptype)
5415 		    {
5416 			const mp_heightfield_load_struct *mp_heightfield_load;
5417 		      case V3DMP_TYPE_HEIGHTFIELD_LOAD:
5418 			mp_heightfield_load = (mp_heightfield_load_struct *)p;
5419 			/* Need to load z points for height field but
5420 			 * not issue any GL commands for it (by
5421 			 * passing the GL list as 0)
5422 			 */
5423 			SARObjLoadHeightField(
5424 			    core_ptr, obj_num, obj_ptr, vmodel,
5425 			    p, filename, 0,
5426 			    0		/* No GL display list */
5427 			);
5428 			break;
5429 		    }
5430 
5431 		}
5432 	    }
5433 
5434 	    /* Create IR SAR Visual Model? */
5435 	    if((sar_vmodel_ir != NULL) &&
5436 	       ((obj_ptr->type == SAR_OBJ_TYPE_STATIC) ||
5437 		(obj_ptr->type == SAR_OBJ_TYPE_AUTOMOBILE) ||
5438 		(obj_ptr->type == SAR_OBJ_TYPE_WATERCRAFT) ||
5439 		(obj_ptr->type == SAR_OBJ_TYPE_AIRCRAFT)
5440 	       )
5441 	    )
5442 	    {
5443 		char *sar_vmodel_name_ir = STRDUP(sar_vmodel_name);
5444 
5445 		/* Format the IR SAR Visual Model name */
5446 		sar_vmodel_name_ir = strinsstr(
5447 		    sar_vmodel_name_ir, -1, "_ir"
5448 		);
5449 
5450 		/* Create a new or return existing IR SAR Visual Model */
5451 		*sar_vmodel_ir = SARVisualModelNew(
5452 		    scene, filename, sar_vmodel_name_ir
5453 		);
5454 		/* Is this a "new" Visual Model? */
5455 		if(SARVisualModelGetRefCount(*sar_vmodel_ir) == 1)
5456 		{
5457 		    /* Create GL display list on the Visual Model */
5458 		    sar_visual_model_struct *vmodel = *sar_vmodel_ir;
5459 		    GLuint list = (GLuint)SARVisualModelNewList(vmodel);
5460 		    if(list != 0)
5461 		    {
5462 			/* Mark SAR Visual Model as loading and begin
5463 			 * recording the GL display list
5464 			 */
5465 			vmodel->load_state = SAR_VISUAL_MODEL_LOADING;
5466 			glNewList(list, GL_COMPILE);
5467 
5468 			/* Process this V3D model into GL commands */
5469 			SARObjLoadProcessVisualModel(
5470 			    core_ptr, obj_num, obj_ptr,
5471 			    vmodel,
5472 			    v3d_model_ptr,
5473 			    True,	/* Process as IR */
5474 			    filename, 0
5475 			);
5476 
5477 			/* End recording GL display list and mark the
5478 			 * IR SAR Visual Model as finished loading
5479 			 */
5480 			glEndList();
5481 			vmodel->load_state = SAR_VISUAL_MODEL_LOADED;
5482 		    }
5483 		}
5484 
5485 		free(sar_vmodel_name_ir);
5486 	    }
5487 
5488 
5489 	    /* Delete the SAR Visual Model name */
5490 	    free(sar_vmodel_name);
5491 	    sar_vmodel_name = NULL;
5492 	}
5493 
5494 
5495 	/* Delete V3D models and V3D header items (no longer needed) */
5496 	V3DMHListDeleteAll(&v3d_h, &total_v3d_h);
5497 	V3DModelListDeleteAll(&v3d_model, &total_v3d_models);
5498 
5499 
5500 	/* Create flight dynamics model if object is an aircraft */
5501 	if(aircraft != NULL)
5502 	{
5503 	    if(scene->realm == NULL)
5504 	    {
5505 		fprintf(
5506 		    stderr,
5507  "%s: Warning: Unable to create SFM for NULL realm on scene\n",
5508 		    filename
5509 		);
5510 	    }
5511 	    else
5512 	    {
5513 		/* Create new flight dynamics model and reset its
5514 		 * values to defaults
5515 		 */
5516 		SFMModelStruct *fdm = SFMModelAllocate();
5517 		if(fdm != NULL)
5518 		{
5519 		    fdm->landed_state = True;
5520 
5521 		    SFMModelAdd(scene->realm, fdm);
5522 		}
5523 		aircraft->fdm = fdm;
5524 	    }
5525 	}
5526 
5527 
5528 	/* Need to realize translation and rotation values on object */
5529 	SARSimWarpObject(
5530 	    scene, obj_ptr,
5531 	    &obj_ptr->pos, &obj_ptr->dir
5532 	);
5533 
5534 	/* Do postload check on object */
5535 	SARObjLoadPostLoadCheck(
5536 	    obj_num, obj_ptr,
5537 	    filename
5538 	);
5539 
5540 
5541 	free(full_path);
5542 
5543 	return(0);
5544 }
5545