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