]> icculus.org git repositories - taylor/freespace2.git/blob - src/model/modelinterp.cpp
pofview: it works
[taylor/freespace2.git] / src / model / modelinterp.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Model/ModelInterp.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  *      Rendering models, I think.
16  *
17  * $Log$
18  * Revision 1.8  2004/09/20 01:31:44  theoddone33
19  * GCC 3.4 fixes.
20  *
21  * Revision 1.7  2004/07/04 11:39:06  taylor
22  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
23  *
24  * Revision 1.6  2003/05/25 02:30:43  taylor
25  * Freespace 1 support
26  *
27  * Revision 1.5  2002/06/17 06:33:09  relnev
28  * ryan's struct patch for gcc 2.95
29  *
30  * Revision 1.4  2002/06/09 04:41:23  relnev
31  * added copyright header
32  *
33  * Revision 1.3  2002/06/01 03:32:00  relnev
34  * fix texture loading mistake.
35  *
36  * enable some d3d stuff for opengl also
37  *
38  * Revision 1.2  2002/05/07 03:16:46  theoddone33
39  * The Great Newline Fix
40  *
41  * Revision 1.1.1.1  2002/05/03 03:28:10  root
42  * Initial import.
43  *
44  * 
45  * 37    9/13/99 11:25p Dave
46  * Fixed problem with mode-switching and D3D movies.
47  * 
48  * 36    9/13/99 10:09a Andsager
49  * Add debug console commands to lower model render detail and fireball
50  * LOD for big ship explosiosns.
51  * 
52  * 35    9/08/99 12:03a Dave
53  * Make squad logos render properly in D3D all the time. Added intel anim
54  * directory.
55  * 
56  * 34    9/01/99 10:09a Dave
57  * Pirate bob.
58  * 
59  * 33    8/30/99 5:01p Dave
60  * Made d3d do less state changing in the nebula. Use new chat server for
61  * PXO.
62  * 
63  * 32    8/24/99 8:55p Dave
64  * Make sure nondimming pixels work properly in tech menu.
65  * 
66  * 31    7/29/99 10:47p Dave
67  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
68  * 
69  * 30    7/29/99 12:05a Dave
70  * Nebula speed optimizations.
71  * 
72  * 29    7/27/99 3:09p Dave
73  * Made g400 work. Whee.
74  * 
75  * 28    7/24/99 4:19p Dave
76  * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much
77  * better. Made model code use zbuffer more intelligently.
78  * 
79  * 27    7/19/99 7:20p Dave
80  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
81  * pre-rendering.
82  * 
83  * 26    7/18/99 5:20p Dave
84  * Jump node icon. Fixed debris fogging. Framerate warning stuff.
85  * 
86  * 25    7/15/99 2:13p Dave
87  * Added 32 bit detection.
88  * 
89  * 24    6/22/99 7:03p Dave
90  * New detail options screen.
91  * 
92  * 23    6/18/99 5:16p Dave
93  * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
94  * dialog to PXO screen.
95  * 
96  * 22    5/26/99 11:46a Dave
97  * Added ship-blasting lighting and made the randomization of lighting
98  * much more customizable.
99  * 
100  * 21    5/24/99 5:45p Dave
101  * Added detail levels to the nebula, with a decent speedup. Split nebula
102  * lightning into its own section.
103  * 
104  * 20    5/12/99 10:05a Johnson
105  * DKA:  Fix fred bug from engine wash
106  * 
107  * 19    5/08/99 8:25p Dave
108  * Upped object pairs. First run of nebula lightning.
109  * 
110  * 18    4/26/99 8:49p Dave
111  * Made all pof based nebula stuff full customizable through fred.
112  * 
113  * 17    4/23/99 5:53p Dave
114  * Started putting in new pof nebula support into Fred.
115  * 
116  * 16    4/20/99 3:30p Andsager
117  * Let get_model_closest_box_point_with_delta() take NULL as pointer to
118  * is_inside
119  * 
120  * 15    4/19/99 12:51p Andsager
121  * Add function to find the nearest point on extneded bounding box and
122  * check if inside bounding box.
123  * 
124  * 14    3/31/99 8:24p Dave
125  * Beefed up all kinds of stuff, incluging beam weapons, nebula effects
126  * and background nebulae. Added per-ship non-dimming pixel colors.
127  * 
128  * 13    3/24/99 6:14p Dave
129  * Added position and orientation checksumming for multiplayer. Fixed LOD
130  * rendering bugs for squad insignias
131  * 
132  * 12    3/23/99 5:17p Dave
133  * Changed model file format somewhat to account for LOD's on insignias
134  * 
135  * 11    3/19/99 9:51a Dave
136  * Checkin to repair massive source safe crash. Also added support for
137  * pof-style nebulae, and some new weapons code.
138  * 
139  * 10    3/08/99 7:03p Dave
140  * First run of new object update system. Looks very promising.
141  * 
142  * 9     3/02/99 9:25p Dave
143  * Added a bunch of model rendering debug code. Started work on fixing
144  * beam weapon wacky firing.
145  * 
146  * 8     2/19/99 11:42a Dave
147  * Put in model rendering autocentering.
148  * 
149  * 7     1/14/99 6:06p Dave
150  * 100% full squad logo support for single player and multiplayer.
151  * 
152  * 6     1/08/99 2:08p Dave
153  * Fixed software rendering for pofview. Super early support for AWACS and
154  * beam weapons.
155  * 
156  * 5     1/06/99 2:24p Dave
157  * Stubs and release build fixes.
158  * 
159  * 4     12/09/98 7:34p Dave
160  * Cleanup up nebula effect. Tweaked many values.
161  * 
162  * 3     12/06/98 2:36p Dave
163  * Drastically improved nebula fogging.
164  * 
165  * 2     10/07/98 10:53a Dave
166  * Initial checkin.
167  * 
168  * 1     10/07/98 10:50a Dave
169  * 
170  * 193   8/28/98 3:29p Dave
171  * EMP effect done. AI effects may need some tweaking as required.
172  * 
173  * 192   6/13/98 3:18p Hoffoss
174  * NOX()ed out a bunch of strings that shouldn't be translated.
175  * 
176  * 191   5/24/98 4:53p John
177  * changed rounding for model caching to get rid of some empty lines in
178  * cached bitmap.
179  * 
180  * 190   5/16/98 4:47p John
181  * Put back in my version 188 changes that someone so rudely (but
182  * hopefully unintentionally) deleted.
183  * 
184  * 189   5/13/98 11:34p Mike
185  * Model caching system.
186  * 
187  * 187   5/12/98 11:32a Mike
188  * Support for weapon pof detail levels.
189  * 
190  * 186   5/08/98 1:32p John
191  * Added code for using two layered subspace effects.
192  * 
193  * 185   4/29/98 11:03a John
194  * Added code to show octants.
195  * 
196  * 184   4/22/98 9:58p John
197  * Added code to view invisible faces.
198  * 
199  * 183   4/22/98 9:43p John
200  * Added code to allow checking of invisible faces, flagged by any texture
201  * name with invisible in it.
202  * 
203  * 182   4/20/98 4:44p John
204  * Fixed problems with black being xparent on model cache rneders.  Made
205  * model cache key off of detail level setting and framerate.
206  * 
207  * 181   4/18/98 4:00p John
208  * Made highest model caching detail level disable model caching.
209  * 
210  * 180   4/14/98 11:11p John
211  * Made ships with < 50% hull left show electrical damage arcs.
212  * 
213  * 179   4/13/98 4:54p John
214  * Made uv rotate independently on subspace effect. Put in DCF function
215  * for setting subspace speeds.
216  * 
217  * 178   4/12/98 5:54p John
218  * Made models work with subspace.  Made subspace rotate also.
219  * 
220  * 177   4/12/98 9:56a John
221  * Made lighting detail flags work.   Made explosions cast light on
222  * highest.
223  * 
224  * 176   4/11/98 6:53p John
225  * Added first rev of subspace effect.
226  * 
227  * 175   4/10/98 5:20p John
228  * Changed RGB in lighting structure to be ubytes.  Removed old
229  * not-necessary 24 bpp software stuff.
230  * 
231  * 174   4/09/98 11:40p John
232  * Fixed some bugs with hardware lighting.
233  * 
234  * 173   4/09/98 4:38p John
235  * Made non-darkening and transparent textures work under Glide.  Fixed
236  * bug with Jim's computer not drawing any bitmaps.
237  * 
238  * $NoKeywords: $
239  */
240
241 #include <math.h>
242
243 #define MODEL_LIB
244
245 #include "2d.h"
246 #include "3d.h"
247 #include "model.h"
248 #include "tmapper.h"
249 #include "floating.h"
250 #include "fvi.h"
251 #include "lighting.h"
252 #include "modelsinc.h"
253 #include "fireballs.h"
254 #include "fix.h"
255 #include "bmpman.h"
256 #include "systemvars.h"
257 #include "key.h"
258 #include "3dinternal.h"
259 #include "timer.h"
260 #include "grinternal.h"
261 #include "palman.h"
262 #include "object.h"                     // For MAX_OBJECTS
263 #include "missionparse.h"
264 #include "neb.h"
265
266
267 // Some debug variables used externally for displaying stats
268 #ifndef NDEBUG
269 int modelstats_num_polys = 0;
270 int modelstats_num_polys_drawn = 0;
271 int modelstats_num_verts = 0;
272 int modelstats_num_sortnorms = 0;
273 int modelstats_num_boxes = 0;
274 #endif
275
276 // a lighting object
277 typedef struct model_light_object {
278         ubyte           r[MAX_POLYGON_NORMS];
279         ubyte           g[MAX_POLYGON_NORMS];
280         ubyte           b[MAX_POLYGON_NORMS];
281         int             objnum;
282         int             skip;
283         int             skip_max;
284 } model_light_object;
285
286 // -----------------------
287 // Local variables
288 //
289
290 // Vertices used internally to rotate model points
291 static vertex Interp_points[MAX_POLYGON_VECS];
292 vector *Interp_verts[MAX_POLYGON_VECS];
293 static int Interp_num_verts;
294
295
296 // -------------------------------------------------------------------
297 // lighting save stuff 
298 //
299 #define MAX_MODEL_LIGHTING_SAVE                 30
300 int hack_skip_max = 1;
301 DCF(skip, "")
302 {
303         dc_get_arg(ARG_INT);
304         hack_skip_max = Dc_arg_int;
305 }
306 // model_light_object Interp_lighting_save[MAX_MODEL_LIGHTING_SAVE];
307 model_light_object Interp_lighting_temp;
308 model_light_object *Interp_lighting = &Interp_lighting_temp;
309 int Interp_use_saved_lighting = 0;
310 int Interp_saved_lighting_full = 0;
311 //
312 // lighting save stuff 
313 // -------------------------------------------------------------------
314
315
316 static ubyte Interp_light_applied[MAX_POLYGON_NORMS];
317 static vector *Interp_norms[MAX_POLYGON_NORMS];
318 static int Interp_num_norms = 0;
319 static ubyte *Interp_lights;
320
321 static float Interp_fog_level = 0.0f;
322
323 // Stuff to control rendering parameters
324 static color Interp_outline_color;
325 static int Interp_detail_level = 0;
326 static uint Interp_flags = 0;
327 static uint Interp_tmap_flags = 0;
328
329 // If non-zero, then the subobject gets scaled by Interp_thrust_scale.
330 static int Interp_thrust_scale_subobj=0;
331 static float Interp_thrust_scale = 0.1f;
332 static int Interp_thrust_bitmap = -1;
333 static int Interp_thrust_glow_bitmap = -1;
334 static float Interp_thrust_glow_noise = 1.0f;
335
336 static int Interp_objnum = -1;
337
338 // if != -1, use this bitmap when rendering ship insignias
339 static int Interp_insignia_bitmap = -1;
340
341 // if != -1, use this bitmap when rendering with a forced texture
342 static int Interp_forced_bitmap = -1;
343
344 // for rendering with the MR_ALL_XPARENT FLAG SET
345 static float Interp_xparent_alpha = 1.0f;
346
347 float Interp_light = 0.0f;
348
349 // forward references
350 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check );
351
352 // call at the beginning of a level. after the level has been loaded
353 void model_level_post_init()
354 {
355         /*
356         int idx;
357
358         // reset lighting stuff 
359         for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
360                 Interp_lighting_save[idx].objnum = -1;
361                 Interp_lighting_save[idx].skip = 0;
362         }
363
364         // saved lighting is not full
365         Interp_saved_lighting_full = 0;
366         */
367 }
368
369 // call to select an object for using "saved" lighting
370 void model_set_saved_lighting(int objnum, int skip_max)
371 {
372         /*
373         int idx;
374
375         // always set to not using saved light to start with
376         Interp_use_saved_lighting = 0;
377         Interp_lighting = &Interp_lighting_temp;
378
379         // if he passed a -1 for either value, no saved lighting
380         if((objnum == -1) || (skip_max == -1)){
381                 return;
382         }
383
384         // see if the object is already on the list
385         for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
386                 // ahha, he is on the list
387                 if(Interp_lighting_save[idx].objnum == objnum){
388                         // if he's entered a new skip max
389                         if(Interp_lighting_save[idx].skip_max != skip_max){
390                                 Interp_lighting_save[idx].skip = 0;
391                                 Interp_lighting_save[idx].skip_max = skip_max;
392                                 Interp_use_saved_lighting = 0;
393                                 Interp_lighting = &Interp_lighting_save[idx];
394                         } 
395                         // if we're reached the "skip" frame, re-render lighting for this guy
396                         else if(Interp_lighting_save[idx].skip == Interp_lighting_save[idx].skip_max){
397                                 Interp_lighting_save[idx].skip = 0;
398                                 Interp_use_saved_lighting = 0;
399                                 Interp_lighting = &Interp_lighting_save[idx];
400                         }
401                         // otherwise, use his saved lighting values
402                         else {
403                                 Interp_lighting_save[idx].skip++;
404                                 Interp_use_saved_lighting = 1;
405                                 Interp_lighting = &Interp_lighting_save[idx];
406                         }
407
408                         // duh
409                         return;
410                 }
411         }
412
413         // no free saved lighting slots
414         if(Interp_saved_lighting_full){
415                 return;
416         }
417         
418         // find a free slot
419         int found = 0;
420         for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
421                 // got one
422                 if(Interp_lighting_save[idx].objnum == -1){
423                         Interp_lighting_save[idx].objnum = objnum;
424                         Interp_lighting_save[idx].skip = 0;
425                         Interp_lighting_save[idx].skip_max = skip_max;
426
427                         Interp_use_saved_lighting = 0;
428                         Interp_lighting = &Interp_lighting_save[idx];
429
430                         found = 1;
431                         break;
432                 }
433         }
434
435         // oops. out of free slots
436         if(!found){
437                 Interp_saved_lighting_full = 1;
438         }
439         */
440 }
441
442 // notify the model system that a ship has died
443 void model_notify_dead_ship(int objnum)
444 {
445         /*
446         int idx;
447
448         // see if this guy was on the lighting list
449         for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
450                 // free him up
451                 if(objnum == Interp_lighting_save[idx].objnum){
452                         Interp_lighting_save[idx].objnum = -1;
453                         Interp_saved_lighting_full = 0;
454                         return;
455                 }
456         }
457         */
458 }
459
460 void interp_clear_instance()
461 {
462         Interp_thrust_scale = 0.1f;
463         Interp_thrust_bitmap = -1;
464         Interp_thrust_glow_bitmap = -1;
465         Interp_thrust_glow_noise = 1.0f;
466         Interp_insignia_bitmap = -1;
467 }
468
469 // Scales the engines thrusters by this much
470 void model_set_thrust( int model_num, float length, int bitmap, int glow_bitmap, float glow_noise )
471 {
472         Interp_thrust_scale = length;
473         Interp_thrust_bitmap = bitmap;
474         Interp_thrust_glow_bitmap = glow_bitmap;
475         Interp_thrust_glow_noise = glow_noise;
476
477         if ( Interp_thrust_scale < 0.1f ) {
478                 Interp_thrust_scale = 0.1f;
479         } else if ( Interp_thrust_scale > 1.0f ) {
480                 Interp_thrust_scale = 1.0f;
481         }
482
483         polymodel * pm = model_get( model_num );
484         int i;
485
486         // If thrust is set up, use it.
487         for (i=0; i<pm->num_lights; i++ )       {
488                 if ( pm->lights[i].type == BSP_LIGHT_TYPE_THRUSTER )    {
489                         float scale = (Interp_thrust_scale-0.1f)*0.5f;
490
491                         pm->lights[i].value += (scale+Interp_thrust_glow_noise*0.2f) / 255.0f;
492                 }
493         }
494 }
495
496
497 // Point list
498 // +0      int         id
499 // +4      int         size
500 // +8      int         n_verts
501 // +12     int         n_norms
502 // +16     int         offset from start of chunk to vertex data
503 // +20     n_verts*char    norm_counts
504 // +offset             vertex data. Each vertex n is a point followed by norm_counts[n] normals.
505 void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm)
506 {
507         int i, n;
508         int nverts = w(p+8);    
509         int offset = w(p+16);
510         int next_norm = 0;
511
512         ubyte * normcount = p+20;
513         vertex *dest = Interp_points;
514         vector *src = vp(p+offset);
515
516         // Get pointer to lights
517         Interp_lights = p+20+nverts;
518
519         SDL_assert( nverts < MAX_POLYGON_VECS );
520         // SDL_assert( nnorms < MAX_POLYGON_NORMS );
521
522         Interp_num_verts = nverts;
523         #ifndef NDEBUG
524         modelstats_num_verts += nverts;
525         #endif
526
527 /*
528         static int Max_vecs = 0;
529         static int Max_norms = 0;
530
531         if ( Max_vecs < nverts )        {
532                 Max_vecs = nverts;
533                 mprintf(( "MAX NORMS = %d\n", Max_norms ));
534                 mprintf(( "MAX VECS = %d\n", Max_vecs ));
535         }
536
537         if ( Max_norms < nnorms )       {
538                 Max_norms = nnorms;
539                 mprintf(( "MAX NORMS = %d\n", Max_norms ));
540                 mprintf(( "MAX VECS = %d\n", Max_vecs ));
541         }
542 */
543
544         if (Interp_thrust_scale_subobj) {
545
546                 // Only scale vertices that aren't on the "base" of 
547                 // the effect.  Base is something Adam decided to be
548                 // anything under 1.5 meters, hence the 1.5f.
549                 float min_thruster_dist = -1.5f;
550
551                 if ( Interp_flags & MR_IS_MISSILE )     {
552                         min_thruster_dist = 0.5f;
553                 }
554
555                 for (n=0; n<nverts; n++ )       {
556                         vector tmp;
557
558                         Interp_verts[n] = src;
559
560                         // Only scale vertices that aren't on the "base" of 
561                         // the effect.  Base is something Adam decided to be
562                         // anything under 1.5 meters, hence the 1.5f.
563                         if ( src->xyz.z < min_thruster_dist )   {
564                                 tmp.xyz.x = src->xyz.x * 1.0f;
565                                 tmp.xyz.y = src->xyz.y * 1.0f;
566                                 tmp.xyz.z = src->xyz.z * Interp_thrust_scale;
567                         } else {
568                                 tmp = *src;
569                         }
570                         
571                         g3_rotate_vertex(dest,&tmp);
572                 
573                         src++;          // move to normal
574
575                         for (i=0; i<normcount[n]; i++ ) {
576                                 Interp_light_applied[next_norm] = 0;
577                                 Interp_norms[next_norm] = src;
578
579                                 next_norm++;
580                                 src++;
581                         }
582                         dest++;
583                 } 
584
585         } else {
586                 for (n=0; n<nverts; n++ )       {       
587                         Interp_verts[n] = src;  
588                         
589                         /*
590                         vector tmp = *src;
591                         // TEST
592                         if(Interp_thrust_twist > 0.0f){
593                                 float theta;
594                                 float st, ct;
595
596                                 // determine theta for this vertex                              
597                                 theta = fl_radian(20.0f + Interp_thrust_twist2);                                
598                                 st = sin(theta);
599                                 ct = cos(theta);
600
601                                 // twist
602                                 tmp.xyz.z = (src->xyz.z * ct) - (src->xyz.y * st);
603                                 tmp.xyz.y = (src->xyz.z * st) + (src->xyz.y * ct);
604
605                                 // scale the z a bit
606                                 tmp.xyz.z += Interp_thrust_twist;
607                         }                       
608         
609                         g3_rotate_vertex(dest, &tmp);
610                         */
611                         
612                         g3_rotate_vertex(dest, src);
613                 
614                         src++;          // move to normal
615
616                         for (i=0; i<normcount[n]; i++ ) {
617                                 Interp_light_applied[next_norm] = 0;
618                                 Interp_norms[next_norm] = src;
619
620                                 next_norm++;
621                                 src++;
622                         }
623                         dest++;
624                 }
625         }
626
627         Interp_num_norms = next_norm;
628
629 }
630
631 matrix *Interp_orient;
632 vector *Interp_pos;
633
634 /*
635 void interp_compute_environment_mapping( vector *nrm, vertex * pnt, vector *vert)
636 {
637         vector R;
638         float a;
639         matrix * m = &View_matrix;
640
641         vm_vec_rotate( &R, nrm, &View_matrix ); 
642         vm_vec_normalize(&R);
643
644         a = 2.0f * R.xyz.z;
645         R.xyz.x = a * R.xyz.x;  // reflected R = 2N(N.E) -E;  E = eye
646         R.xyz.y = a * R.xyz.y;
647         R.xyz.z = a * R.xyz.z;
648         vm_vec_normalize(&R);
649         a = (float)fl_sqrt( 1.0f - R.xyz.y * R.xyz.y);
650         pnt->u = (float)atan2( R.xyz.x, -R.xyz.z) / (2.0f * 3.14159f);
651         if (pnt->u < 0.0) pnt->u += 1.0f;
652         pnt->v = 1.0f - (float)atan2( a, R.xyz.y) / 3.14159f;
653 }
654 */
655
656
657 // Flat Poly
658 // +0      int         id
659 // +4      int         size 
660 // +8      vector      normal
661 // +20     vector      center
662 // +32     float       radius
663 // +36     int         nverts
664 // +40     byte        red
665 // +41     byte        green
666 // +42     byte        blue
667 // +43     byte        pad
668 // +44     nverts*short*short  vertlist, smoothlist
669 void model_interp_flatpoly(ubyte * p,polymodel * pm)
670 {
671         vertex *Interp_list[TMAP_MAX_VERTS];
672         int nv = w(p+36);
673
674         if ( nv < 0 )   return;
675
676         #ifndef NDEBUG
677         modelstats_num_polys++;
678         #endif
679
680         if (!g3_check_normal_facing(vp(p+20),vp(p+8)) ) return;
681         
682
683         int i;
684         short * verts = (short *)(p+44);
685         
686         for (i=0;i<nv;i++)      {
687                 Interp_list[i] = &Interp_points[verts[i*2]];
688
689                 if ( Interp_flags & MR_NO_LIGHTING )    {
690                         Interp_list[i]->r = 191;
691                         Interp_list[i]->g = 191;
692                         Interp_list[i]->b = 191;
693                 } else {
694                         int vertnum = verts[i*2+0];
695                         int norm = verts[i*2+1];
696         
697                         if ( Interp_flags & MR_NO_SMOOTHING )   {
698                                 light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
699                         } else {
700                                 // if we're not using saved lighting
701                                 if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] )        {
702                                         light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], vp(p+8), Interp_light );
703                                         Interp_light_applied[norm] = 1;
704                                 }
705
706                                 Interp_list[i]->r = Interp_lighting->r[norm];
707                                 Interp_list[i]->g = Interp_lighting->g[norm];
708                                 Interp_list[i]->b = Interp_lighting->b[norm];
709                         }
710                 }
711         }
712
713         // HACK!!! FIX ME!!! I'M SLOW!!!!
714         if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
715                 gr_set_color( *(p+40), *(p+41), *(p+42) );
716         }
717
718         if ( !(Interp_flags & MR_NO_POLYS))     {
719                 g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB );
720         }
721
722         if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET))    {
723                 int i, j;
724
725                 if ( Interp_flags & MR_SHOW_OUTLINE )   {
726                         gr_set_color_fast( &Interp_outline_color );
727                 }
728
729                 for (i=0; i<nv; i++ )   {
730                         j = (i + 1) % nv;
731                         g3_draw_line(Interp_list[i], Interp_list[j] );
732                 }
733         }
734 }
735
736 // Textured Poly
737 // +0      int         id
738 // +4      int         size 
739 // +8      vector      normal
740 // +20     vector      center
741 // +32     float       radius
742 // +36     int         nverts
743 // +40     int         tmap_num
744 // +44     nverts*(model_tmap_vert) vertlist (n,u,v)
745 extern int Tmap_show_layers;
746
747 int Interp_subspace = 0;
748 float Interp_subspace_offset_u = 0.0f;
749 float Interp_subspace_offset_v = 0.0f;
750 ubyte Interp_subspace_r = 255;
751 ubyte Interp_subspace_g = 255;
752 ubyte Interp_subspace_b = 255;
753
754 void model_interp_tmappoly(ubyte * p,polymodel * pm)
755 {
756         vertex *Interp_list[TMAP_MAX_VERTS];
757         int i;
758         int nv;
759         model_tmap_vert *verts;
760
761         int is_invisible = 0;
762
763         if ((!Interp_thrust_scale_subobj) && (pm->textures[w(p+40)]<0)) {
764                 // Don't draw invisible polygons.
765                 if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES)) {
766                         return;
767                 } else {
768                         is_invisible = 1;
769                 }
770         }
771
772         nv = w(p+36);
773
774 //      Tmap_show_layers = 1;
775
776         #ifndef NDEBUG
777         modelstats_num_polys++;
778         #endif
779
780         if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)) return;
781
782         if ( nv < 0 ) return;
783
784         verts = (model_tmap_vert *)(p+44);
785
786         for (i=0;i<nv;i++)      {
787                 Interp_list[i] = &Interp_points[verts[i].vertnum];
788
789                 Interp_list[i]->u = verts[i].u;
790                 Interp_list[i]->v = verts[i].v;
791                 
792                 if ( Interp_subspace )  {
793                         Interp_list[i]->v += Interp_subspace_offset_u;
794                         Interp_list[i]->u += Interp_subspace_offset_v;
795                         Interp_list[i]->r = Interp_subspace_r;
796                         Interp_list[i]->g = Interp_subspace_g;
797                         Interp_list[i]->b = Interp_subspace_b;
798                 } else {
799
800         //              if ( !(pm->flags & PM_FLAG_ALLOW_TILING) )      {
801         //                      SDL_assert(verts[i].u <= 1.0f );
802         //                      SDL_assert(verts[i].v <= 1.0f );
803         //              }
804
805         //              SDL_assert( verts[i].normnum == verts[i].vertnum );
806
807                         if ( Interp_flags & MR_NO_LIGHTING )    {
808                                 Interp_list[i]->r = 191;
809                                 Interp_list[i]->g = 191;
810                                 Interp_list[i]->b = 191;
811                         } else {
812                                 int vertnum = verts[i].vertnum;
813                                 int norm = verts[i].normnum;
814                 
815                                 if ( Interp_flags & MR_NO_SMOOTHING )   {
816                                         light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
817                                 } else {                                        
818                                         // if we're applying lighting as normal, and not using saved lighting
819                                         if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] )        {
820                                                 light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], Interp_norms[norm], Interp_light );
821
822                                                 Interp_light_applied[norm] = 1;
823                                         }
824
825                                         Interp_list[i]->r = Interp_lighting->r[norm];
826                                         Interp_list[i]->g = Interp_lighting->g[norm];
827                                         Interp_list[i]->b = Interp_lighting->b[norm];
828                                 }
829                         }
830                 }
831
832 //              SDL_assert(verts[i].u >= 0.0f );
833 //              SDL_assert(verts[i].v >= 0.0f );
834         }
835
836         #ifndef NDEBUG
837         modelstats_num_polys_drawn++;
838         #endif
839
840         if (!(Interp_flags & MR_NO_POLYS) )             {
841                 if ( is_invisible )     {
842                         gr_set_color( 0, 255, 0 );
843                         g3_draw_poly( nv, Interp_list, 0 );             
844                 } else if (Interp_thrust_scale_subobj)  {
845                         if ( (Interp_thrust_bitmap > -1) && (Interp_thrust_scale > 0.0f) ) {
846                                 gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f, -1, -1);
847                                 g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );            
848                         } else if ( !Pofview_running ) {
849                                 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
850                                         gr_set_color( 128, 128, 255 );
851                                 }
852                                 uint tflags = Interp_tmap_flags;
853                                 tflags &= (~(TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT));
854                                 g3_draw_poly( nv, Interp_list, tflags );                
855                         }
856                 } else {
857                         // all textured polys go through here
858                         if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED )   {
859                                 // subspace special case
860                                 if (Interp_subspace) {
861                                         gr_set_bitmap( pm->textures[w(p+40)], GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f, -1, -1);
862                                 }
863                                 // all other textures
864                                 else {                                  
865                                         int texture;
866
867                                         // if we're rendering a nebula background pof, maybe select a custom texture
868                                         if((Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0)){
869                                                 texture = Interp_forced_bitmap;
870                                         } else {
871                                                 texture = pm->textures[w(p+40)];
872                                         }
873
874                                         // muzzle flashes draw xparent
875                                         if(Interp_flags & MR_ALL_XPARENT){
876                                                 gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha, -1, -1);
877                                         } else {
878                                                 gr_set_bitmap( texture, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
879                                         }
880                                 }
881                         } else {
882                                 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
883                                         gr_set_color( 128, 128, 128 );
884                                 }
885                         }
886
887                         if ( Interp_subspace )  {
888                                 g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT );          
889                         } else {                                
890                                 if(Interp_flags & MR_ALL_XPARENT){
891                                         g3_draw_poly( nv, Interp_list, Interp_tmap_flags );
892                                 } else {
893                                         g3_draw_poly( nv, Interp_list, Interp_tmap_flags|TMAP_FLAG_NONDARKENING );              
894                                 }
895                         }
896                 }
897         }
898
899         if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) )   {
900         
901                 if ( Interp_flags & MR_SHOW_OUTLINE )   {
902                         gr_set_color_fast( &Interp_outline_color );
903                 }
904
905                 for (i=0; i<nv; i++ )   {
906                         int j = (i + 1) % nv;
907                         g3_draw_line(Interp_list[i], Interp_list[j] );
908                 }
909         }
910
911 }
912
913 void interp_draw_box( vector *min, vector *max )
914 {
915 /*
916         int i;
917         vector bounding_box[8];
918         vertex pts[8];
919         vertex *l[4];
920
921         model_calc_bound_box(bounding_box,min,max);
922
923         for (i=0; i<8; i++ )    {
924                 g3_rotate_vertex( &pts[i], &bounding_box[i] );
925         }
926
927         gr_set_color(128,0,0);
928
929         Tmap_show_layers = 1;
930
931         l[3] = &pts[0];  l[2] = &pts[1]; l[1] = &pts[2];  l[0] = &pts[3];
932         g3_draw_poly( 4, l, 0 );
933
934         l[3] = &pts[3];  l[2] = &pts[2]; l[1] = &pts[6];  l[0] = &pts[7];
935         g3_draw_poly( 4, l, 0 );
936
937         l[3] = &pts[2];  l[2] = &pts[1]; l[1] = &pts[5];  l[0] = &pts[6];
938         g3_draw_poly( 4, l, 0 );
939
940         l[3] = &pts[1];  l[2] = &pts[0]; l[1] = &pts[4];  l[0] = &pts[5];
941         g3_draw_poly( 4, l, 0 );
942
943         l[3] = &pts[0];  l[2] = &pts[3]; l[1] = &pts[7];  l[0] = &pts[4];
944         g3_draw_poly( 4, l, 0 );
945
946         l[3] = &pts[4];  l[2] = &pts[7]; l[1] = &pts[6];  l[0] = &pts[5];
947         g3_draw_poly( 4, l, 0 );
948 */
949 }
950
951
952 // Sortnorms
953 // +0      int         id
954 // +4      int         size 
955 // +8      vector      normal
956 // +20     vector      normal_point
957 // +32     int         tmp=0
958 // 36     int     front offset
959 // 40     int     back offset
960 // 44     int     prelist offset
961 // 48     int     postlist offset
962 // 52     int     online offset
963 void model_interp_sortnorm(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
964 {
965         #ifndef NDEBUG
966         modelstats_num_sortnorms++;
967         #endif
968
969 //      SDL_assert( w(p+4) == 56 );
970
971         int frontlist = w(p+36);
972         int backlist = w(p+40);
973         int prelist = w(p+44);
974         int postlist = w(p+48);
975         int onlist = w(p+52);
976
977         // min = 
978         // max = 
979 //      light_filter_push_box( vp(p+56), vp(p+68) );
980
981 #if 1   //def BACK_TO_FRONT
982
983         if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check);            // prelist
984
985         if (g3_check_normal_facing(vp(p+20),vp(p+8))) {         //facing
986
987                 //draw back then front
988
989                 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
990
991                 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check);                      //onlist
992
993                 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
994
995         }       else {                  //not facing.  draw front then back
996
997                 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
998
999                 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check);                      //onlist
1000
1001                 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1002         }
1003
1004         if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check);          // postlist
1005
1006 #else
1007         if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check);          // postlist
1008
1009         if (g3_check_normal_facing(vp(p+20),vp(p+8))) {         //facing
1010
1011                 // 
1012
1013                 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1014
1015                 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check);                      //onlist
1016
1017                 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1018
1019         }       else {                  //not facing.  draw front then back
1020
1021                 //draw back then front
1022
1023                 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1024
1025                 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check);                      //onlist
1026
1027                 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1028
1029         }
1030
1031         if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check);            // prelist
1032 #endif
1033
1034
1035 //      light_filter_pop();
1036 }
1037
1038
1039 void model_draw_debug_points( polymodel *pm, bsp_info * submodel )
1040 {
1041         if ( Interp_flags & MR_SHOW_OUTLINE_PRESET )    {
1042                 return;
1043         }
1044
1045         // Draw the brown "core" mass
1046 //      if ( submodel && (submodel->parent==-1) )       {
1047 //              gr_set_color(128,128,0);
1048 //              g3_draw_sphere_ez( &vmd_zero_vector, pm->core_radius );
1049 //      }
1050
1051         // Draw a red pivot point
1052         gr_set_color(128,0,0);
1053         g3_draw_sphere_ez(&vmd_zero_vector, 2.0f );
1054
1055         // Draw a green center of mass when drawing the hull
1056         if ( submodel && (submodel->parent==-1) )       {
1057                 gr_set_color(0,128,0);
1058                 g3_draw_sphere_ez( &pm->center_of_mass, 1.0f );
1059         }
1060
1061         if ( submodel ) {
1062                 // Draw a blue center point
1063                 gr_set_color(0,0,128);
1064                 g3_draw_sphere_ez( &submodel->geometric_center, 0.9f );
1065         }
1066         
1067         // Draw the bounding box
1068         int i;
1069         vertex pts[8];
1070
1071         if ( submodel ) {
1072                 for (i=0; i<8; i++ )    {
1073                         g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] );
1074                 }
1075                 gr_set_color(128,128,128);
1076                 g3_draw_line( &pts[0], &pts[1] );
1077                 g3_draw_line( &pts[1], &pts[2] );
1078                 g3_draw_line( &pts[2], &pts[3] );
1079                 g3_draw_line( &pts[3], &pts[0] );
1080
1081                 g3_draw_line( &pts[4], &pts[5] );
1082                 g3_draw_line( &pts[5], &pts[6] );
1083                 g3_draw_line( &pts[6], &pts[7] );
1084                 g3_draw_line( &pts[7], &pts[4] );
1085
1086                 g3_draw_line( &pts[0], &pts[4] );
1087                 g3_draw_line( &pts[1], &pts[5] );
1088                 g3_draw_line( &pts[2], &pts[6] );
1089                 g3_draw_line( &pts[3], &pts[7] );
1090         } else {
1091                 //for (i=0; i<8; i++ )  {
1092                 //      g3_rotate_vertex( &pts[i], &pm->bounding_box[i] );
1093                 //}
1094                 gr_set_color(0,255,0);
1095
1096                 int j;
1097                 for (j=0; j<8; j++ )    {
1098
1099                         vector  bounding_box[8];                // caclulated fron min/max
1100                         model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max);
1101
1102                         for (i=0; i<8; i++ )    {
1103                                 g3_rotate_vertex( &pts[i], &bounding_box[i] );
1104                         }
1105                         gr_set_color(128,0,0);
1106                         g3_draw_line( &pts[0], &pts[1] );
1107                         g3_draw_line( &pts[1], &pts[2] );
1108                         g3_draw_line( &pts[2], &pts[3] );
1109                         g3_draw_line( &pts[3], &pts[0] );
1110
1111                         g3_draw_line( &pts[4], &pts[5] );
1112                         g3_draw_line( &pts[5], &pts[6] );
1113                         g3_draw_line( &pts[6], &pts[7] );
1114                         g3_draw_line( &pts[7], &pts[4] );
1115
1116                         g3_draw_line( &pts[0], &pts[4] );
1117                         g3_draw_line( &pts[1], &pts[5] );
1118                         g3_draw_line( &pts[2], &pts[6] );
1119                         g3_draw_line( &pts[3], &pts[7] );                       
1120                 }               
1121         }
1122 }
1123
1124 // Debug code to show all the paths of a model
1125 void model_draw_paths( int model_num )
1126 {
1127         int i,j;
1128         vector pnt;
1129         polymodel * pm; 
1130
1131         if ( Interp_flags & MR_SHOW_OUTLINE_PRESET )    {
1132                 return;
1133         }
1134
1135         pm = model_get(model_num);
1136
1137         if (pm->n_paths<1){
1138                 return;
1139         }       
1140
1141         for (i=0; i<pm->n_paths; i++ )  {
1142                 vertex prev_pnt;
1143
1144                 for (j=0; j<pm->paths[i].nverts; j++ )  {
1145                         // Rotate point into world coordinates                  
1146                         pnt = pm->paths[i].verts[j].pos;
1147
1148                         // Pnt is now the x,y,z world coordinates of this vert.
1149                         // For this example, I am just drawing a sphere at that
1150                         // point.
1151                         {
1152                                 vertex tmp;
1153                                 g3_rotate_vertex(&tmp,&pnt);
1154
1155                                 if ( pm->paths[i].verts[j].nturrets > 0 ){
1156                                         gr_set_color( 0, 0, 255 );                                              // draw points covered by turrets in blue
1157                                 } else {
1158                                         gr_set_color( 255, 0, 0 );
1159                                 }
1160
1161 //                              g3_draw_sphere( &tmp, pm->paths[i].verts[j].radius );
1162                                 g3_draw_sphere( &tmp, 0.5f );
1163
1164                                 gr_set_color( 255, 0, 0 );
1165                                 if (j){
1166                                         g3_draw_line(&prev_pnt, &tmp);
1167                                 }
1168
1169                                 prev_pnt = tmp;
1170                         }
1171                 }
1172         }
1173 }
1174
1175
1176 // docking bay and fighter bay paths
1177 void model_draw_bay_paths(int model_num)
1178 {
1179         int idx, s_idx;
1180         vector v1, v2;
1181         vertex l1, l2;  
1182
1183         polymodel *pm = model_get(model_num);
1184         if(pm == NULL){
1185                 return;
1186         }
1187
1188         // render docking bay normals
1189         gr_set_color(0, 255, 0);
1190         for(idx=0; idx<pm->n_docks; idx++){
1191                 for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
1192                         v1 = pm->docking_bays[idx].pnt[s_idx];
1193                         vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
1194
1195                         // rotate the points
1196                         g3_rotate_vertex(&l1, &v1);
1197                         g3_rotate_vertex(&l2, &v2);
1198
1199                         // draw the point and normal
1200                         g3_draw_sphere(&l1, 2.0);
1201                         g3_draw_line(&l1, &l2);
1202                 }
1203         }
1204
1205         // render figher bay paths
1206         gr_set_color(0, 255, 255);
1207                 
1208         // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
1209         for (idx = 0; idx<pm->n_paths; idx++) {
1210                 if ( !SDL_strncasecmp(pm->paths[idx].name, NOX("$bay"), 4) ) {                                          
1211                         for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
1212                                 v1 = pm->paths[idx].verts[s_idx].pos;
1213                                 v2 = pm->paths[idx].verts[s_idx+1].pos;
1214
1215                                 // rotate and draw
1216                                 g3_rotate_vertex(&l1, &v1);
1217                                 g3_rotate_vertex(&l2, &v2);
1218                                 g3_draw_line(&l1, &l2);
1219                         }
1220                 }
1221         }       
1222 }       
1223 /*
1224 // struct that holds the indicies into path information associated with a fighter bay on a capital ship
1225 // NOTE: Fighter bay paths are identified by the path_name $bayN (where N is numbered from 1).
1226 //                      Capital ships only have ONE fighter bay on the entire ship
1227 #define MAX_SHIP_BAY_PATHS              10
1228 typedef struct ship_bay {
1229         int     num_paths;                                                      // how many paths are associated with the model's fighter bay
1230         int     paths[MAX_SHIP_BAY_PATHS];              // index into polymodel->paths[] array
1231         int     arrive_flags;   // bitfield, set to 1 when that path number is reserved for an arrival
1232         int     depart_flags;   // bitfield, set to 1 when that path number is reserved for a departure
1233 } ship_bay;
1234
1235   typedef struct mp_vert {
1236         vector          pos;                            // xyz coordinates of vertex in object's frame of reference
1237         int                     nturrets;               // number of turrets guarding this vertex
1238         int                     *turret_ids;    // array of indices into ship_subsys linked list (can't index using [] though)
1239         float                   radius;                 // How far the closest obstruction is from this vertex
1240 } mp_vert;
1241
1242 typedef struct model_path {
1243         char                    name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
1244         char                    parent_name[MAX_NAME_LEN];                      // parent name of submodel that path is linked to in POF
1245         int                     parent_submodel;
1246         int                     nverts;
1247         mp_vert         *verts;
1248         int                     goal;                   // Which of the verts is the one closest to the goal of this path
1249         int                     type;                   // What this path takes you to... See MP_TYPE_??? defines above for details
1250         int                     value;          // This depends on the type.
1251                                                                         // For MP_TYPE_UNUSED, this means nothing.
1252                                                                         // For MP_TYPE_SUBSYS, this is the subsystem number this path takes you to.
1253 } model_path;
1254 */
1255
1256
1257 void interp_render_arc_segment( vector *v1, vector *v2, int depth )
1258 {
1259         float d = vm_vec_dist_quick( v1, v2 );
1260
1261         if ( d < 0.30f || (depth>4) )   {
1262                 vertex p1, p2;
1263                 g3_rotate_vertex( &p1, v1 );
1264                 g3_rotate_vertex( &p2, v2 );
1265
1266                 //g3_draw_rod( v1, 0.2f, v2, 0.2f, NULL, 0);
1267                 g3_draw_line( &p1, &p2 );
1268         } else {
1269                 // divide in half
1270                 vector tmp;
1271                 vm_vec_avg( &tmp, v1, v2 );
1272         
1273                 float scaler = 0.30f;
1274                 tmp.xyz.x += (frand()-0.5f)*d*scaler;
1275                 tmp.xyz.y += (frand()-0.5f)*d*scaler;
1276                 tmp.xyz.z += (frand()-0.5f)*d*scaler;
1277                 
1278                 interp_render_arc_segment( v1, &tmp, depth+1 );
1279                 interp_render_arc_segment( &tmp, v2, depth+1 );
1280         }
1281 }
1282
1283 int Interp_lightning = 1;
1284 DCF_BOOL( Arcs, Interp_lightning )
1285
1286 int AR = 64;
1287 int AG = 64;
1288 int AB = 5;
1289 int AR2 = 128;
1290 int AG2 = 128;
1291 int AB2 = 10;
1292 void interp_render_lightning( polymodel *pm, bsp_info * sm )
1293 {
1294         SDL_assert( sm->num_arcs > 0 );
1295
1296         int i;
1297
1298         if ( Interp_flags & MR_SHOW_OUTLINE_PRESET )    {
1299                 return;
1300         }
1301
1302         if (!Interp_lightning) return;
1303
1304 //      if ( key_pressed(SDLK_LSHIFT) ) return;
1305 //      if ( rad < 3.0f ) return;       
1306         
1307         for (i=0; i<sm->num_arcs; i++ ) {
1308                 // pick a color based upon arc type
1309                 switch(sm->arc_type[i]){
1310                 // "normal", Freespace 1 style arcs
1311                 case MARC_TYPE_NORMAL:
1312                         if ( (myrand()>>4) & 1 )        {
1313                                 gr_set_color( 64, 64, 255 );
1314                         } else {
1315                                 gr_set_color( 128, 128, 255 );
1316                         }
1317                         break;
1318
1319                 // "EMP" style arcs
1320                 case MARC_TYPE_EMP:
1321                         if ( (myrand()>>4) & 1 )        {
1322                                 gr_set_color( AR, AG, AB );
1323                         } else {
1324                                 gr_set_color( AR2, AG2, AB2 );
1325                         }
1326                         break;
1327
1328                 default:
1329                         Int3();
1330                 }
1331
1332                 // render the actual arc segment
1333                 interp_render_arc_segment( &sm->arc_pts[i][0], &sm->arc_pts[i][1], 0 );
1334         }
1335 }
1336
1337 void model_interp_subcall(polymodel * pm, int mn, int detail_level)
1338 {
1339         int i;
1340         int zbuf_mode = gr_zbuffer_get();
1341
1342         if ( (mn < 0) || (mn>=pm->n_models) )
1343                 return;
1344
1345         SDL_assert( mn >= 0 );
1346         SDL_assert( mn < pm->n_models );
1347
1348 //      mprintf(( "Name = '%s'\n", pm->submodel[mn].name ));
1349 //      char * p = pm->submodel[mn].name;
1350
1351         if (pm->submodel[mn].blown_off){
1352                 return;
1353         }
1354
1355         if (pm->submodel[mn].is_thruster )      {
1356                 if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){
1357                         return;
1358                 }
1359                 Interp_thrust_scale_subobj=1;
1360         } else {
1361                 Interp_thrust_scale_subobj=0;
1362         }
1363         
1364         g3_start_instance_angles(&pm->submodel[mn].offset, &pm->submodel[mn].angs);
1365         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
1366                 light_rotate_all();
1367         }
1368
1369         model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 );
1370
1371         if (Interp_flags & MR_SHOW_PIVOTS )
1372                 model_draw_debug_points( pm, &pm->submodel[mn] );
1373
1374         if ( pm->submodel[mn].num_arcs )        {
1375                 interp_render_lightning( pm, &pm->submodel[mn]);
1376         }
1377
1378         i = pm->submodel[mn].first_child;
1379         while( i>-1 )   {
1380                 if (!pm->submodel[i].is_thruster )      {
1381                         if(Interp_flags & MR_NO_ZBUFFER){
1382                                 zbuf_mode = GR_ZBUFF_NONE;
1383                         } else {
1384                                 zbuf_mode = GR_ZBUFF_FULL;              // read only
1385                         }
1386
1387                         gr_zbuffer_set(zbuf_mode);
1388
1389                         model_interp_subcall( pm, i, detail_level );
1390                 }
1391                 i = pm->submodel[i].next_sibling;
1392         }
1393
1394
1395
1396         g3_done_instance();
1397 }
1398
1399 // Returns one of the following
1400 #define IBOX_ALL_OFF 0
1401 #define IBOX_ALL_ON 1
1402 #define IBOX_SOME_ON_SOME_OFF 2
1403
1404 int interp_box_offscreen( vector *min, vector *max )
1405 {
1406         if ( key_pressed(SDLK_LSHIFT) ) {
1407                 return IBOX_ALL_ON;
1408         }
1409
1410         vector v[8];
1411         v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z;
1412         v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z;
1413         v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z;
1414         v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z;
1415
1416         v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z;
1417         v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z;
1418         v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z;
1419         v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z;
1420
1421         ubyte and_codes = 0xff;
1422         ubyte or_codes = 0xff;
1423         int i;
1424
1425         for (i=0; i<8; i++ )    {
1426                 vertex tmp;
1427                 ubyte codes=g3_rotate_vertex( &tmp, &v[i] );
1428 // Early out which we cannot do because we want to differentiate btwn
1429 // IBOX_SOME_ON_SOME_OFF and IBOX_ALL_ON
1430 //              if ( !codes )   {
1431 //                      //mprintf(( "A point is inside, so render it.\n" ));
1432 //                      return 0;               // this point is in, so return 0
1433 //              }
1434                 or_codes |= codes;
1435                 and_codes &= codes;
1436         }
1437
1438         // If and_codes is set this means that all points lie off to the
1439         // same side of the screen.
1440         if (and_codes)  {
1441                 //mprintf(( "All points offscreen, so don't render it.\n" ));
1442                 return IBOX_ALL_OFF;    //all points off screen
1443         }
1444
1445         // If this is set it means at least one of the points is offscreen,
1446         // but they aren't all off to the same side.
1447         if (or_codes)   {
1448                 return IBOX_SOME_ON_SOME_OFF;
1449         }
1450
1451         // They are all onscreen.
1452         return IBOX_ALL_ON;     
1453 }
1454
1455
1456 //calls the object interpreter to render an object.  
1457 //returns true if drew
1458 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check )
1459 {
1460         ubyte *p = (ubyte *)model_ptr;
1461         int chunk_type, chunk_size;
1462         int pushed = 0;
1463
1464         chunk_type = w(p);
1465         chunk_size = w(p+4);
1466
1467         while ( chunk_type != OP_EOF )  {
1468
1469 //              mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size ));
1470
1471                 switch (chunk_type) {
1472                 case OP_EOF: return 1;
1473                 case OP_DEFPOINTS:      model_interp_defpoints(p,pm,sm); break;
1474                 case OP_FLATPOLY:               model_interp_flatpoly(p,pm); break;
1475                 case OP_TMAPPOLY:               model_interp_tmappoly(p,pm);    break;
1476                 case OP_SORTNORM:               model_interp_sortnorm(p,pm,sm,do_box_check); break;
1477         
1478                 case OP_BOUNDBOX:               
1479
1480                         if ( do_box_check )     {
1481                                 int retval = interp_box_offscreen( vp(p+8), vp(p+20) );
1482                                 switch( retval )        {
1483                                 case IBOX_ALL_OFF:
1484                                         goto DoneWithThis;      // Don't need to draw any more polys from this box
1485                                         break;
1486
1487                                 case IBOX_ALL_ON:
1488                                         do_box_check = 0;               // Don't need to check boxes any more
1489                                         break;
1490
1491                                 case IBOX_SOME_ON_SOME_OFF:
1492                                         // continue like we were
1493                                         break;
1494                                 default:
1495                                         Int3();
1496                                 }
1497                         }
1498
1499
1500                         if (Interp_flags & MR_SHOW_PIVOTS )     {
1501                                 #ifndef NDEBUG
1502                                 modelstats_num_boxes++;
1503                                 #endif
1504                                 interp_draw_box( vp(p+8), vp(p+20) );
1505                         }
1506
1507                         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
1508                                 if ( pushed )   {
1509                                         light_filter_pop();
1510                                         pushed = 0;
1511
1512                                 }
1513                                 light_filter_push_box( vp(p+8), vp(p+20) );
1514                                 pushed = 1;
1515                         }
1516                         break;
1517
1518                 default:
1519                         mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size ));
1520                         Int3();         // Bad chunk type!
1521                         return 0;
1522                 }
1523                 p += chunk_size;
1524                 chunk_type = w(p);
1525                 chunk_size = w(p+4);
1526         }
1527
1528 DoneWithThis:
1529
1530         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
1531                 if ( pushed )   {
1532                         light_filter_pop();
1533                         pushed = 0;
1534                 }
1535         }
1536
1537         return 1;
1538 }
1539
1540
1541 void model_render_shields( polymodel * pm )
1542 {
1543         int i, j;
1544         shield_tri *tri;
1545         vertex pnt0, tmp, prev_pnt;
1546
1547         if ( Interp_flags & MR_SHOW_OUTLINE_PRESET )    {
1548                 return;
1549         }
1550
1551         gr_set_color(0, 0, 200 );
1552
1553         //      Scan all the triangles in the mesh.
1554         for (i=0; i<pm->shield.ntris; i++ )     {
1555
1556                 tri = &pm->shield.tris[i];
1557
1558                 if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) {
1559
1560                         //      Process the vertices.
1561                         //      Note this rotates each vertex each time it's needed, very dumb.
1562                         for (j=0; j<3; j++ )    {
1563
1564                                 g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos );
1565
1566                                 if (j)
1567                                         g3_draw_line(&prev_pnt, &tmp);
1568                                 else
1569                                         pnt0 = tmp;
1570                                 prev_pnt = tmp;
1571                         }
1572
1573                         g3_draw_line(&pnt0, &prev_pnt);
1574                 }
1575         }
1576 }
1577
1578 void model_render_insignias(polymodel *pm, int detail_level)
1579 {
1580         int idx, s_idx;
1581         vertex vecs[3];
1582         vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] };
1583         vector t1, t2, t3;
1584         int i1, i2, i3;
1585
1586         // if the model has no insignias we're done
1587         if(pm->num_ins <= 0){
1588                 return;
1589         }
1590
1591         // set the proper texture
1592         if(Interp_insignia_bitmap >= 0){                
1593                 gr_set_bitmap(Interp_insignia_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1594         }
1595         // otherwise don't even bother rendering
1596         else {
1597                 return;
1598         }
1599
1600         // otherwise render them        
1601         for(idx=0; idx<pm->num_ins; idx++){     
1602                 // skip insignias not on our detail level
1603                 if(pm->ins[idx].detail_level != detail_level){
1604                         continue;
1605                 }
1606
1607                 for(s_idx=0; s_idx<pm->ins[idx].num_faces; s_idx++){
1608                         // get vertex indices
1609                         i1 = pm->ins[idx].faces[s_idx][0];
1610                         i2 = pm->ins[idx].faces[s_idx][1];
1611                         i3 = pm->ins[idx].faces[s_idx][2];
1612
1613                         // transform vecs and setup vertices
1614                         vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset);
1615                         vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset);
1616                         vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset);
1617                         g3_rotate_vertex(&vecs[0], &t1);
1618                         g3_rotate_vertex(&vecs[1], &t2);
1619                         g3_rotate_vertex(&vecs[2], &t3);
1620                         
1621                         // setup texture coords
1622                         vecs[0].u = pm->ins[idx].u[s_idx][0];  vecs[0].v = pm->ins[idx].v[s_idx][0];
1623                         vecs[1].u = pm->ins[idx].u[s_idx][1];  vecs[1].v = pm->ins[idx].v[s_idx][1];
1624                         vecs[2].u = pm->ins[idx].u[s_idx][2];  vecs[2].v = pm->ins[idx].v[s_idx][2];
1625
1626                         // draw the polygon
1627                         g3_draw_poly(3, vlist, TMAP_FLAG_TEXTURED);
1628                 }
1629         }
1630 }
1631
1632 int Model_texturing = 1;
1633 int Model_polys = 1;
1634
1635 DCF_BOOL( model_texturing, Model_texturing )
1636 DCF_BOOL( model_polys, Model_polys )
1637
1638 MONITOR( NumModelsRend );       
1639 MONITOR( NumHiModelsRend );     
1640 MONITOR( NumMedModelsRend );    
1641 MONITOR( NumLowModelsRend );    
1642
1643
1644 void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id );
1645
1646
1647 //draws a bitmap with the specified 3d width & height 
1648 //returns 1 if off screen, 0 if not
1649 int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v)
1650 {
1651         float sa, ca;
1652         int i;
1653
1654         SDL_assert( G3_count == 1 );
1655
1656
1657
1658 //      angle = 0.0f;
1659                 
1660         sa = (float)sin(angle);
1661         ca = (float)cos(angle);
1662
1663         float width, height;
1664
1665         width = height = rad;
1666
1667         v[0].x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
1668         v[0].y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
1669         v[0].z = pnt->z;
1670         v[0].sw = 0.0f;
1671         v[0].u = 0.0f;
1672         v[0].v = 0.0f;
1673
1674         v[1].x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
1675         v[1].y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
1676         v[1].z = pnt->z;
1677         v[1].sw = 0.0f;
1678         v[1].u = 1.0f;
1679         v[1].v = 0.0f;
1680
1681         v[2].x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
1682         v[2].y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
1683         v[2].z = pnt->z;
1684         v[2].sw = 0.0f;
1685         v[2].u = 1.0f;
1686         v[2].v = 1.0f;
1687
1688         v[3].x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
1689         v[3].y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
1690         v[3].z = pnt->z;
1691         v[3].sw = 0.0f;
1692         v[3].u = 0.0f;
1693         v[3].v = 1.0f;
1694
1695         ubyte codes_and=0xff;
1696
1697         float sw,z;
1698         z = pnt->z - rad / 4.0f;
1699         if ( z < 0.0f ) z = 0.0f;
1700         sw = 1.0f / z;
1701
1702         for (i=0; i<4; i++ )    {
1703                 //now code the four points
1704                 codes_and &= g3_code_vertex(&v[i]);
1705                 v[i].flags = 0;         // mark as not yet projected
1706                 g3_project_vertex(&v[i]);
1707                 v[i].sw = sw;
1708         }
1709
1710         if (codes_and)
1711                 return 1;               //1 means off screen
1712
1713         return 0;
1714 }
1715
1716
1717 float Interp_depth_scale = 1500.0f;
1718
1719 DCF(model_darkening,"Makes models darker with distance")
1720 {
1721         if ( Dc_command )       {
1722                 dc_get_arg(ARG_FLOAT);
1723                 Interp_depth_scale = Dc_arg_float;
1724         }
1725
1726         if ( Dc_help )  {
1727                 dc_printf( "Usage: model_darkening float\n" );
1728                 Dc_status = 0;  // don't print status if help is printed.  Too messy.
1729         }
1730
1731         if ( Dc_status )        {
1732                 dc_printf( "model_darkening = %.1f\n", Interp_depth_scale );
1733         }
1734 }
1735
1736                 // Compare it to 999.75f at R = 64.0f
1737                 //               0.0000f at R = 4.0f
1738                 
1739                 //float cmp_val = 999.75f;              // old
1740
1741 // Return a number from 'min' to 'max' where it is
1742 // 'v' is between v1 and v2.
1743 float scale_it( float min, float max, float v, float v1, float v2 )
1744 {
1745         if ( v < v1 ) return min;
1746         if ( v > v2 ) return max;
1747
1748         v = (v - v1)/(v2-v1);
1749         v = v*(max-min)+min;
1750
1751         return v;
1752 }
1753
1754 void model_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int lighting_skip )
1755 {
1756         polymodel *pm = model_get(model_num);
1757
1758         // maybe turn off (hardware) culling
1759         if(flags & MR_NO_CULL){
1760                 gr_set_cull(0);
1761         }
1762
1763         Interp_objnum = objnum;
1764
1765         if ( flags & MR_NO_LIGHTING )   {
1766                 Interp_light = 1.0f;
1767
1768                 // never use saved lighitng for this object
1769                 model_set_saved_lighting(-1, -1);
1770         } else if ( flags & MR_IS_ASTEROID ) {
1771                 // Dim it based on distance
1772                 float depth = vm_vec_dist_quick( pos, &Eye_position );
1773                 if ( depth > Interp_depth_scale )       {
1774                         Interp_light = Interp_depth_scale/depth;
1775                         // If it is too far, exit
1776                         if ( Interp_light < (1.0f/32.0f) ) {
1777                                 Interp_light = 0.0f;
1778                                 return;
1779                         } else if ( Interp_light > 1.0f )       {
1780                                 Interp_light = 1.0f;
1781                         }
1782                 } else {
1783                         Interp_light = 1.0f;
1784                 }
1785
1786                 // never use saved lighitng for this object
1787                 model_set_saved_lighting(-1, -1);
1788         } else {
1789                 Interp_light = 1.0f;
1790
1791                 // maybe use saved lighting
1792                 model_set_saved_lighting(objnum, hack_skip_max);
1793         }
1794
1795         if ( !(flags & MR_NO_LIGHTING ) )       {
1796                 light_filter_push( objnum, pos, pm->rad );
1797         }
1798
1799         model_really_render(model_num, orient, pos, flags, objnum);
1800         
1801         if ( !(flags & MR_NO_LIGHTING ) )       {
1802                 light_filter_pop();
1803         }
1804
1805         // maybe turn culling back on
1806         if(flags & MR_NO_CULL){
1807                 gr_set_cull(1);
1808         }
1809
1810         // turn off fog after each model renders
1811         if(The_mission.flags & MISSION_FLAG_FULLNEB){
1812                 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
1813         }
1814 }
1815
1816 // tmp_detail_level
1817 // 0 - Max
1818 // 1
1819 // 2
1820 // 3
1821 // 4 - None
1822
1823 #if MAX_DETAIL_LEVEL != 4
1824 #error MAX_DETAIL_LEVEL is assumed to be 4 in ModelInterp.cpp
1825 #endif
1826
1827 // Find the distance from p0 to the closest point on a box.
1828 // The box's dimensions from 'min' to 'max'.
1829 float interp_closest_dist_to_box( vector *hitpt, vector *p0, vector *min, vector *max )
1830 {
1831         float *origin = (float *)&p0->xyz.x;
1832         float *minB = (float *)min;
1833         float *maxB = (float *)max;
1834         float *coord = (float *)&hitpt->xyz.x;
1835         int inside = 1;
1836         int i;
1837
1838         for (i=0; i<3; i++ )    {
1839                 if ( origin[i] < minB[i] )      {
1840                         coord[i] = minB[i];
1841                         inside = 0;
1842                 } else if (origin[i] > maxB[i] )        {
1843                         coord[i] = maxB[i];
1844                         inside = 0;
1845                 } else {
1846                         coord[i] = origin[i];
1847                 }
1848         }
1849         
1850         if ( inside )   {
1851                 return 0.0f;
1852         }
1853
1854         return vm_vec_dist(hitpt,p0);
1855 }
1856
1857
1858 // Finds the closest point on a model to a point in space.  Actually only finds a point
1859 // on the bounding box of the model.    
1860 // Given:
1861 //   model_num      Which model
1862 //   orient         Orientation of the model
1863 //   pos            Position of the model
1864 //   eye_pos        Point that you want to find the closest point to
1865 // Returns:
1866 //   distance from eye_pos to closest_point.  0 means eye_pos is 
1867 //   on or inside the bounding box.
1868 //   Also fills in outpnt with the actual closest point.
1869 float model_find_closest_point( vector *outpnt, int model_num, int submodel_num, matrix *orient, vector * pos, vector *eye_pos )
1870 {
1871         vector closest_pos, tempv, eye_rel_pos;
1872         
1873         polymodel *pm = model_get(model_num);
1874
1875         if ( submodel_num < 0 ) {
1876                  submodel_num = pm->detail[0];
1877         }
1878
1879         // Rotate eye pos into object coordinates
1880         vm_vec_sub(&tempv,pos,eye_pos );
1881         vm_vec_rotate(&eye_rel_pos,&tempv,orient );
1882
1883         return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max );
1884 }
1885
1886 int tiling = 1;
1887 DCF(tiling, "")
1888 {
1889         tiling = !tiling;
1890         if(tiling){
1891                 dc_printf("Tiled textures\n");
1892         } else {
1893                 dc_printf("Non-tiled textures\n");
1894         }
1895 }
1896
1897 void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id )
1898 {
1899         int i, detail_level;
1900         polymodel * pm;
1901         uint save_gr_zbuffering_mode;
1902         int zbuf_mode;
1903
1904         MONITOR_INC( NumModelsRend, 1 );        
1905
1906         Interp_orient = orient;
1907         Interp_pos = pos;
1908
1909         int tmp_detail_level = Game_detail_level;
1910         
1911 //      if ( D3D_enabled )      {
1912 //              tmp_detail_level = -1;          // Force no hires models for Direct3D
1913 //      }
1914
1915         //      Tmap_show_layers = 1;
1916 //      model_set_detail_level(0);
1917 //      flags |= MR_LOCK_DETAIL|MR_NO_TEXTURING|MR_NO_LIGHTING;         //MR_LOCK_DETAIL |      |MR_NO_LIGHTING|MR_NO_SMOOTHINGMR_NO_TEXTURING | 
1918
1919         // Turn off engine effect
1920         Interp_thrust_scale_subobj=0;
1921
1922         if (!Model_texturing)
1923                 flags |= MR_NO_TEXTURING;
1924
1925         if ( !Model_polys )     {
1926                 flags |= MR_NO_POLYS;
1927         }
1928
1929         Interp_flags = flags;
1930
1931         pm = model_get(model_num);      
1932
1933         // Set the flags we will pass to the tmapper
1934         Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
1935
1936         // if we're in nebula mode
1937         if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
1938                 Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
1939         }
1940
1941         if ( !(Interp_flags & MR_NO_TEXTURING) )        {
1942                 Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
1943
1944                 if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
1945                         Interp_tmap_flags |= TMAP_FLAG_TILED;
1946
1947                 if ( !(Interp_flags & MR_NO_CORRECT) )  {
1948                         Interp_tmap_flags |= TMAP_FLAG_CORRECT;
1949                 }
1950         }
1951
1952         save_gr_zbuffering_mode = gr_zbuffer_get();
1953         zbuf_mode = gr_zbuffer_get();
1954
1955         if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) {
1956                 gr_set_color(0,128,0);
1957                 g3_draw_sphere_ez( pos, pm->rad );
1958                 return;
1959         }
1960
1961         g3_start_instance_matrix(pos,orient);   
1962
1963         if ( Interp_flags & MR_SHOW_RADIUS )    {
1964                 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
1965                         gr_set_color(0,64,0);
1966                         g3_draw_sphere_ez(&vmd_zero_vector,pm->rad);
1967                 }
1968         }
1969
1970         SDL_assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS );
1971
1972         vector closest_pos;
1973         float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
1974         if ( pm->n_detail_levels > 1 )  {
1975
1976                 if ( Interp_flags & MR_LOCK_DETAIL )    {
1977                         i = Interp_detail_level+1;
1978                 } else {
1979
1980                         //gr_set_color(0,128,0);
1981                         //g3_draw_sphere_ez( &closest_pos, 2.0f );
1982
1983                         #if MAX_DETAIL_LEVEL != 4
1984                         #error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4
1985                         #endif
1986
1987                         switch( Detail.detail_distance )        {
1988                         case 0:         // lowest
1989                                 depth *= 8.0f;
1990                                 break;
1991                         case 1:         // lower than normal
1992                                 depth *= 4.0f; 
1993                                 break;
1994                         case 2:         // default  (leave the same)
1995                                 break;
1996                         case 3:         // above normal
1997                                 depth /= 4.0f;
1998                                 break;
1999                         case 4:         // even more normal
2000                                 depth /= 8.0f;
2001                                 break;
2002                         }
2003
2004                         // nebula ?
2005                         if(The_mission.flags & MISSION_FLAG_FULLNEB){
2006                                 depth *= neb2_get_lod_scale(Interp_objnum);
2007                         }
2008
2009                         for ( i=0; i<pm->n_detail_levels; i++ ) {
2010                                 if ( depth<=pm->detail_depth[i] ){
2011                                         break;
2012                                 }
2013                         }
2014
2015                         // If no valid detail depths specified, use highest.
2016                         if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f)) {
2017                                 i = 1;
2018                         }
2019                 }
2020
2021
2022                 // maybe force lower detail
2023                 if (Interp_flags & MR_FORCE_LOWER_DETAIL) {
2024                         i++;
2025                 }
2026
2027                 //detail_level = fl2i(depth/10.0f);             
2028                 //detail_level = 0;
2029                 detail_level = i-1-tmp_detail_level;
2030
2031                 if ( detail_level < 0 ) 
2032                         detail_level = 0;
2033                 else if (detail_level >= pm->n_detail_levels ) 
2034                         detail_level = pm->n_detail_levels-1;
2035
2036                 //mprintf(( "Depth = %.2f, detail = %d\n", depth, detail_level ));
2037
2038         } else {
2039                 detail_level = 0;
2040         }
2041
2042 #ifndef NDEBUG
2043         if ( detail_level==0 )  {
2044                 MONITOR_INC( NumHiModelsRend, 1 );      
2045         } else if ( detail_level ==pm->n_detail_levels-1 )      {
2046                 MONITOR_INC( NumLowModelsRend, 1 );     
2047         }  else {
2048                 MONITOR_INC( NumMedModelsRend, 1 );     
2049         }
2050 #endif  
2051
2052         if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){
2053                 vector auto_back = pm->autocenter;                              
2054                 vm_vec_scale(&auto_back, -1.0f);                
2055                 g3_start_instance_matrix(&auto_back, NULL);             
2056         }       
2057
2058         gr_zbias(1);
2059
2060         // Draw the subobjects  
2061         i = pm->submodel[pm->detail[detail_level]].first_child;
2062
2063         while( i>-1 )   {
2064                 if (!pm->submodel[i].is_thruster )      {
2065                         zbuf_mode = GR_ZBUFF_WRITE;
2066
2067                         // no zbuffering
2068                         if(Interp_flags & MR_NO_ZBUFFER){
2069                                 zbuf_mode = GR_ZBUFF_NONE;
2070                         }
2071
2072                         gr_zbuffer_set(zbuf_mode);
2073
2074                         model_interp_subcall( pm, i, detail_level );
2075                 } 
2076                 i = pm->submodel[i].next_sibling;
2077         }       
2078
2079         // rotate lights for the hull
2080         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
2081                 light_rotate_all();
2082         }       
2083
2084         if ( pm->submodel[pm->detail[detail_level]].num_children > 0 ){
2085                 // zbuf_mode |= GR_ZBUFF_WRITE;         // write only
2086                 zbuf_mode = GR_ZBUFF_FULL;
2087         }
2088
2089         // no zbuffering
2090         if(Interp_flags & MR_NO_ZBUFFER){
2091                 zbuf_mode = GR_ZBUFF_NONE;
2092         }
2093         
2094         gr_zbuffer_set(zbuf_mode);
2095
2096         gr_zbias(0);
2097
2098         // draw the hull of the ship
2099         model_interp_sub( (ubyte *)pm->submodel[pm->detail[detail_level]].bsp_data, pm, &pm->submodel[pm->detail[detail_level]], 0 );
2100
2101         if (Interp_flags & MR_SHOW_PIVOTS )     {
2102                 model_draw_debug_points( pm, NULL );
2103                 model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]] );
2104
2105                 if(pm->flags & PM_FLAG_AUTOCEN){
2106                         gr_set_color(255, 255, 255);
2107                         g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
2108                 }
2109         }
2110
2111         if ( pm->submodel[pm->detail[0]].num_arcs )     {
2112                 interp_render_lightning( pm, &pm->submodel[pm->detail[0]]);
2113         }       
2114
2115         if ( Interp_flags & MR_SHOW_SHIELDS )   {
2116                 model_render_shields(pm);
2117         }       
2118                         
2119         // render model insignias
2120         gr_zbias(1);
2121         
2122         gr_zbuffer_set(GR_ZBUFF_READ);
2123         model_render_insignias(pm, detail_level);       
2124
2125         // zbias back to 0      
2126         gr_zbias(0);
2127
2128         // Draw the thruster glow
2129 #ifndef MAKE_FS1
2130         if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) /*&& (Detail.engine_glows)*/ )     {
2131 #else
2132         if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) && (Detail.engine_glows) ) {
2133 #endif
2134
2135                 for (i = 0; i < pm->n_thrusters; i++ ) {
2136                         thruster_bank *bank = &pm->thrusters[i];
2137                         int j;
2138
2139                         for ( j=0; j<bank->num_slots; j++ )     {
2140                                 float d;
2141                                 vector tempv;
2142                                 vm_vec_sub(&tempv,&View_position,&bank->pnt[j]);
2143                                 vm_vec_normalize(&tempv);
2144
2145                                 d = vm_vec_dot(&tempv,&bank->norm[j]);
2146
2147                                 if ( d > 0.0f)  {
2148                                         vertex p;                                       
2149
2150                                         // Make glow bitmap fade in/out quicker from sides.
2151                                         d *= 3.0f;
2152                                         if ( d > 1.0f ) d = 1.0f;
2153
2154                                         // fade them in the nebula as well
2155                                         if(The_mission.flags & MISSION_FLAG_FULLNEB){
2156                                                 d *= (1.0f - Interp_fog_level);
2157                                         }
2158
2159                                         //ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
2160                                         #define NOISE_SCALE 0.5f
2161                                         #define MIN_SCALE 3.4f
2162                                         #define MAX_SCALE 4.7f
2163                                         float scale = MIN_SCALE;
2164
2165                                         scale = (Interp_thrust_scale-0.1f)*(MAX_SCALE-MIN_SCALE)+MIN_SCALE;
2166
2167                                         float w = bank->radius[j]*(scale+Interp_thrust_glow_noise*NOISE_SCALE );
2168
2169                                         // disable fogging
2170                                         gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
2171
2172                                         g3_rotate_vertex( &p, &bank->pnt[j] );
2173                                         gr_set_bitmap( Interp_thrust_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, d, -1, -1);
2174                                         {
2175 #ifndef PLAT_UNIX
2176                                                 extern int Gr_scaler_zbuffering;
2177                                                 Gr_scaler_zbuffering = 1;
2178                                                 g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED );
2179                                                 //g3_draw_rotated_bitmap(&p,0.0f,w,w, TMAP_FLAG_TEXTURED );
2180                                                 Gr_scaler_zbuffering = 0;
2181 #else
2182                                                 g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED );
2183 #endif
2184                                         }
2185                                 }
2186
2187                         }
2188                 }
2189         }
2190
2191         Interp_thrust_glow_bitmap = -1; 
2192
2193         gr_set_cull(0); 
2194
2195         // Draw the thruster subobjects 
2196         i = pm->submodel[pm->detail[detail_level]].first_child;
2197         while( i>-1 )   {
2198                 if (pm->submodel[i].is_thruster )       {
2199                         zbuf_mode = GR_ZBUFF_READ;
2200
2201                         // no zbuffering
2202                         if(Interp_flags & MR_NO_ZBUFFER){
2203                                 zbuf_mode = GR_ZBUFF_NONE;
2204                         }
2205
2206                         gr_zbuffer_set(zbuf_mode);
2207
2208                         model_interp_subcall( pm, i, detail_level );
2209                 }
2210                 i = pm->submodel[i].next_sibling;
2211         }       
2212
2213         gr_set_cull(1); 
2214
2215         if ( Interp_flags & MR_SHOW_PATHS ){
2216                 model_draw_paths(model_num);
2217         }
2218
2219         if (Interp_flags & MR_BAY_PATHS ){
2220                 model_draw_bay_paths(model_num);
2221         }
2222
2223         if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){
2224                 g3_done_instance();
2225         }
2226
2227         g3_done_instance();
2228         gr_zbuffer_set(save_gr_zbuffering_mode);        
2229 }
2230
2231
2232 void submodel_render(int model_num, int submodel_num, matrix *orient, vector * pos, uint flags, int light_ignore_id)
2233 {
2234         polymodel * pm;
2235
2236         MONITOR_INC( NumModelsRend, 1 );        
2237
2238         if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return;
2239
2240         // Turn off engine effect
2241         Interp_thrust_scale_subobj=0;
2242
2243         if (!Model_texturing)
2244                 flags |= MR_NO_TEXTURING;
2245
2246         Interp_flags = flags;
2247
2248         pm = model_get(model_num);
2249
2250         // Set the flags we will pass to the tmapper
2251         Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
2252
2253         // if we're in nebula mode
2254         if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
2255                 Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
2256         }
2257
2258         if ( !(Interp_flags & MR_NO_TEXTURING) )        {
2259                 Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
2260
2261                 if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling )
2262                         Interp_tmap_flags |= TMAP_FLAG_TILED;
2263
2264                 if ( !(Interp_flags & MR_NO_CORRECT) )  {
2265                         Interp_tmap_flags |= TMAP_FLAG_CORRECT;
2266                 }
2267         }
2268
2269         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
2270                 light_filter_push( -1, pos, pm->submodel[submodel_num].rad );
2271         }
2272
2273         g3_start_instance_matrix(pos,orient);
2274
2275         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
2276                 light_rotate_all();
2277         }
2278
2279         model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 );
2280         if ( pm->submodel[submodel_num].num_arcs )      {
2281                 interp_render_lightning( pm, &pm->submodel[submodel_num]);
2282         }
2283
2284         if (Interp_flags & MR_SHOW_PIVOTS )
2285                 model_draw_debug_points( pm, &pm->submodel[submodel_num] );
2286
2287         if ( !(Interp_flags & MR_NO_LIGHTING ) )        {
2288                 light_filter_pop();     
2289         }
2290         g3_done_instance();
2291
2292 }
2293
2294
2295
2296 // Fills in an array with points from a model.
2297 // Only gets up to max_num verts;
2298 // Returns number of verts found;
2299 static int submodel_get_points_internal(int model_num, int submodel_num, int max_num, vector **pnts, vector **norms )
2300 {
2301         polymodel * pm;
2302
2303         pm = model_get(model_num);
2304
2305         if ( submodel_num < 0 ) {
2306                 submodel_num = pm->detail[0];
2307         }
2308
2309         ubyte *p = pm->submodel[submodel_num].bsp_data;
2310         int chunk_type, chunk_size;
2311
2312         chunk_type = w(p);
2313         chunk_size = w(p+4);
2314
2315         while (chunk_type != OP_EOF)    {
2316                 switch (chunk_type) {
2317                 case OP_EOF: return 1;
2318                 case OP_DEFPOINTS:      {
2319                                 int n;
2320                                 int nverts = w(p+8);                            
2321                                 int offset = w(p+16);                           
2322
2323                                 ubyte * normcount = p+20;
2324                                 vector *src = vp(p+offset);
2325
2326                                 if ( nverts > max_num )
2327                                         nverts = max_num; 
2328
2329                                 for (n=0; n<nverts; n++ )       {
2330                                         *pnts++ = src;
2331                                         *norms++ = src + 1;             // first normal associated with the point
2332
2333                                         src += normcount[n]+1;
2334                                 } 
2335                                 return nverts;          // Read in 'n' points
2336                         }
2337                         break;
2338                 case OP_FLATPOLY:               break;
2339                 case OP_TMAPPOLY:               break;
2340                 case OP_SORTNORM:               break;
2341                 case OP_BOUNDBOX:               break;
2342                 default:
2343                         mprintf(( "Bad chunk type %d, len=%d in submodel_get_points\n", chunk_type, chunk_size ));
2344                         Int3();         // Bad chunk type!
2345                         return 0;
2346                 }
2347                 p += chunk_size;
2348                 chunk_type = w(p);
2349                 chunk_size = w(p+4);
2350         }
2351         return 0;               // Couldn't find 'em
2352 }
2353
2354 // Gets two random points on a model
2355 void submodel_get_two_random_points(int model_num, int submodel_num, vector *v1, vector *v2, vector *n1, vector *n2 )
2356 {
2357         int nv = submodel_get_points_internal(model_num, submodel_num, MAX_POLYGON_VECS, Interp_verts, Interp_norms );
2358         
2359         int vn1 = (myrand()>>5) % nv;
2360         int vn2 = (myrand()>>5) % nv;
2361
2362         *v1 = *Interp_verts[vn1];
2363         *v2 = *Interp_verts[vn2];
2364
2365         if(n1 != NULL){
2366                 *n1 = *Interp_norms[vn1];
2367         }
2368         if(n2 != NULL){
2369                 *n2 = *Interp_norms[vn2];
2370         }
2371 }
2372
2373 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
2374 // This defaults to black.
2375 void model_set_outline_color(int r, int g, int b )
2376 {
2377         gr_init_color( &Interp_outline_color, r, g, b );
2378
2379 }
2380
2381 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
2382 // This defaults to black.
2383 void model_set_outline_color_fast(void *outline_color)
2384 {
2385         Interp_outline_color = *((color*)(outline_color));
2386 }
2387
2388 // IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n'
2389 // This defaults to 0. (0=highest, larger=lower)
2390 void model_set_detail_level(int n)
2391 {
2392         Interp_detail_level = n;
2393 }
2394
2395
2396 // Returns number of verts in a submodel;
2397 int submodel_get_num_verts(int model_num, int submodel_num )
2398 {
2399         polymodel * pm;
2400
2401         pm = model_get(model_num);
2402
2403         ubyte *p = pm->submodel[submodel_num].bsp_data;
2404         int chunk_type, chunk_size;
2405
2406         chunk_type = w(p);
2407         chunk_size = w(p+4);
2408
2409         while (chunk_type != OP_EOF)    {
2410                 switch (chunk_type) {
2411                 case OP_EOF: return 0;
2412                 case OP_DEFPOINTS:      {
2413                                 int n=w(p+8);
2414                                 return n;               // Read in 'n' points
2415                         }
2416                         break;
2417                 case OP_FLATPOLY:               break;
2418                 case OP_TMAPPOLY:               break;
2419                 case OP_SORTNORM:               break;
2420                 case OP_BOUNDBOX:       break;
2421                 default:
2422                         mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size ));
2423                         Int3();         // Bad chunk type!
2424                         return 0;
2425                 }
2426                 p += chunk_size;
2427                 chunk_type = w(p);
2428                 chunk_size = w(p+4);
2429         }
2430         return 0;               // Couldn't find 'em
2431 }
2432
2433 // Returns number of tmaps & flat polys in a submodel;
2434 int submodel_get_num_polys_sub( ubyte *p )
2435 {
2436         int chunk_type = w(p);
2437         int chunk_size = w(p+4);
2438         int n = 0;
2439         
2440         while (chunk_type != OP_EOF)    {
2441                 switch (chunk_type) {
2442                 case OP_EOF:                    return n;
2443                 case OP_DEFPOINTS:      break;
2444                 case OP_FLATPOLY:               n++; break;
2445                 case OP_TMAPPOLY:               n++; break;
2446                 case OP_SORTNORM:               {
2447                         int frontlist = w(p+36);
2448                         int backlist = w(p+40);
2449                         int prelist = w(p+44);
2450                         int postlist = w(p+48);
2451                         int onlist = w(p+52);
2452                         n += submodel_get_num_polys_sub(p+frontlist);
2453                         n += submodel_get_num_polys_sub(p+backlist);
2454                         n += submodel_get_num_polys_sub(p+prelist);
2455                         n += submodel_get_num_polys_sub(p+postlist );
2456                         n += submodel_get_num_polys_sub(p+onlist );
2457                         }
2458                         break;
2459                 case OP_BOUNDBOX:       break;
2460                 default:
2461                         mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size ));
2462                         Int3();         // Bad chunk type!
2463                         return 0;
2464                 }
2465                 p += chunk_size;
2466                 chunk_type = w(p);
2467                 chunk_size = w(p+4);
2468         }
2469         return n;               
2470 }
2471
2472 // Returns number of tmaps & flat polys in a submodel;
2473 int submodel_get_num_polys(int model_num, int submodel_num )
2474 {
2475         polymodel * pm;
2476
2477         pm = model_get(model_num);
2478
2479         return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data );
2480
2481 }
2482
2483 // Sets the submodel instance data in a submodel
2484 // If show_damaged is true it shows only damaged submodels.
2485 // If it is false it shows only undamaged submodels.
2486 void model_show_damaged(int model_num, int show_damaged )
2487 {
2488         polymodel * pm;
2489         int i;
2490
2491         pm = model_get(model_num);
2492
2493         for (i=0; i<pm->n_models; i++ ) {
2494                 bsp_info *sm = &pm->submodel[i];
2495
2496                 // Set the "blown out" flags    
2497                 sm->blown_off = 0;
2498         }
2499
2500         for (i=0; i<pm->n_models; i++ ) {
2501                 bsp_info *sm = &pm->submodel[i];
2502
2503                 // Set the "blown out" flags    
2504                 if ( show_damaged )     {
2505                         if ( sm->my_replacement > -1 )  {
2506                                 pm->submodel[sm->my_replacement].blown_off = 0;
2507                                 sm->blown_off = 1;
2508                         }
2509                 } else {
2510                         if ( sm->my_replacement > -1 )  {
2511                                 pm->submodel[sm->my_replacement].blown_off = 1;
2512                                 sm->blown_off = 0;
2513                         }
2514                 }
2515         }
2516 }
2517
2518 // set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether)
2519 void model_set_insignia_bitmap(int bmap)
2520 {
2521         Interp_insignia_bitmap = bmap;
2522 }
2523
2524 // set the forces bitmap
2525 void model_set_forced_texture(int bmap)
2526 {
2527         Interp_forced_bitmap = bmap;
2528 }
2529
2530 // set model transparency for use with MR_ALL_XPARENT
2531 void model_set_alpha(float alpha)
2532 {
2533         Interp_xparent_alpha = alpha;
2534 }
2535
2536 // see if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error
2537 int model_find_texture(int model_num, int bitmap)
2538 {
2539         polymodel * pm; 
2540         int idx;
2541
2542         // get a handle to the model
2543         pm = model_get(model_num);
2544         if(pm == NULL){
2545                 return -1;
2546         }
2547
2548         // find the texture
2549         for(idx=0; idx<pm->n_textures; idx++){
2550                 if(pm->textures[idx] == bitmap){
2551                         return 1;
2552                 }
2553         }
2554
2555         // no texture
2556         return 0;
2557 }
2558
2559 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
2560 // returns closest distance to extended box
2561 // positive return value means start_point is outside extended box
2562 // displaces closest point an optional amount delta to the outside of the box
2563 // closest_box_point can be NULL.
2564 float get_model_closest_box_point_with_delta(vector *closest_box_point, vector *start_point, int modelnum, int *is_inside, float delta)
2565 {
2566         int i, idx;
2567         vector box_point, ray_direction, *extremes;
2568         float dist, best_dist;
2569         polymodel *pm;
2570         int inside = 0;
2571         int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
2572         int mask_inside = 0x3f;
2573
2574         best_dist = FLT_MAX;
2575         pm = model_get(modelnum);
2576
2577         for (i=0; i<6; i++) {
2578                 idx = i / 2;    // which row vector of Identity matrix
2579
2580                 memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vector));
2581
2582                 // do negative, then positive plane for each axis
2583                 if (2 * idx == i) {
2584                         extremes = &pm->mins;
2585                         vm_vec_negate(&ray_direction);
2586                 } else {
2587                         extremes = &pm->maxs;
2588                 }
2589
2590                 // a negative distance means started outside the box
2591                 dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f);
2592                 if (dist > 0) {
2593                         inside |= masks[i];
2594                 }
2595                 if (fabs(dist) < fabs(best_dist)) {
2596                         best_dist = dist;
2597                         if (closest_box_point) {
2598                                 vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta);
2599                         }
2600                 }
2601         }
2602
2603         // is start_point inside the box
2604         if (is_inside) {
2605                 *is_inside = (inside == mask_inside);
2606         }
2607
2608         return -best_dist;
2609 }
2610
2611 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
2612 // returns closest distance to extended box
2613 // positive return value means start_point is outside extended box
2614 // displaces closest point an optional amount delta to the outside of the box
2615 // closest_box_point can be NULL.
2616 float get_world_closest_box_point_with_delta(vector *closest_box_point, object *box_obj, vector *start_point, int *is_inside, float delta)
2617 {
2618         vector temp, box_start;
2619         float dist;
2620         int modelnum;
2621
2622         // get modelnum
2623         modelnum = Ships[box_obj->instance].modelnum;
2624
2625         // rotate start_point to box_obj RF
2626         vm_vec_sub(&temp, start_point, &box_obj->pos);
2627         vm_vec_rotate(&box_start, &temp, &box_obj->orient);
2628
2629         dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta);
2630
2631         // rotate closest_box_point to world RF
2632         if (closest_box_point) {
2633                 vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient);
2634                 vm_vec_add(closest_box_point, &temp, &box_obj->pos);
2635         }
2636
2637         return dist;
2638 }
2639
2640 void model_set_fog_level(float l)
2641 {
2642         Interp_fog_level = l;
2643 }
2644
2645 // given a newly loaded model, page in all textures
2646 void model_page_in_textures(int modelnum, int ship_info_index)
2647 {
2648         int idx;
2649         ship_info *sip;
2650
2651         // valid ship type?
2652         if((ship_info_index < 0) || (ship_info_index >= Num_ship_types)){
2653                 return;
2654         }
2655         sip = &Ship_info[ship_info_index];
2656
2657         polymodel *pm = model_get(modelnum);
2658
2659         // bogus
2660         if(pm == NULL){
2661                 return;
2662         }
2663
2664         // set nondarkening pixels      
2665         if(sip->num_nondark_colors){            
2666                 palman_set_nondarkening(sip->nondark_colors, sip->num_nondark_colors);
2667         }
2668         // use the colors from the default table
2669         else {          
2670                 palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default);
2671         }
2672                                                                 
2673         for (idx=0; idx<pm->n_textures; idx++ ){
2674                 int bitmap_num = pm->original_textures[idx];
2675
2676                 if ( bitmap_num > -1 )  {
2677                         bm_lock(bitmap_num, 16, BMP_TEX_OTHER);
2678                         bm_unlock(bitmap_num);
2679                 }
2680         }
2681 }
2682
2683 // is the given model a pirate ship?
2684 int model_is_pirate_ship(int modelnum)
2685 {
2686         return 0;
2687 }