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