2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Model/ModelInterp.cpp $
15 * Rendering models, I think.
18 * Revision 1.8 2004/09/20 01:31:44 theoddone33
21 * Revision 1.7 2004/07/04 11:39:06 taylor
22 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
24 * Revision 1.6 2003/05/25 02:30:43 taylor
27 * Revision 1.5 2002/06/17 06:33:09 relnev
28 * ryan's struct patch for gcc 2.95
30 * Revision 1.4 2002/06/09 04:41:23 relnev
31 * added copyright header
33 * Revision 1.3 2002/06/01 03:32:00 relnev
34 * fix texture loading mistake.
36 * enable some d3d stuff for opengl also
38 * Revision 1.2 2002/05/07 03:16:46 theoddone33
39 * The Great Newline Fix
41 * Revision 1.1.1.1 2002/05/03 03:28:10 root
45 * 37 9/13/99 11:25p Dave
46 * Fixed problem with mode-switching and D3D movies.
48 * 36 9/13/99 10:09a Andsager
49 * Add debug console commands to lower model render detail and fireball
50 * LOD for big ship explosiosns.
52 * 35 9/08/99 12:03a Dave
53 * Make squad logos render properly in D3D all the time. Added intel anim
56 * 34 9/01/99 10:09a Dave
59 * 33 8/30/99 5:01p Dave
60 * Made d3d do less state changing in the nebula. Use new chat server for
63 * 32 8/24/99 8:55p Dave
64 * Make sure nondimming pixels work properly in tech menu.
66 * 31 7/29/99 10:47p Dave
67 * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
69 * 30 7/29/99 12:05a Dave
70 * Nebula speed optimizations.
72 * 29 7/27/99 3:09p Dave
73 * Made g400 work. Whee.
75 * 28 7/24/99 4:19p Dave
76 * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much
77 * better. Made model code use zbuffer more intelligently.
79 * 27 7/19/99 7:20p Dave
80 * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
83 * 26 7/18/99 5:20p Dave
84 * Jump node icon. Fixed debris fogging. Framerate warning stuff.
86 * 25 7/15/99 2:13p Dave
87 * Added 32 bit detection.
89 * 24 6/22/99 7:03p Dave
90 * New detail options screen.
92 * 23 6/18/99 5:16p Dave
93 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
94 * dialog to PXO screen.
96 * 22 5/26/99 11:46a Dave
97 * Added ship-blasting lighting and made the randomization of lighting
98 * much more customizable.
100 * 21 5/24/99 5:45p Dave
101 * Added detail levels to the nebula, with a decent speedup. Split nebula
102 * lightning into its own section.
104 * 20 5/12/99 10:05a Johnson
105 * DKA: Fix fred bug from engine wash
107 * 19 5/08/99 8:25p Dave
108 * Upped object pairs. First run of nebula lightning.
110 * 18 4/26/99 8:49p Dave
111 * Made all pof based nebula stuff full customizable through fred.
113 * 17 4/23/99 5:53p Dave
114 * Started putting in new pof nebula support into Fred.
116 * 16 4/20/99 3:30p Andsager
117 * Let get_model_closest_box_point_with_delta() take NULL as pointer to
120 * 15 4/19/99 12:51p Andsager
121 * Add function to find the nearest point on extneded bounding box and
122 * check if inside bounding box.
124 * 14 3/31/99 8:24p Dave
125 * Beefed up all kinds of stuff, incluging beam weapons, nebula effects
126 * and background nebulae. Added per-ship non-dimming pixel colors.
128 * 13 3/24/99 6:14p Dave
129 * Added position and orientation checksumming for multiplayer. Fixed LOD
130 * rendering bugs for squad insignias
132 * 12 3/23/99 5:17p Dave
133 * Changed model file format somewhat to account for LOD's on insignias
135 * 11 3/19/99 9:51a Dave
136 * Checkin to repair massive source safe crash. Also added support for
137 * pof-style nebulae, and some new weapons code.
139 * 10 3/08/99 7:03p Dave
140 * First run of new object update system. Looks very promising.
142 * 9 3/02/99 9:25p Dave
143 * Added a bunch of model rendering debug code. Started work on fixing
144 * beam weapon wacky firing.
146 * 8 2/19/99 11:42a Dave
147 * Put in model rendering autocentering.
149 * 7 1/14/99 6:06p Dave
150 * 100% full squad logo support for single player and multiplayer.
152 * 6 1/08/99 2:08p Dave
153 * Fixed software rendering for pofview. Super early support for AWACS and
156 * 5 1/06/99 2:24p Dave
157 * Stubs and release build fixes.
159 * 4 12/09/98 7:34p Dave
160 * Cleanup up nebula effect. Tweaked many values.
162 * 3 12/06/98 2:36p Dave
163 * Drastically improved nebula fogging.
165 * 2 10/07/98 10:53a Dave
168 * 1 10/07/98 10:50a Dave
170 * 193 8/28/98 3:29p Dave
171 * EMP effect done. AI effects may need some tweaking as required.
173 * 192 6/13/98 3:18p Hoffoss
174 * NOX()ed out a bunch of strings that shouldn't be translated.
176 * 191 5/24/98 4:53p John
177 * changed rounding for model caching to get rid of some empty lines in
180 * 190 5/16/98 4:47p John
181 * Put back in my version 188 changes that someone so rudely (but
182 * hopefully unintentionally) deleted.
184 * 189 5/13/98 11:34p Mike
185 * Model caching system.
187 * 187 5/12/98 11:32a Mike
188 * Support for weapon pof detail levels.
190 * 186 5/08/98 1:32p John
191 * Added code for using two layered subspace effects.
193 * 185 4/29/98 11:03a John
194 * Added code to show octants.
196 * 184 4/22/98 9:58p John
197 * Added code to view invisible faces.
199 * 183 4/22/98 9:43p John
200 * Added code to allow checking of invisible faces, flagged by any texture
201 * name with invisible in it.
203 * 182 4/20/98 4:44p John
204 * Fixed problems with black being xparent on model cache rneders. Made
205 * model cache key off of detail level setting and framerate.
207 * 181 4/18/98 4:00p John
208 * Made highest model caching detail level disable model caching.
210 * 180 4/14/98 11:11p John
211 * Made ships with < 50% hull left show electrical damage arcs.
213 * 179 4/13/98 4:54p John
214 * Made uv rotate independently on subspace effect. Put in DCF function
215 * for setting subspace speeds.
217 * 178 4/12/98 5:54p John
218 * Made models work with subspace. Made subspace rotate also.
220 * 177 4/12/98 9:56a John
221 * Made lighting detail flags work. Made explosions cast light on
224 * 176 4/11/98 6:53p John
225 * Added first rev of subspace effect.
227 * 175 4/10/98 5:20p John
228 * Changed RGB in lighting structure to be ubytes. Removed old
229 * not-necessary 24 bpp software stuff.
231 * 174 4/09/98 11:40p John
232 * Fixed some bugs with hardware lighting.
234 * 173 4/09/98 4:38p John
235 * Made non-darkening and transparent textures work under Glide. Fixed
236 * bug with Jim's computer not drawing any bitmaps.
249 #include "floating.h"
251 #include "lighting.h"
252 #include "modelsinc.h"
253 #include "fireballs.h"
256 #include "systemvars.h"
258 #include "3dinternal.h"
260 #include "grinternal.h"
262 #include "object.h" // For MAX_OBJECTS
263 #include "missionparse.h"
267 // Some debug variables used externally for displaying stats
269 int modelstats_num_polys = 0;
270 int modelstats_num_polys_drawn = 0;
271 int modelstats_num_verts = 0;
272 int modelstats_num_sortnorms = 0;
273 int modelstats_num_boxes = 0;
277 typedef struct model_light_object {
278 ubyte r[MAX_POLYGON_NORMS];
279 ubyte g[MAX_POLYGON_NORMS];
280 ubyte b[MAX_POLYGON_NORMS];
284 } model_light_object;
286 // -----------------------
290 // Vertices used internally to rotate model points
291 static vertex Interp_points[MAX_POLYGON_VECS];
292 vector *Interp_verts[MAX_POLYGON_VECS];
293 static int Interp_num_verts;
296 // -------------------------------------------------------------------
297 // lighting save stuff
299 #define MAX_MODEL_LIGHTING_SAVE 30
300 int hack_skip_max = 1;
304 hack_skip_max = Dc_arg_int;
306 // model_light_object Interp_lighting_save[MAX_MODEL_LIGHTING_SAVE];
307 model_light_object Interp_lighting_temp;
308 model_light_object *Interp_lighting = &Interp_lighting_temp;
309 int Interp_use_saved_lighting = 0;
310 int Interp_saved_lighting_full = 0;
312 // lighting save stuff
313 // -------------------------------------------------------------------
316 static ubyte Interp_light_applied[MAX_POLYGON_NORMS];
317 static vector *Interp_norms[MAX_POLYGON_NORMS];
318 static int Interp_num_norms = 0;
319 static ubyte *Interp_lights;
321 static float Interp_fog_level = 0.0f;
323 // Stuff to control rendering parameters
324 static color Interp_outline_color;
325 static int Interp_detail_level = 0;
326 static uint Interp_flags = 0;
327 static uint Interp_tmap_flags = 0;
329 // If non-zero, then the subobject gets scaled by Interp_thrust_scale.
330 static int Interp_thrust_scale_subobj=0;
331 static float Interp_thrust_scale = 0.1f;
332 static int Interp_thrust_bitmap = -1;
333 static int Interp_thrust_glow_bitmap = -1;
334 static float Interp_thrust_glow_noise = 1.0f;
336 static int Interp_objnum = -1;
338 // if != -1, use this bitmap when rendering ship insignias
339 static int Interp_insignia_bitmap = -1;
341 // if != -1, use this bitmap when rendering with a forced texture
342 static int Interp_forced_bitmap = -1;
344 // for rendering with the MR_ALL_XPARENT FLAG SET
345 static float Interp_xparent_alpha = 1.0f;
347 float Interp_light = 0.0f;
349 // forward references
350 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check );
352 // call at the beginning of a level. after the level has been loaded
353 void model_level_post_init()
358 // reset lighting stuff
359 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
360 Interp_lighting_save[idx].objnum = -1;
361 Interp_lighting_save[idx].skip = 0;
364 // saved lighting is not full
365 Interp_saved_lighting_full = 0;
369 // call to select an object for using "saved" lighting
370 void model_set_saved_lighting(int objnum, int skip_max)
375 // always set to not using saved light to start with
376 Interp_use_saved_lighting = 0;
377 Interp_lighting = &Interp_lighting_temp;
379 // if he passed a -1 for either value, no saved lighting
380 if((objnum == -1) || (skip_max == -1)){
384 // see if the object is already on the list
385 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
386 // ahha, he is on the list
387 if(Interp_lighting_save[idx].objnum == objnum){
388 // if he's entered a new skip max
389 if(Interp_lighting_save[idx].skip_max != skip_max){
390 Interp_lighting_save[idx].skip = 0;
391 Interp_lighting_save[idx].skip_max = skip_max;
392 Interp_use_saved_lighting = 0;
393 Interp_lighting = &Interp_lighting_save[idx];
395 // if we're reached the "skip" frame, re-render lighting for this guy
396 else if(Interp_lighting_save[idx].skip == Interp_lighting_save[idx].skip_max){
397 Interp_lighting_save[idx].skip = 0;
398 Interp_use_saved_lighting = 0;
399 Interp_lighting = &Interp_lighting_save[idx];
401 // otherwise, use his saved lighting values
403 Interp_lighting_save[idx].skip++;
404 Interp_use_saved_lighting = 1;
405 Interp_lighting = &Interp_lighting_save[idx];
413 // no free saved lighting slots
414 if(Interp_saved_lighting_full){
420 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
422 if(Interp_lighting_save[idx].objnum == -1){
423 Interp_lighting_save[idx].objnum = objnum;
424 Interp_lighting_save[idx].skip = 0;
425 Interp_lighting_save[idx].skip_max = skip_max;
427 Interp_use_saved_lighting = 0;
428 Interp_lighting = &Interp_lighting_save[idx];
435 // oops. out of free slots
437 Interp_saved_lighting_full = 1;
442 // notify the model system that a ship has died
443 void model_notify_dead_ship(int objnum)
448 // see if this guy was on the lighting list
449 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
451 if(objnum == Interp_lighting_save[idx].objnum){
452 Interp_lighting_save[idx].objnum = -1;
453 Interp_saved_lighting_full = 0;
460 void interp_clear_instance()
462 Interp_thrust_scale = 0.1f;
463 Interp_thrust_bitmap = -1;
464 Interp_thrust_glow_bitmap = -1;
465 Interp_thrust_glow_noise = 1.0f;
466 Interp_insignia_bitmap = -1;
469 // Scales the engines thrusters by this much
470 void model_set_thrust( int model_num, float length, int bitmap, int glow_bitmap, float glow_noise )
472 Interp_thrust_scale = length;
473 Interp_thrust_bitmap = bitmap;
474 Interp_thrust_glow_bitmap = glow_bitmap;
475 Interp_thrust_glow_noise = glow_noise;
477 if ( Interp_thrust_scale < 0.1f ) {
478 Interp_thrust_scale = 0.1f;
479 } else if ( Interp_thrust_scale > 1.0f ) {
480 Interp_thrust_scale = 1.0f;
483 polymodel * pm = model_get( model_num );
486 // If thrust is set up, use it.
487 for (i=0; i<pm->num_lights; i++ ) {
488 if ( pm->lights[i].type == BSP_LIGHT_TYPE_THRUSTER ) {
489 float scale = (Interp_thrust_scale-0.1f)*0.5f;
491 pm->lights[i].value += (scale+Interp_thrust_glow_noise*0.2f) / 255.0f;
502 // +16 int offset from start of chunk to vertex data
503 // +20 n_verts*char norm_counts
504 // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
505 void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm)
509 int offset = w(p+16);
512 ubyte * normcount = p+20;
513 vertex *dest = Interp_points;
514 vector *src = vp(p+offset);
516 // Get pointer to lights
517 Interp_lights = p+20+nverts;
519 SDL_assert( nverts < MAX_POLYGON_VECS );
520 // SDL_assert( nnorms < MAX_POLYGON_NORMS );
522 Interp_num_verts = nverts;
524 modelstats_num_verts += nverts;
528 static int Max_vecs = 0;
529 static int Max_norms = 0;
531 if ( Max_vecs < nverts ) {
533 mprintf(( "MAX NORMS = %d\n", Max_norms ));
534 mprintf(( "MAX VECS = %d\n", Max_vecs ));
537 if ( Max_norms < nnorms ) {
539 mprintf(( "MAX NORMS = %d\n", Max_norms ));
540 mprintf(( "MAX VECS = %d\n", Max_vecs ));
544 if (Interp_thrust_scale_subobj) {
546 // Only scale vertices that aren't on the "base" of
547 // the effect. Base is something Adam decided to be
548 // anything under 1.5 meters, hence the 1.5f.
549 float min_thruster_dist = -1.5f;
551 if ( Interp_flags & MR_IS_MISSILE ) {
552 min_thruster_dist = 0.5f;
555 for (n=0; n<nverts; n++ ) {
558 Interp_verts[n] = src;
560 // Only scale vertices that aren't on the "base" of
561 // the effect. Base is something Adam decided to be
562 // anything under 1.5 meters, hence the 1.5f.
563 if ( src->xyz.z < min_thruster_dist ) {
564 tmp.xyz.x = src->xyz.x * 1.0f;
565 tmp.xyz.y = src->xyz.y * 1.0f;
566 tmp.xyz.z = src->xyz.z * Interp_thrust_scale;
571 g3_rotate_vertex(dest,&tmp);
573 src++; // move to normal
575 for (i=0; i<normcount[n]; i++ ) {
576 Interp_light_applied[next_norm] = 0;
577 Interp_norms[next_norm] = src;
586 for (n=0; n<nverts; n++ ) {
587 Interp_verts[n] = src;
592 if(Interp_thrust_twist > 0.0f){
596 // determine theta for this vertex
597 theta = fl_radian(20.0f + Interp_thrust_twist2);
602 tmp.xyz.z = (src->xyz.z * ct) - (src->xyz.y * st);
603 tmp.xyz.y = (src->xyz.z * st) + (src->xyz.y * ct);
606 tmp.xyz.z += Interp_thrust_twist;
609 g3_rotate_vertex(dest, &tmp);
612 g3_rotate_vertex(dest, src);
614 src++; // move to normal
616 for (i=0; i<normcount[n]; i++ ) {
617 Interp_light_applied[next_norm] = 0;
618 Interp_norms[next_norm] = src;
627 Interp_num_norms = next_norm;
631 matrix *Interp_orient;
635 void interp_compute_environment_mapping( vector *nrm, vertex * pnt, vector *vert)
639 matrix * m = &View_matrix;
641 vm_vec_rotate( &R, nrm, &View_matrix );
642 vm_vec_normalize(&R);
645 R.xyz.x = a * R.xyz.x; // reflected R = 2N(N.E) -E; E = eye
646 R.xyz.y = a * R.xyz.y;
647 R.xyz.z = a * R.xyz.z;
648 vm_vec_normalize(&R);
649 a = (float)fl_sqrt( 1.0f - R.xyz.y * R.xyz.y);
650 pnt->u = (float)atan2( R.xyz.x, -R.xyz.z) / (2.0f * 3.14159f);
651 if (pnt->u < 0.0) pnt->u += 1.0f;
652 pnt->v = 1.0f - (float)atan2( a, R.xyz.y) / 3.14159f;
668 // +44 nverts*short*short vertlist, smoothlist
669 void model_interp_flatpoly(ubyte * p,polymodel * pm)
671 vertex *Interp_list[TMAP_MAX_VERTS];
674 if ( nv < 0 ) return;
677 modelstats_num_polys++;
680 if (!g3_check_normal_facing(vp(p+20),vp(p+8)) ) return;
684 short * verts = (short *)(p+44);
687 Interp_list[i] = &Interp_points[verts[i*2]];
689 if ( Interp_flags & MR_NO_LIGHTING ) {
690 Interp_list[i]->r = 191;
691 Interp_list[i]->g = 191;
692 Interp_list[i]->b = 191;
694 int vertnum = verts[i*2+0];
695 int norm = verts[i*2+1];
697 if ( Interp_flags & MR_NO_SMOOTHING ) {
698 light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
700 // if we're not using saved lighting
701 if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
702 light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], vp(p+8), Interp_light );
703 Interp_light_applied[norm] = 1;
706 Interp_list[i]->r = Interp_lighting->r[norm];
707 Interp_list[i]->g = Interp_lighting->g[norm];
708 Interp_list[i]->b = Interp_lighting->b[norm];
713 // HACK!!! FIX ME!!! I'M SLOW!!!!
714 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
715 gr_set_color( *(p+40), *(p+41), *(p+42) );
718 if ( !(Interp_flags & MR_NO_POLYS)) {
719 g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB );
722 if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET)) {
725 if ( Interp_flags & MR_SHOW_OUTLINE ) {
726 gr_set_color_fast( &Interp_outline_color );
729 for (i=0; i<nv; i++ ) {
731 g3_draw_line(Interp_list[i], Interp_list[j] );
744 // +44 nverts*(model_tmap_vert) vertlist (n,u,v)
745 extern int Tmap_show_layers;
747 int Interp_subspace = 0;
748 float Interp_subspace_offset_u = 0.0f;
749 float Interp_subspace_offset_v = 0.0f;
750 ubyte Interp_subspace_r = 255;
751 ubyte Interp_subspace_g = 255;
752 ubyte Interp_subspace_b = 255;
754 void model_interp_tmappoly(ubyte * p,polymodel * pm)
756 vertex *Interp_list[TMAP_MAX_VERTS];
759 model_tmap_vert *verts;
761 int is_invisible = 0;
763 if ((!Interp_thrust_scale_subobj) && (pm->textures[w(p+40)]<0)) {
764 // Don't draw invisible polygons.
765 if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES)) {
774 // Tmap_show_layers = 1;
777 modelstats_num_polys++;
780 if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)) return;
782 if ( nv < 0 ) return;
784 verts = (model_tmap_vert *)(p+44);
787 Interp_list[i] = &Interp_points[verts[i].vertnum];
789 Interp_list[i]->u = verts[i].u;
790 Interp_list[i]->v = verts[i].v;
792 if ( Interp_subspace ) {
793 Interp_list[i]->v += Interp_subspace_offset_u;
794 Interp_list[i]->u += Interp_subspace_offset_v;
795 Interp_list[i]->r = Interp_subspace_r;
796 Interp_list[i]->g = Interp_subspace_g;
797 Interp_list[i]->b = Interp_subspace_b;
800 // if ( !(pm->flags & PM_FLAG_ALLOW_TILING) ) {
801 // SDL_assert(verts[i].u <= 1.0f );
802 // SDL_assert(verts[i].v <= 1.0f );
805 // SDL_assert( verts[i].normnum == verts[i].vertnum );
807 if ( Interp_flags & MR_NO_LIGHTING ) {
808 Interp_list[i]->r = 191;
809 Interp_list[i]->g = 191;
810 Interp_list[i]->b = 191;
812 int vertnum = verts[i].vertnum;
813 int norm = verts[i].normnum;
815 if ( Interp_flags & MR_NO_SMOOTHING ) {
816 light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
818 // if we're applying lighting as normal, and not using saved lighting
819 if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
820 light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], Interp_norms[norm], Interp_light );
822 Interp_light_applied[norm] = 1;
825 Interp_list[i]->r = Interp_lighting->r[norm];
826 Interp_list[i]->g = Interp_lighting->g[norm];
827 Interp_list[i]->b = Interp_lighting->b[norm];
832 // SDL_assert(verts[i].u >= 0.0f );
833 // SDL_assert(verts[i].v >= 0.0f );
837 modelstats_num_polys_drawn++;
840 if (!(Interp_flags & MR_NO_POLYS) ) {
841 if ( is_invisible ) {
842 gr_set_color( 0, 255, 0 );
843 g3_draw_poly( nv, Interp_list, 0 );
844 } else if (Interp_thrust_scale_subobj) {
845 if ( (Interp_thrust_bitmap > -1) && (Interp_thrust_scale > 0.0f) ) {
846 gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f, -1, -1);
847 g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );
848 } else if ( !Pofview_running ) {
849 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
850 gr_set_color( 128, 128, 255 );
852 uint tflags = Interp_tmap_flags;
853 tflags &= (~(TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT));
854 g3_draw_poly( nv, Interp_list, tflags );
857 // all textured polys go through here
858 if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED ) {
859 // subspace special case
860 if (Interp_subspace) {
861 gr_set_bitmap( pm->textures[w(p+40)], GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f, -1, -1);
863 // all other textures
867 // if we're rendering a nebula background pof, maybe select a custom texture
868 if((Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0)){
869 texture = Interp_forced_bitmap;
871 texture = pm->textures[w(p+40)];
874 // muzzle flashes draw xparent
875 if(Interp_flags & MR_ALL_XPARENT){
876 gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha, -1, -1);
878 gr_set_bitmap( texture, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
882 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
883 gr_set_color( 128, 128, 128 );
887 if ( Interp_subspace ) {
888 g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT );
890 if(Interp_flags & MR_ALL_XPARENT){
891 g3_draw_poly( nv, Interp_list, Interp_tmap_flags );
893 g3_draw_poly( nv, Interp_list, Interp_tmap_flags|TMAP_FLAG_NONDARKENING );
899 if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) ) {
901 if ( Interp_flags & MR_SHOW_OUTLINE ) {
902 gr_set_color_fast( &Interp_outline_color );
905 for (i=0; i<nv; i++ ) {
906 int j = (i + 1) % nv;
907 g3_draw_line(Interp_list[i], Interp_list[j] );
913 void interp_draw_box( vector *min, vector *max )
917 vector bounding_box[8];
921 model_calc_bound_box(bounding_box,min,max);
923 for (i=0; i<8; i++ ) {
924 g3_rotate_vertex( &pts[i], &bounding_box[i] );
927 gr_set_color(128,0,0);
929 Tmap_show_layers = 1;
931 l[3] = &pts[0]; l[2] = &pts[1]; l[1] = &pts[2]; l[0] = &pts[3];
932 g3_draw_poly( 4, l, 0 );
934 l[3] = &pts[3]; l[2] = &pts[2]; l[1] = &pts[6]; l[0] = &pts[7];
935 g3_draw_poly( 4, l, 0 );
937 l[3] = &pts[2]; l[2] = &pts[1]; l[1] = &pts[5]; l[0] = &pts[6];
938 g3_draw_poly( 4, l, 0 );
940 l[3] = &pts[1]; l[2] = &pts[0]; l[1] = &pts[4]; l[0] = &pts[5];
941 g3_draw_poly( 4, l, 0 );
943 l[3] = &pts[0]; l[2] = &pts[3]; l[1] = &pts[7]; l[0] = &pts[4];
944 g3_draw_poly( 4, l, 0 );
946 l[3] = &pts[4]; l[2] = &pts[7]; l[1] = &pts[6]; l[0] = &pts[5];
947 g3_draw_poly( 4, l, 0 );
956 // +20 vector normal_point
958 // 36 int front offset
959 // 40 int back offset
960 // 44 int prelist offset
961 // 48 int postlist offset
962 // 52 int online offset
963 void model_interp_sortnorm(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
966 modelstats_num_sortnorms++;
969 // SDL_assert( w(p+4) == 56 );
971 int frontlist = w(p+36);
972 int backlist = w(p+40);
973 int prelist = w(p+44);
974 int postlist = w(p+48);
975 int onlist = w(p+52);
979 // light_filter_push_box( vp(p+56), vp(p+68) );
981 #if 1 //def BACK_TO_FRONT
983 if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
985 if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
987 //draw back then front
989 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
991 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
993 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
995 } else { //not facing. draw front then back
997 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
999 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1001 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1004 if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
1007 if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
1009 if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
1013 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1015 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1017 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1019 } else { //not facing. draw front then back
1021 //draw back then front
1023 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1025 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1027 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1031 if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
1035 // light_filter_pop();
1039 void model_draw_debug_points( polymodel *pm, bsp_info * submodel )
1041 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1045 // Draw the brown "core" mass
1046 // if ( submodel && (submodel->parent==-1) ) {
1047 // gr_set_color(128,128,0);
1048 // g3_draw_sphere_ez( &vmd_zero_vector, pm->core_radius );
1051 // Draw a red pivot point
1052 gr_set_color(128,0,0);
1053 g3_draw_sphere_ez(&vmd_zero_vector, 2.0f );
1055 // Draw a green center of mass when drawing the hull
1056 if ( submodel && (submodel->parent==-1) ) {
1057 gr_set_color(0,128,0);
1058 g3_draw_sphere_ez( &pm->center_of_mass, 1.0f );
1062 // Draw a blue center point
1063 gr_set_color(0,0,128);
1064 g3_draw_sphere_ez( &submodel->geometric_center, 0.9f );
1067 // Draw the bounding box
1072 for (i=0; i<8; i++ ) {
1073 g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] );
1075 gr_set_color(128,128,128);
1076 g3_draw_line( &pts[0], &pts[1] );
1077 g3_draw_line( &pts[1], &pts[2] );
1078 g3_draw_line( &pts[2], &pts[3] );
1079 g3_draw_line( &pts[3], &pts[0] );
1081 g3_draw_line( &pts[4], &pts[5] );
1082 g3_draw_line( &pts[5], &pts[6] );
1083 g3_draw_line( &pts[6], &pts[7] );
1084 g3_draw_line( &pts[7], &pts[4] );
1086 g3_draw_line( &pts[0], &pts[4] );
1087 g3_draw_line( &pts[1], &pts[5] );
1088 g3_draw_line( &pts[2], &pts[6] );
1089 g3_draw_line( &pts[3], &pts[7] );
1091 //for (i=0; i<8; i++ ) {
1092 // g3_rotate_vertex( &pts[i], &pm->bounding_box[i] );
1094 gr_set_color(0,255,0);
1097 for (j=0; j<8; j++ ) {
1099 vector bounding_box[8]; // caclulated fron min/max
1100 model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max);
1102 for (i=0; i<8; i++ ) {
1103 g3_rotate_vertex( &pts[i], &bounding_box[i] );
1105 gr_set_color(128,0,0);
1106 g3_draw_line( &pts[0], &pts[1] );
1107 g3_draw_line( &pts[1], &pts[2] );
1108 g3_draw_line( &pts[2], &pts[3] );
1109 g3_draw_line( &pts[3], &pts[0] );
1111 g3_draw_line( &pts[4], &pts[5] );
1112 g3_draw_line( &pts[5], &pts[6] );
1113 g3_draw_line( &pts[6], &pts[7] );
1114 g3_draw_line( &pts[7], &pts[4] );
1116 g3_draw_line( &pts[0], &pts[4] );
1117 g3_draw_line( &pts[1], &pts[5] );
1118 g3_draw_line( &pts[2], &pts[6] );
1119 g3_draw_line( &pts[3], &pts[7] );
1124 // Debug code to show all the paths of a model
1125 void model_draw_paths( int model_num )
1131 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1135 pm = model_get(model_num);
1141 for (i=0; i<pm->n_paths; i++ ) {
1144 for (j=0; j<pm->paths[i].nverts; j++ ) {
1145 // Rotate point into world coordinates
1146 pnt = pm->paths[i].verts[j].pos;
1148 // Pnt is now the x,y,z world coordinates of this vert.
1149 // For this example, I am just drawing a sphere at that
1153 g3_rotate_vertex(&tmp,&pnt);
1155 if ( pm->paths[i].verts[j].nturrets > 0 ){
1156 gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
1158 gr_set_color( 255, 0, 0 );
1161 // g3_draw_sphere( &tmp, pm->paths[i].verts[j].radius );
1162 g3_draw_sphere( &tmp, 0.5f );
1164 gr_set_color( 255, 0, 0 );
1166 g3_draw_line(&prev_pnt, &tmp);
1176 // docking bay and fighter bay paths
1177 void model_draw_bay_paths(int model_num)
1183 polymodel *pm = model_get(model_num);
1188 // render docking bay normals
1189 gr_set_color(0, 255, 0);
1190 for(idx=0; idx<pm->n_docks; idx++){
1191 for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
1192 v1 = pm->docking_bays[idx].pnt[s_idx];
1193 vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
1195 // rotate the points
1196 g3_rotate_vertex(&l1, &v1);
1197 g3_rotate_vertex(&l2, &v2);
1199 // draw the point and normal
1200 g3_draw_sphere(&l1, 2.0);
1201 g3_draw_line(&l1, &l2);
1205 // render figher bay paths
1206 gr_set_color(0, 255, 255);
1208 // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
1209 for (idx = 0; idx<pm->n_paths; idx++) {
1210 if ( !SDL_strncasecmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
1211 for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
1212 v1 = pm->paths[idx].verts[s_idx].pos;
1213 v2 = pm->paths[idx].verts[s_idx+1].pos;
1216 g3_rotate_vertex(&l1, &v1);
1217 g3_rotate_vertex(&l2, &v2);
1218 g3_draw_line(&l1, &l2);
1224 // struct that holds the indicies into path information associated with a fighter bay on a capital ship
1225 // NOTE: Fighter bay paths are identified by the path_name $bayN (where N is numbered from 1).
1226 // Capital ships only have ONE fighter bay on the entire ship
1227 #define MAX_SHIP_BAY_PATHS 10
1228 typedef struct ship_bay {
1229 int num_paths; // how many paths are associated with the model's fighter bay
1230 int paths[MAX_SHIP_BAY_PATHS]; // index into polymodel->paths[] array
1231 int arrive_flags; // bitfield, set to 1 when that path number is reserved for an arrival
1232 int depart_flags; // bitfield, set to 1 when that path number is reserved for a departure
1235 typedef struct mp_vert {
1236 vector pos; // xyz coordinates of vertex in object's frame of reference
1237 int nturrets; // number of turrets guarding this vertex
1238 int *turret_ids; // array of indices into ship_subsys linked list (can't index using [] though)
1239 float radius; // How far the closest obstruction is from this vertex
1242 typedef struct model_path {
1243 char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD
1244 char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF
1245 int parent_submodel;
1248 int goal; // Which of the verts is the one closest to the goal of this path
1249 int type; // What this path takes you to... See MP_TYPE_??? defines above for details
1250 int value; // This depends on the type.
1251 // For MP_TYPE_UNUSED, this means nothing.
1252 // For MP_TYPE_SUBSYS, this is the subsystem number this path takes you to.
1257 void interp_render_arc_segment( vector *v1, vector *v2, int depth )
1259 float d = vm_vec_dist_quick( v1, v2 );
1261 if ( d < 0.30f || (depth>4) ) {
1263 g3_rotate_vertex( &p1, v1 );
1264 g3_rotate_vertex( &p2, v2 );
1266 //g3_draw_rod( v1, 0.2f, v2, 0.2f, NULL, 0);
1267 g3_draw_line( &p1, &p2 );
1271 vm_vec_avg( &tmp, v1, v2 );
1273 float scaler = 0.30f;
1274 tmp.xyz.x += (frand()-0.5f)*d*scaler;
1275 tmp.xyz.y += (frand()-0.5f)*d*scaler;
1276 tmp.xyz.z += (frand()-0.5f)*d*scaler;
1278 interp_render_arc_segment( v1, &tmp, depth+1 );
1279 interp_render_arc_segment( &tmp, v2, depth+1 );
1283 int Interp_lightning = 1;
1284 DCF_BOOL( Arcs, Interp_lightning )
1292 void interp_render_lightning( polymodel *pm, bsp_info * sm )
1294 SDL_assert( sm->num_arcs > 0 );
1298 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1302 if (!Interp_lightning) return;
1304 // if ( key_pressed(SDLK_LSHIFT) ) return;
1305 // if ( rad < 3.0f ) return;
1307 for (i=0; i<sm->num_arcs; i++ ) {
1308 // pick a color based upon arc type
1309 switch(sm->arc_type[i]){
1310 // "normal", Freespace 1 style arcs
1311 case MARC_TYPE_NORMAL:
1312 if ( (myrand()>>4) & 1 ) {
1313 gr_set_color( 64, 64, 255 );
1315 gr_set_color( 128, 128, 255 );
1321 if ( (myrand()>>4) & 1 ) {
1322 gr_set_color( AR, AG, AB );
1324 gr_set_color( AR2, AG2, AB2 );
1332 // render the actual arc segment
1333 interp_render_arc_segment( &sm->arc_pts[i][0], &sm->arc_pts[i][1], 0 );
1337 void model_interp_subcall(polymodel * pm, int mn, int detail_level)
1340 int zbuf_mode, zbuf_mode_save;
1342 if ( (mn < 0) || (mn>=pm->n_models) )
1345 SDL_assert( mn >= 0 );
1346 SDL_assert( mn < pm->n_models );
1348 // mprintf(( "Name = '%s'\n", pm->submodel[mn].name ));
1349 // char * p = pm->submodel[mn].name;
1351 if (pm->submodel[mn].blown_off){
1355 if (pm->submodel[mn].is_thruster ) {
1356 if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){
1359 Interp_thrust_scale_subobj=1;
1361 Interp_thrust_scale_subobj=0;
1364 g3_start_instance_angles(&pm->submodel[mn].offset, &pm->submodel[mn].angs);
1365 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1369 model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 );
1371 if (Interp_flags & MR_SHOW_PIVOTS )
1372 model_draw_debug_points( pm, &pm->submodel[mn] );
1374 if ( pm->submodel[mn].num_arcs ) {
1375 interp_render_lightning( pm, &pm->submodel[mn]);
1378 zbuf_mode_save = gr_zbuffer_get();
1380 i = pm->submodel[mn].first_child;
1382 if (!pm->submodel[i].is_thruster ) {
1383 if(Interp_flags & MR_NO_ZBUFFER){
1384 zbuf_mode = GR_ZBUFF_NONE;
1386 zbuf_mode = GR_ZBUFF_FULL; // read only
1389 gr_zbuffer_set(zbuf_mode);
1391 model_interp_subcall( pm, i, detail_level );
1393 i = pm->submodel[i].next_sibling;
1396 gr_zbuffer_set(zbuf_mode_save);
1401 // Returns one of the following
1402 #define IBOX_ALL_OFF 0
1403 #define IBOX_ALL_ON 1
1404 #define IBOX_SOME_ON_SOME_OFF 2
1406 int interp_box_offscreen( vector *min, vector *max )
1408 if ( key_pressed(SDLK_LSHIFT) ) {
1413 v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z;
1414 v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z;
1415 v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z;
1416 v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z;
1418 v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z;
1419 v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z;
1420 v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z;
1421 v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z;
1423 ubyte and_codes = 0xff;
1424 ubyte or_codes = 0xff;
1427 for (i=0; i<8; i++ ) {
1429 ubyte codes=g3_rotate_vertex( &tmp, &v[i] );
1430 // Early out which we cannot do because we want to differentiate btwn
1431 // IBOX_SOME_ON_SOME_OFF and IBOX_ALL_ON
1433 // //mprintf(( "A point is inside, so render it.\n" ));
1434 // return 0; // this point is in, so return 0
1440 // If and_codes is set this means that all points lie off to the
1441 // same side of the screen.
1443 //mprintf(( "All points offscreen, so don't render it.\n" ));
1444 return IBOX_ALL_OFF; //all points off screen
1447 // If this is set it means at least one of the points is offscreen,
1448 // but they aren't all off to the same side.
1450 return IBOX_SOME_ON_SOME_OFF;
1453 // They are all onscreen.
1458 //calls the object interpreter to render an object.
1459 //returns true if drew
1460 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check )
1462 ubyte *p = (ubyte *)model_ptr;
1463 int chunk_type, chunk_size;
1467 chunk_size = w(p+4);
1469 while ( chunk_type != OP_EOF ) {
1471 // mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size ));
1473 switch (chunk_type) {
1474 case OP_EOF: return 1;
1475 case OP_DEFPOINTS: model_interp_defpoints(p,pm,sm); break;
1476 case OP_FLATPOLY: model_interp_flatpoly(p,pm); break;
1477 case OP_TMAPPOLY: model_interp_tmappoly(p,pm); break;
1478 case OP_SORTNORM: model_interp_sortnorm(p,pm,sm,do_box_check); break;
1482 if ( do_box_check ) {
1483 int retval = interp_box_offscreen( vp(p+8), vp(p+20) );
1486 goto DoneWithThis; // Don't need to draw any more polys from this box
1490 do_box_check = 0; // Don't need to check boxes any more
1493 case IBOX_SOME_ON_SOME_OFF:
1494 // continue like we were
1502 if (Interp_flags & MR_SHOW_PIVOTS ) {
1504 modelstats_num_boxes++;
1506 interp_draw_box( vp(p+8), vp(p+20) );
1509 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1514 light_filter_push_box( vp(p+8), vp(p+20) );
1520 mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size ));
1521 Int3(); // Bad chunk type!
1526 chunk_size = w(p+4);
1531 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1541 void model_render_shields( polymodel * pm )
1545 vertex pnt0, tmp, prev_pnt;
1547 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1551 gr_set_color(0, 0, 200 );
1553 // Scan all the triangles in the mesh.
1554 for (i=0; i<pm->shield.ntris; i++ ) {
1556 tri = &pm->shield.tris[i];
1558 if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) {
1560 // Process the vertices.
1561 // Note this rotates each vertex each time it's needed, very dumb.
1562 for (j=0; j<3; j++ ) {
1564 g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos );
1567 g3_draw_line(&prev_pnt, &tmp);
1573 g3_draw_line(&pnt0, &prev_pnt);
1578 void model_render_insignias(polymodel *pm, int detail_level)
1582 vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] };
1586 // if the model has no insignias we're done
1587 if(pm->num_ins <= 0){
1591 // set the proper texture
1592 if(Interp_insignia_bitmap >= 0){
1593 gr_set_bitmap(Interp_insignia_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1595 // otherwise don't even bother rendering
1600 // otherwise render them
1601 for(idx=0; idx<pm->num_ins; idx++){
1602 // skip insignias not on our detail level
1603 if(pm->ins[idx].detail_level != detail_level){
1607 for(s_idx=0; s_idx<pm->ins[idx].num_faces; s_idx++){
1608 // get vertex indices
1609 i1 = pm->ins[idx].faces[s_idx][0];
1610 i2 = pm->ins[idx].faces[s_idx][1];
1611 i3 = pm->ins[idx].faces[s_idx][2];
1613 // transform vecs and setup vertices
1614 vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset);
1615 vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset);
1616 vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset);
1617 g3_rotate_vertex(&vecs[0], &t1);
1618 g3_rotate_vertex(&vecs[1], &t2);
1619 g3_rotate_vertex(&vecs[2], &t3);
1621 // setup texture coords
1622 vecs[0].u = pm->ins[idx].u[s_idx][0]; vecs[0].v = pm->ins[idx].v[s_idx][0];
1623 vecs[1].u = pm->ins[idx].u[s_idx][1]; vecs[1].v = pm->ins[idx].v[s_idx][1];
1624 vecs[2].u = pm->ins[idx].u[s_idx][2]; vecs[2].v = pm->ins[idx].v[s_idx][2];
1627 g3_draw_poly(3, vlist, TMAP_FLAG_TEXTURED);
1632 int Model_texturing = 1;
1633 int Model_polys = 1;
1635 DCF_BOOL( model_texturing, Model_texturing )
1636 DCF_BOOL( model_polys, Model_polys )
1638 MONITOR( NumModelsRend );
1639 MONITOR( NumHiModelsRend );
1640 MONITOR( NumMedModelsRend );
1641 MONITOR( NumLowModelsRend );
1644 void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id );
1647 //draws a bitmap with the specified 3d width & height
1648 //returns 1 if off screen, 0 if not
1649 int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v)
1654 SDL_assert( G3_count == 1 );
1660 sa = (float)sin(angle);
1661 ca = (float)cos(angle);
1663 float width, height;
1665 width = height = rad;
1667 v[0].x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
1668 v[0].y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
1674 v[1].x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
1675 v[1].y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
1681 v[2].x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
1682 v[2].y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
1688 v[3].x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
1689 v[3].y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
1695 ubyte codes_and=0xff;
1698 z = pnt->z - rad / 4.0f;
1699 if ( z < 0.0f ) z = 0.0f;
1702 for (i=0; i<4; i++ ) {
1703 //now code the four points
1704 codes_and &= g3_code_vertex(&v[i]);
1705 v[i].flags = 0; // mark as not yet projected
1706 g3_project_vertex(&v[i]);
1711 return 1; //1 means off screen
1717 float Interp_depth_scale = 1500.0f;
1719 DCF(model_darkening,"Makes models darker with distance")
1722 dc_get_arg(ARG_FLOAT);
1723 Interp_depth_scale = Dc_arg_float;
1727 dc_printf( "Usage: model_darkening float\n" );
1728 Dc_status = 0; // don't print status if help is printed. Too messy.
1732 dc_printf( "model_darkening = %.1f\n", Interp_depth_scale );
1736 // Compare it to 999.75f at R = 64.0f
1737 // 0.0000f at R = 4.0f
1739 //float cmp_val = 999.75f; // old
1741 // Return a number from 'min' to 'max' where it is
1742 // 'v' is between v1 and v2.
1743 float scale_it( float min, float max, float v, float v1, float v2 )
1745 if ( v < v1 ) return min;
1746 if ( v > v2 ) return max;
1748 v = (v - v1)/(v2-v1);
1749 v = v*(max-min)+min;
1754 void model_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int lighting_skip )
1756 polymodel *pm = model_get(model_num);
1758 // maybe turn off (hardware) culling
1759 if(flags & MR_NO_CULL){
1763 Interp_objnum = objnum;
1765 if ( flags & MR_NO_LIGHTING ) {
1766 Interp_light = 1.0f;
1768 // never use saved lighitng for this object
1769 model_set_saved_lighting(-1, -1);
1770 } else if ( flags & MR_IS_ASTEROID ) {
1771 // Dim it based on distance
1772 float depth = vm_vec_dist_quick( pos, &Eye_position );
1773 if ( depth > Interp_depth_scale ) {
1774 Interp_light = Interp_depth_scale/depth;
1775 // If it is too far, exit
1776 if ( Interp_light < (1.0f/32.0f) ) {
1777 Interp_light = 0.0f;
1779 } else if ( Interp_light > 1.0f ) {
1780 Interp_light = 1.0f;
1783 Interp_light = 1.0f;
1786 // never use saved lighitng for this object
1787 model_set_saved_lighting(-1, -1);
1789 Interp_light = 1.0f;
1791 // maybe use saved lighting
1792 model_set_saved_lighting(objnum, hack_skip_max);
1795 if ( !(flags & MR_NO_LIGHTING ) ) {
1796 light_filter_push( objnum, pos, pm->rad );
1799 model_really_render(model_num, orient, pos, flags, objnum);
1801 if ( !(flags & MR_NO_LIGHTING ) ) {
1805 // maybe turn culling back on
1806 if(flags & MR_NO_CULL){
1810 // turn off fog after each model renders
1811 if(The_mission.flags & MISSION_FLAG_FULLNEB){
1812 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
1823 #if MAX_DETAIL_LEVEL != 4
1824 #error MAX_DETAIL_LEVEL is assumed to be 4 in ModelInterp.cpp
1827 // Find the distance from p0 to the closest point on a box.
1828 // The box's dimensions from 'min' to 'max'.
1829 float interp_closest_dist_to_box( vector *hitpt, vector *p0, vector *min, vector *max )
1831 float *origin = (float *)&p0->xyz.x;
1832 float *minB = (float *)min;
1833 float *maxB = (float *)max;
1834 float *coord = (float *)&hitpt->xyz.x;
1838 for (i=0; i<3; i++ ) {
1839 if ( origin[i] < minB[i] ) {
1842 } else if (origin[i] > maxB[i] ) {
1846 coord[i] = origin[i];
1854 return vm_vec_dist(hitpt,p0);
1858 // Finds the closest point on a model to a point in space. Actually only finds a point
1859 // on the bounding box of the model.
1861 // model_num Which model
1862 // orient Orientation of the model
1863 // pos Position of the model
1864 // eye_pos Point that you want to find the closest point to
1866 // distance from eye_pos to closest_point. 0 means eye_pos is
1867 // on or inside the bounding box.
1868 // Also fills in outpnt with the actual closest point.
1869 float model_find_closest_point( vector *outpnt, int model_num, int submodel_num, matrix *orient, vector * pos, vector *eye_pos )
1871 vector closest_pos, tempv, eye_rel_pos;
1873 polymodel *pm = model_get(model_num);
1875 if ( submodel_num < 0 ) {
1876 submodel_num = pm->detail[0];
1879 // Rotate eye pos into object coordinates
1880 vm_vec_sub(&tempv,pos,eye_pos );
1881 vm_vec_rotate(&eye_rel_pos,&tempv,orient );
1883 return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max );
1891 dc_printf("Tiled textures\n");
1893 dc_printf("Non-tiled textures\n");
1897 void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id )
1899 int i, detail_level;
1901 uint save_gr_zbuffering_mode;
1904 MONITOR_INC( NumModelsRend, 1 );
1906 Interp_orient = orient;
1909 int tmp_detail_level = Game_detail_level;
1911 // if ( D3D_enabled ) {
1912 // tmp_detail_level = -1; // Force no hires models for Direct3D
1915 // Tmap_show_layers = 1;
1916 // model_set_detail_level(0);
1917 // flags |= MR_LOCK_DETAIL|MR_NO_TEXTURING|MR_NO_LIGHTING; //MR_LOCK_DETAIL | |MR_NO_LIGHTING|MR_NO_SMOOTHINGMR_NO_TEXTURING |
1919 // Turn off engine effect
1920 Interp_thrust_scale_subobj=0;
1922 if (!Model_texturing)
1923 flags |= MR_NO_TEXTURING;
1925 if ( !Model_polys ) {
1926 flags |= MR_NO_POLYS;
1929 Interp_flags = flags;
1931 pm = model_get(model_num);
1933 // Set the flags we will pass to the tmapper
1934 Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
1936 // if we're in nebula mode
1937 if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
1938 Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
1941 if ( !(Interp_flags & MR_NO_TEXTURING) ) {
1942 Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
1944 if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
1945 Interp_tmap_flags |= TMAP_FLAG_TILED;
1947 if ( !(Interp_flags & MR_NO_CORRECT) ) {
1948 Interp_tmap_flags |= TMAP_FLAG_CORRECT;
1952 save_gr_zbuffering_mode = gr_zbuffer_get();
1953 zbuf_mode = gr_zbuffer_get();
1955 if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) {
1956 gr_set_color(0,128,0);
1957 g3_draw_sphere_ez( pos, pm->rad );
1961 g3_start_instance_matrix(pos,orient);
1963 if ( Interp_flags & MR_SHOW_RADIUS ) {
1964 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
1965 gr_set_color(0,64,0);
1966 g3_draw_sphere_ez(&vmd_zero_vector,pm->rad);
1970 SDL_assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS );
1973 float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
1974 if ( pm->n_detail_levels > 1 ) {
1976 if ( Interp_flags & MR_LOCK_DETAIL ) {
1977 i = Interp_detail_level+1;
1980 //gr_set_color(0,128,0);
1981 //g3_draw_sphere_ez( &closest_pos, 2.0f );
1983 #if MAX_DETAIL_LEVEL != 4
1984 #error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4
1987 switch( Detail.detail_distance ) {
1991 case 1: // lower than normal
1994 case 2: // default (leave the same)
1996 case 3: // above normal
1999 case 4: // even more normal
2005 if(The_mission.flags & MISSION_FLAG_FULLNEB){
2006 depth *= neb2_get_lod_scale(Interp_objnum);
2009 for ( i=0; i<pm->n_detail_levels; i++ ) {
2010 if ( depth<=pm->detail_depth[i] ){
2015 // If no valid detail depths specified, use highest.
2016 if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f)) {
2022 // maybe force lower detail
2023 if (Interp_flags & MR_FORCE_LOWER_DETAIL) {
2027 //detail_level = fl2i(depth/10.0f);
2029 detail_level = i-1-tmp_detail_level;
2031 if ( detail_level < 0 )
2033 else if (detail_level >= pm->n_detail_levels )
2034 detail_level = pm->n_detail_levels-1;
2036 //mprintf(( "Depth = %.2f, detail = %d\n", depth, detail_level ));
2043 if ( detail_level==0 ) {
2044 MONITOR_INC( NumHiModelsRend, 1 );
2045 } else if ( detail_level ==pm->n_detail_levels-1 ) {
2046 MONITOR_INC( NumLowModelsRend, 1 );
2048 MONITOR_INC( NumMedModelsRend, 1 );
2052 if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){
2053 vector auto_back = pm->autocenter;
2054 vm_vec_scale(&auto_back, -1.0f);
2055 g3_start_instance_matrix(&auto_back, NULL);
2060 // Draw the subobjects
2061 i = pm->submodel[pm->detail[detail_level]].first_child;
2064 if (!pm->submodel[i].is_thruster ) {
2065 zbuf_mode = GR_ZBUFF_WRITE;
2068 if(Interp_flags & MR_NO_ZBUFFER){
2069 zbuf_mode = GR_ZBUFF_NONE;
2072 gr_zbuffer_set(zbuf_mode);
2074 model_interp_subcall( pm, i, detail_level );
2076 i = pm->submodel[i].next_sibling;
2079 // rotate lights for the hull
2080 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2084 if ( pm->submodel[pm->detail[detail_level]].num_children > 0 ){
2085 // zbuf_mode |= GR_ZBUFF_WRITE; // write only
2086 zbuf_mode = GR_ZBUFF_FULL;
2090 if(Interp_flags & MR_NO_ZBUFFER){
2091 zbuf_mode = GR_ZBUFF_NONE;
2094 gr_zbuffer_set(zbuf_mode);
2098 // draw the hull of the ship
2099 model_interp_sub( (ubyte *)pm->submodel[pm->detail[detail_level]].bsp_data, pm, &pm->submodel[pm->detail[detail_level]], 0 );
2101 if (Interp_flags & MR_SHOW_PIVOTS ) {
2102 model_draw_debug_points( pm, NULL );
2103 model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]] );
2105 if(pm->flags & PM_FLAG_AUTOCEN){
2106 gr_set_color(255, 255, 255);
2107 g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
2111 if ( pm->submodel[pm->detail[0]].num_arcs ) {
2112 interp_render_lightning( pm, &pm->submodel[pm->detail[0]]);
2115 if ( Interp_flags & MR_SHOW_SHIELDS ) {
2116 model_render_shields(pm);
2119 // render model insignias
2122 gr_zbuffer_set(GR_ZBUFF_READ);
2123 model_render_insignias(pm, detail_level);
2128 // Draw the thruster glow
2130 if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) /*&& (Detail.engine_glows)*/ ) {
2132 if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) && (Detail.engine_glows) ) {
2135 for (i = 0; i < pm->n_thrusters; i++ ) {
2136 thruster_bank *bank = &pm->thrusters[i];
2139 for ( j=0; j<bank->num_slots; j++ ) {
2142 vm_vec_sub(&tempv,&View_position,&bank->pnt[j]);
2143 vm_vec_normalize(&tempv);
2145 d = vm_vec_dot(&tempv,&bank->norm[j]);
2150 // Make glow bitmap fade in/out quicker from sides.
2152 if ( d > 1.0f ) d = 1.0f;
2154 // fade them in the nebula as well
2155 if(The_mission.flags & MISSION_FLAG_FULLNEB){
2156 d *= (1.0f - Interp_fog_level);
2159 //ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
2160 #define NOISE_SCALE 0.5f
2161 #define MIN_SCALE 3.4f
2162 #define MAX_SCALE 4.7f
2163 float scale = MIN_SCALE;
2165 scale = (Interp_thrust_scale-0.1f)*(MAX_SCALE-MIN_SCALE)+MIN_SCALE;
2167 float w = bank->radius[j]*(scale+Interp_thrust_glow_noise*NOISE_SCALE );
2170 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0, -1.0f, -1.0f);
2172 g3_rotate_vertex( &p, &bank->pnt[j] );
2173 gr_set_bitmap( Interp_thrust_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, d, -1, -1);
2174 g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED );
2181 Interp_thrust_glow_bitmap = -1;
2185 // Draw the thruster subobjects
2186 i = pm->submodel[pm->detail[detail_level]].first_child;
2188 if (pm->submodel[i].is_thruster ) {
2189 zbuf_mode = GR_ZBUFF_READ;
2192 if(Interp_flags & MR_NO_ZBUFFER){
2193 zbuf_mode = GR_ZBUFF_NONE;
2196 gr_zbuffer_set(zbuf_mode);
2198 model_interp_subcall( pm, i, detail_level );
2200 i = pm->submodel[i].next_sibling;
2205 if ( Interp_flags & MR_SHOW_PATHS ){
2206 model_draw_paths(model_num);
2209 if (Interp_flags & MR_BAY_PATHS ){
2210 model_draw_bay_paths(model_num);
2213 if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){
2218 gr_zbuffer_set(save_gr_zbuffering_mode);
2222 void submodel_render(int model_num, int submodel_num, matrix *orient, vector * pos, uint flags, int light_ignore_id)
2226 MONITOR_INC( NumModelsRend, 1 );
2228 if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return;
2230 // Turn off engine effect
2231 Interp_thrust_scale_subobj=0;
2233 if (!Model_texturing)
2234 flags |= MR_NO_TEXTURING;
2236 Interp_flags = flags;
2238 pm = model_get(model_num);
2240 // Set the flags we will pass to the tmapper
2241 Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
2243 // if we're in nebula mode
2244 if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
2245 Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
2248 if ( !(Interp_flags & MR_NO_TEXTURING) ) {
2249 Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
2251 if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling )
2252 Interp_tmap_flags |= TMAP_FLAG_TILED;
2254 if ( !(Interp_flags & MR_NO_CORRECT) ) {
2255 Interp_tmap_flags |= TMAP_FLAG_CORRECT;
2259 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2260 light_filter_push( -1, pos, pm->submodel[submodel_num].rad );
2263 g3_start_instance_matrix(pos,orient);
2265 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2269 model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 );
2270 if ( pm->submodel[submodel_num].num_arcs ) {
2271 interp_render_lightning( pm, &pm->submodel[submodel_num]);
2274 if (Interp_flags & MR_SHOW_PIVOTS )
2275 model_draw_debug_points( pm, &pm->submodel[submodel_num] );
2277 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2286 // Fills in an array with points from a model.
2287 // Only gets up to max_num verts;
2288 // Returns number of verts found;
2289 static int submodel_get_points_internal(int model_num, int submodel_num, int max_num, vector **pnts, vector **norms )
2293 pm = model_get(model_num);
2295 if ( submodel_num < 0 ) {
2296 submodel_num = pm->detail[0];
2299 ubyte *p = pm->submodel[submodel_num].bsp_data;
2300 int chunk_type, chunk_size;
2303 chunk_size = w(p+4);
2305 while (chunk_type != OP_EOF) {
2306 switch (chunk_type) {
2307 case OP_EOF: return 1;
2308 case OP_DEFPOINTS: {
2310 int nverts = w(p+8);
2311 int offset = w(p+16);
2313 ubyte * normcount = p+20;
2314 vector *src = vp(p+offset);
2316 if ( nverts > max_num )
2319 for (n=0; n<nverts; n++ ) {
2321 *norms++ = src + 1; // first normal associated with the point
2323 src += normcount[n]+1;
2325 return nverts; // Read in 'n' points
2328 case OP_FLATPOLY: break;
2329 case OP_TMAPPOLY: break;
2330 case OP_SORTNORM: break;
2331 case OP_BOUNDBOX: break;
2333 mprintf(( "Bad chunk type %d, len=%d in submodel_get_points\n", chunk_type, chunk_size ));
2334 Int3(); // Bad chunk type!
2339 chunk_size = w(p+4);
2341 return 0; // Couldn't find 'em
2344 // Gets two random points on a model
2345 void submodel_get_two_random_points(int model_num, int submodel_num, vector *v1, vector *v2, vector *n1, vector *n2 )
2347 int nv = submodel_get_points_internal(model_num, submodel_num, MAX_POLYGON_VECS, Interp_verts, Interp_norms );
2351 int vn1 = (myrand()>>5) % nv;
2352 int vn2 = (myrand()>>5) % nv;
2354 *v1 = *Interp_verts[vn1];
2355 *v2 = *Interp_verts[vn2];
2358 *n1 = *Interp_norms[vn1];
2361 *n2 = *Interp_norms[vn2];
2365 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
2366 // This defaults to black.
2367 void model_set_outline_color(int r, int g, int b )
2369 gr_init_color( &Interp_outline_color, r, g, b );
2373 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
2374 // This defaults to black.
2375 void model_set_outline_color_fast(void *outline_color)
2377 Interp_outline_color = *((color*)(outline_color));
2380 // IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n'
2381 // This defaults to 0. (0=highest, larger=lower)
2382 void model_set_detail_level(int n)
2384 Interp_detail_level = n;
2388 // Returns number of verts in a submodel;
2389 int submodel_get_num_verts(int model_num, int submodel_num )
2393 pm = model_get(model_num);
2395 ubyte *p = pm->submodel[submodel_num].bsp_data;
2396 int chunk_type, chunk_size;
2399 chunk_size = w(p+4);
2401 while (chunk_type != OP_EOF) {
2402 switch (chunk_type) {
2403 case OP_EOF: return 0;
2404 case OP_DEFPOINTS: {
2406 return n; // Read in 'n' points
2409 case OP_FLATPOLY: break;
2410 case OP_TMAPPOLY: break;
2411 case OP_SORTNORM: break;
2412 case OP_BOUNDBOX: break;
2414 mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size ));
2415 Int3(); // Bad chunk type!
2420 chunk_size = w(p+4);
2422 return 0; // Couldn't find 'em
2425 // Returns number of tmaps & flat polys in a submodel;
2426 int submodel_get_num_polys_sub( ubyte *p )
2428 int chunk_type = w(p);
2429 int chunk_size = w(p+4);
2432 while (chunk_type != OP_EOF) {
2433 switch (chunk_type) {
2434 case OP_EOF: return n;
2435 case OP_DEFPOINTS: break;
2436 case OP_FLATPOLY: n++; break;
2437 case OP_TMAPPOLY: n++; break;
2439 int frontlist = w(p+36);
2440 int backlist = w(p+40);
2441 int prelist = w(p+44);
2442 int postlist = w(p+48);
2443 int onlist = w(p+52);
2444 n += submodel_get_num_polys_sub(p+frontlist);
2445 n += submodel_get_num_polys_sub(p+backlist);
2446 n += submodel_get_num_polys_sub(p+prelist);
2447 n += submodel_get_num_polys_sub(p+postlist );
2448 n += submodel_get_num_polys_sub(p+onlist );
2451 case OP_BOUNDBOX: break;
2453 mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size ));
2454 Int3(); // Bad chunk type!
2459 chunk_size = w(p+4);
2464 // Returns number of tmaps & flat polys in a submodel;
2465 int submodel_get_num_polys(int model_num, int submodel_num )
2469 pm = model_get(model_num);
2471 return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data );
2475 // Sets the submodel instance data in a submodel
2476 // If show_damaged is true it shows only damaged submodels.
2477 // If it is false it shows only undamaged submodels.
2478 void model_show_damaged(int model_num, int show_damaged )
2483 pm = model_get(model_num);
2485 for (i=0; i<pm->n_models; i++ ) {
2486 bsp_info *sm = &pm->submodel[i];
2488 // Set the "blown out" flags
2492 for (i=0; i<pm->n_models; i++ ) {
2493 bsp_info *sm = &pm->submodel[i];
2495 // Set the "blown out" flags
2496 if ( show_damaged ) {
2497 if ( sm->my_replacement > -1 ) {
2498 pm->submodel[sm->my_replacement].blown_off = 0;
2502 if ( sm->my_replacement > -1 ) {
2503 pm->submodel[sm->my_replacement].blown_off = 1;
2510 // set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether)
2511 void model_set_insignia_bitmap(int bmap)
2513 Interp_insignia_bitmap = bmap;
2516 // set the forces bitmap
2517 void model_set_forced_texture(int bmap)
2519 Interp_forced_bitmap = bmap;
2522 // set model transparency for use with MR_ALL_XPARENT
2523 void model_set_alpha(float alpha)
2525 Interp_xparent_alpha = alpha;
2528 // see if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error
2529 int model_find_texture(int model_num, int bitmap)
2534 // get a handle to the model
2535 pm = model_get(model_num);
2541 for(idx=0; idx<pm->n_textures; idx++){
2542 if(pm->textures[idx] == bitmap){
2551 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
2552 // returns closest distance to extended box
2553 // positive return value means start_point is outside extended box
2554 // displaces closest point an optional amount delta to the outside of the box
2555 // closest_box_point can be NULL.
2556 float get_model_closest_box_point_with_delta(vector *closest_box_point, vector *start_point, int modelnum, int *is_inside, float delta)
2559 vector box_point, ray_direction, *extremes;
2560 float dist, best_dist;
2563 int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
2564 int mask_inside = 0x3f;
2566 best_dist = FLT_MAX;
2567 pm = model_get(modelnum);
2569 for (i=0; i<6; i++) {
2570 idx = i / 2; // which row vector of Identity matrix
2572 memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vector));
2574 // do negative, then positive plane for each axis
2576 extremes = &pm->mins;
2577 vm_vec_negate(&ray_direction);
2579 extremes = &pm->maxs;
2582 // a negative distance means started outside the box
2583 dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f);
2587 if (fabs(dist) < fabs(best_dist)) {
2589 if (closest_box_point) {
2590 vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta);
2595 // is start_point inside the box
2597 *is_inside = (inside == mask_inside);
2603 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
2604 // returns closest distance to extended box
2605 // positive return value means start_point is outside extended box
2606 // displaces closest point an optional amount delta to the outside of the box
2607 // closest_box_point can be NULL.
2608 float get_world_closest_box_point_with_delta(vector *closest_box_point, object *box_obj, vector *start_point, int *is_inside, float delta)
2610 vector temp, box_start;
2615 modelnum = Ships[box_obj->instance].modelnum;
2617 // rotate start_point to box_obj RF
2618 vm_vec_sub(&temp, start_point, &box_obj->pos);
2619 vm_vec_rotate(&box_start, &temp, &box_obj->orient);
2621 dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta);
2623 // rotate closest_box_point to world RF
2624 if (closest_box_point) {
2625 vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient);
2626 vm_vec_add(closest_box_point, &temp, &box_obj->pos);
2632 void model_set_fog_level(float l)
2634 Interp_fog_level = l;
2637 // given a newly loaded model, page in all textures
2638 void model_page_in_textures(int modelnum, int ship_info_index)
2644 if((ship_info_index < 0) || (ship_info_index >= Num_ship_types)){
2647 sip = &Ship_info[ship_info_index];
2649 polymodel *pm = model_get(modelnum);
2656 // set nondarkening pixels
2657 if(sip->num_nondark_colors){
2658 palman_set_nondarkening(sip->nondark_colors, sip->num_nondark_colors);
2660 // use the colors from the default table
2662 palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default);
2665 for (idx=0; idx<pm->n_textures; idx++ ){
2666 int bitmap_num = pm->original_textures[idx];
2668 if ( bitmap_num > -1 ) {
2669 bm_lock(bitmap_num, 16, BMP_TEX_OTHER);
2670 bm_unlock(bitmap_num);
2675 // is the given model a pirate ship?
2676 int model_is_pirate_ship(int modelnum)