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