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