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