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