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