/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ /* * $Logfile: /Freespace2/code/Model/ModelInterp.cpp $ * $Revision: 2.2 $ * $Date: 2002-07-10 18:42:14 $ * $Author: wmcoolmon $ * * Rendering models, I think. * * $Log: not supported by cvs2svn $ * Revision 2.1 2002/07/07 19:55:59 penguin * Back-port to MSVC * * Revision 2.0 2002/06/03 04:02:25 penguin * Warpcore CVS sync * * Revision 1.3 2002/05/09 13:49:30 mharris * Added ifndef NO_DIRECT3D * * Revision 1.2 2002/05/04 04:52:22 mharris * 1st draft at porting * * Revision 1.1 2002/05/02 18:03:10 mharris * Initial checkin - converted filenames and includes to lower case * * * 37 9/13/99 11:25p Dave * Fixed problem with mode-switching and D3D movies. * * 36 9/13/99 10:09a Andsager * Add debug console commands to lower model render detail and fireball * LOD for big ship explosiosns. * * 35 9/08/99 12:03a Dave * Make squad logos render properly in D3D all the time. Added intel anim * directory. * * 34 9/01/99 10:09a Dave * Pirate bob. * * 33 8/30/99 5:01p Dave * Made d3d do less state changing in the nebula. Use new chat server for * PXO. * * 32 8/24/99 8:55p Dave * Make sure nondimming pixels work properly in tech menu. * * 31 7/29/99 10:47p Dave * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. * * 30 7/29/99 12:05a Dave * Nebula speed optimizations. * * 29 7/27/99 3:09p Dave * Made g400 work. Whee. * * 28 7/24/99 4:19p Dave * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much * better. Made model code use zbuffer more intelligently. * * 27 7/19/99 7:20p Dave * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula * pre-rendering. * * 26 7/18/99 5:20p Dave * Jump node icon. Fixed debris fogging. Framerate warning stuff. * * 25 7/15/99 2:13p Dave * Added 32 bit detection. * * 24 6/22/99 7:03p Dave * New detail options screen. * * 23 6/18/99 5:16p Dave * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD * dialog to PXO screen. * * 22 5/26/99 11:46a Dave * Added ship-blasting lighting and made the randomization of lighting * much more customizable. * * 21 5/24/99 5:45p Dave * Added detail levels to the nebula, with a decent speedup. Split nebula * lightning into its own section. * * 20 5/12/99 10:05a Johnson * DKA: Fix fred bug from engine wash * * 19 5/08/99 8:25p Dave * Upped object pairs. First run of nebula lightning. * * 18 4/26/99 8:49p Dave * Made all pof based nebula stuff full customizable through fred. * * 17 4/23/99 5:53p Dave * Started putting in new pof nebula support into Fred. * * 16 4/20/99 3:30p Andsager * Let get_model_closest_box_point_with_delta() take NULL as pointer to * is_inside * * 15 4/19/99 12:51p Andsager * Add function to find the nearest point on extneded bounding box and * check if inside bounding box. * * 14 3/31/99 8:24p Dave * Beefed up all kinds of stuff, incluging beam weapons, nebula effects * and background nebulae. Added per-ship non-dimming pixel colors. * * 13 3/24/99 6:14p Dave * Added position and orientation checksumming for multiplayer. Fixed LOD * rendering bugs for squad insignias * * 12 3/23/99 5:17p Dave * Changed model file format somewhat to account for LOD's on insignias * * 11 3/19/99 9:51a Dave * Checkin to repair massive source safe crash. Also added support for * pof-style nebulae, and some new weapons code. * * 10 3/08/99 7:03p Dave * First run of new object update system. Looks very promising. * * 9 3/02/99 9:25p Dave * Added a bunch of model rendering debug code. Started work on fixing * beam weapon wacky firing. * * 8 2/19/99 11:42a Dave * Put in model rendering autocentering. * * 7 1/14/99 6:06p Dave * 100% full squad logo support for single player and multiplayer. * * 6 1/08/99 2:08p Dave * Fixed software rendering for pofview. Super early support for AWACS and * beam weapons. * * 5 1/06/99 2:24p Dave * Stubs and release build fixes. * * 4 12/09/98 7:34p Dave * Cleanup up nebula effect. Tweaked many values. * * 3 12/06/98 2:36p Dave * Drastically improved nebula fogging. * * 2 10/07/98 10:53a Dave * Initial checkin. * * 1 10/07/98 10:50a Dave * * 193 8/28/98 3:29p Dave * EMP effect done. AI effects may need some tweaking as required. * * 192 6/13/98 3:18p Hoffoss * NOX()ed out a bunch of strings that shouldn't be translated. * * 191 5/24/98 4:53p John * changed rounding for model caching to get rid of some empty lines in * cached bitmap. * * 190 5/16/98 4:47p John * Put back in my version 188 changes that someone so rudely (but * hopefully unintentionally) deleted. * * 189 5/13/98 11:34p Mike * Model caching system. * * 187 5/12/98 11:32a Mike * Support for weapon pof detail levels. * * 186 5/08/98 1:32p John * Added code for using two layered subspace effects. * * 185 4/29/98 11:03a John * Added code to show octants. * * 184 4/22/98 9:58p John * Added code to view invisible faces. * * 183 4/22/98 9:43p John * Added code to allow checking of invisible faces, flagged by any texture * name with invisible in it. * * 182 4/20/98 4:44p John * Fixed problems with black being xparent on model cache rneders. Made * model cache key off of detail level setting and framerate. * * 181 4/18/98 4:00p John * Made highest model caching detail level disable model caching. * * 180 4/14/98 11:11p John * Made ships with < 50% hull left show electrical damage arcs. * * 179 4/13/98 4:54p John * Made uv rotate independently on subspace effect. Put in DCF function * for setting subspace speeds. * * 178 4/12/98 5:54p John * Made models work with subspace. Made subspace rotate also. * * 177 4/12/98 9:56a John * Made lighting detail flags work. Made explosions cast light on * highest. * * 176 4/11/98 6:53p John * Added first rev of subspace effect. * * 175 4/10/98 5:20p John * Changed RGB in lighting structure to be ubytes. Removed old * not-necessary 24 bpp software stuff. * * 174 4/09/98 11:40p John * Fixed some bugs with hardware lighting. * * 173 4/09/98 4:38p John * Made non-darkening and transparent textures work under Glide. Fixed * bug with Jim's computer not drawing any bitmaps. * * $NoKeywords: $ */ #include #define MODEL_LIB #include "pstypes.h" #include "2d.h" #include "3d.h" #include "model.h" #include "tmapper.h" #include "floating.h" #include "fvi.h" #include "lighting.h" #include "modelsinc.h" #include "fireballs.h" #include "fix.h" #include "bmpman.h" #include "systemvars.h" #include "key.h" #include "3dinternal.h" #include "timer.h" #include "grinternal.h" #include "palman.h" #include "object.h" // For MAX_OBJECTS #include "missionparse.h" #include "neb.h" // Some debug variables used externally for displaying stats #ifndef NDEBUG int modelstats_num_polys = 0; int modelstats_num_polys_drawn = 0; int modelstats_num_verts = 0; int modelstats_num_sortnorms = 0; int modelstats_num_boxes = 0; #endif // a lighting object typedef struct model_light_object { ubyte r[MAX_POLYGON_NORMS]; ubyte g[MAX_POLYGON_NORMS]; ubyte b[MAX_POLYGON_NORMS]; int objnum; int skip; int skip_max; } model_light_object; // ----------------------- // Local variables // // Vertices used internally to rotate model points static vertex Interp_points[MAX_POLYGON_VECS]; vector *Interp_verts[MAX_POLYGON_VECS]; static int Interp_num_verts; // ------------------------------------------------------------------- // lighting save stuff // #define MAX_MODEL_LIGHTING_SAVE 30 int hack_skip_max = 1; DCF(skip, "") { dc_get_arg(ARG_INT); hack_skip_max = Dc_arg_int; } // model_light_object Interp_lighting_save[MAX_MODEL_LIGHTING_SAVE]; model_light_object Interp_lighting_temp; model_light_object *Interp_lighting = &Interp_lighting_temp; int Interp_use_saved_lighting = 0; int Interp_saved_lighting_full = 0; // // lighting save stuff // ------------------------------------------------------------------- static ubyte Interp_light_applied[MAX_POLYGON_NORMS]; static vector *Interp_norms[MAX_POLYGON_NORMS]; static int Interp_num_norms = 0; static ubyte *Interp_lights; static float Interp_fog_level = 0.0f; // Stuff to control rendering parameters static color Interp_outline_color; static int Interp_detail_level = 0; static uint Interp_flags = 0; static uint Interp_tmap_flags = 0; // If non-zero, then the subobject gets scaled by Interp_thrust_scale. static int Interp_thrust_scale_subobj=0; static float Interp_thrust_scale = 0.1f; static int Interp_thrust_bitmap = -1; static int Interp_thrust_glow_bitmap = -1; static float Interp_thrust_glow_noise = 1.0f; static int Interp_objnum = -1; // if != -1, use this bitmap when rendering ship insignias static int Interp_insignia_bitmap = -1; // if != -1, use this bitmap when rendering with a forced texture static int Interp_forced_bitmap = -1; // for rendering with the MR_ALL_XPARENT FLAG SET static float Interp_xparent_alpha = 1.0f; float Interp_light = 0.0f; // forward references int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check ); // call at the beginning of a level. after the level has been loaded void model_level_post_init() { /* int idx; // reset lighting stuff for(idx=0; idx 1.0f ) { Interp_thrust_scale = 1.0f; } polymodel * pm = model_get( model_num ); int i; // If thrust is set up, use it. for (i=0; inum_lights; i++ ) { if ( pm->lights[i].type == BSP_LIGHT_TYPE_THRUSTER ) { float scale = (Interp_thrust_scale-0.1f)*0.5f; pm->lights[i].value += (scale+Interp_thrust_glow_noise*0.2f) / 255.0f; } } } // Point list // +0 int id // +4 int size // +8 int n_verts // +12 int n_norms // +16 int offset from start of chunk to vertex data // +20 n_verts*char norm_counts // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals. void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm) { int i, n; int nverts = w(p+8); int offset = w(p+16); int next_norm = 0; ubyte * normcount = p+20; vertex *dest = Interp_points; vector *src = vp(p+offset); // Get pointer to lights Interp_lights = p+20+nverts; Assert( nverts < MAX_POLYGON_VECS ); // Assert( nnorms < MAX_POLYGON_NORMS ); Interp_num_verts = nverts; #ifndef NDEBUG modelstats_num_verts += nverts; #endif /* static int Max_vecs = 0; static int Max_norms = 0; if ( Max_vecs < nverts ) { Max_vecs = nverts; mprintf(( "MAX NORMS = %d\n", Max_norms )); mprintf(( "MAX VECS = %d\n", Max_vecs )); } if ( Max_norms < nnorms ) { Max_norms = nnorms; mprintf(( "MAX NORMS = %d\n", Max_norms )); mprintf(( "MAX VECS = %d\n", Max_vecs )); } */ if (Interp_thrust_scale_subobj) { // Only scale vertices that aren't on the "base" of // the effect. Base is something Adam decided to be // anything under 1.5 meters, hence the 1.5f. float min_thruster_dist = -1.5f; if ( Interp_flags & MR_IS_MISSILE ) { min_thruster_dist = 0.5f; } for (n=0; nxyz.z < min_thruster_dist ) { tmp.xyz.x = src->xyz.x * 1.0f; tmp.xyz.y = src->xyz.y * 1.0f; tmp.xyz.z = src->xyz.z * Interp_thrust_scale; } else { tmp = *src; } g3_rotate_vertex(dest,&tmp); src++; // move to normal for (i=0; i 0.0f){ float theta; float st, ct; // determine theta for this vertex theta = fl_radian(20.0f + Interp_thrust_twist2); st = sin(theta); ct = cos(theta); // twist tmp.xyz.z = (src->xyz.z * ct) - (src->xyz.y * st); tmp.xyz.y = (src->xyz.z * st) + (src->xyz.y * ct); // scale the z a bit tmp.xyz.z += Interp_thrust_twist; } g3_rotate_vertex(dest, &tmp); */ g3_rotate_vertex(dest, src); src++; // move to normal for (i=0; iu = (float)atan2( R.xyz.x, -R.xyz.z) / (2.0f * 3.14159f); if (pnt->u < 0.0) pnt->u += 1.0f; pnt->v = 1.0f - (float)atan2( a, R.xyz.y) / 3.14159f; } */ // Flat Poly // +0 int id // +4 int size // +8 vector normal // +20 vector center // +32 float radius // +36 int nverts // +40 byte red // +41 byte green // +42 byte blue // +43 byte pad // +44 nverts*short*short vertlist, smoothlist void model_interp_flatpoly(ubyte * p,polymodel * pm) { vertex *Interp_list[TMAP_MAX_VERTS]; int nv = w(p+36); if ( nv < 0 ) return; #ifndef NDEBUG modelstats_num_polys++; #endif if (!g3_check_normal_facing(vp(p+20),vp(p+8)) ) return; int i; short * verts = (short *)(p+44); for (i=0;ir = 191; Interp_list[i]->g = 191; Interp_list[i]->b = 191; } else { Interp_list[i]->b = 191; } } else { int vertnum = verts[i*2+0]; int norm = verts[i*2+1]; if ( Interp_flags & MR_NO_SMOOTHING ) { if ( D3D_enabled ) { light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light ); } else { Interp_list[i]->b = light_apply( Interp_verts[vertnum], vp(p+8), Interp_light ); } } else { // if we're not using saved lighting if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) { if ( D3D_enabled ) { light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], vp(p+8), Interp_light ); } else { Interp_lighting->b[norm] = light_apply( Interp_verts[vertnum], Interp_norms[norm], Interp_light ); } Interp_light_applied[norm] = 1; } if ( D3D_enabled ) { Interp_list[i]->r = Interp_lighting->r[norm]; Interp_list[i]->g = Interp_lighting->g[norm]; Interp_list[i]->b = Interp_lighting->b[norm]; } else { Interp_list[i]->b = Interp_lighting->b[norm]; } } } } // HACK!!! FIX ME!!! I'M SLOW!!!! if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { gr_set_color( *(p+40), *(p+41), *(p+42) ); } if ( !(Interp_flags & MR_NO_POLYS)) { if ( D3D_enabled ) { g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB ); } else { g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP ); } } if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET)) { int i, j; if ( Interp_flags & MR_SHOW_OUTLINE ) { gr_set_color_fast( &Interp_outline_color ); } for (i=0; itextures[w(p+40)]<0)) { // Don't draw invisible polygons. if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES)) { return; } else { is_invisible = 1; } } nv = w(p+36); // Tmap_show_layers = 1; #ifndef NDEBUG modelstats_num_polys++; #endif if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)) return; if ( nv < 0 ) return; verts = (model_tmap_vert *)(p+44); for (i=0;iu = verts[i].u; Interp_list[i]->v = verts[i].v; if ( Interp_subspace ) { Interp_list[i]->v += Interp_subspace_offset_u; Interp_list[i]->u += Interp_subspace_offset_v; Interp_list[i]->r = Interp_subspace_r; Interp_list[i]->g = Interp_subspace_g; Interp_list[i]->b = Interp_subspace_b; } else { // if ( !(pm->flags & PM_FLAG_ALLOW_TILING) ) { // Assert(verts[i].u <= 1.0f ); // Assert(verts[i].v <= 1.0f ); // } // Assert( verts[i].normnum == verts[i].vertnum ); if ( Interp_flags & MR_NO_LIGHTING ) { if ( D3D_enabled ) { Interp_list[i]->r = 191; Interp_list[i]->g = 191; Interp_list[i]->b = 191; } else { Interp_list[i]->b = 191; } } else { int vertnum = verts[i].vertnum; int norm = verts[i].normnum; if ( Interp_flags & MR_NO_SMOOTHING ) { if ( D3D_enabled ) { light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light ); } else { Interp_list[i]->b = light_apply( Interp_verts[vertnum], vp(p+8), Interp_light ); } } else { // if we're applying lighting as normal, and not using saved lighting if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) { if ( D3D_enabled ) { light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], Interp_norms[norm], Interp_light ); } else { int li; ubyte l; l = light_apply( Interp_verts[vertnum], Interp_norms[norm], Interp_light ); if ( Detail.lighting > 1 ) { // Add in precalculated muzzle flashes float fl = i2fl(l)/255.0f; ubyte *tmp = &Interp_lights[norm*pm->num_lights]; for ( li=0; linum_lights; li++ ) { fl += i2fl(tmp[li])*pm->lights[li].value; } if ( fl < 0.0f ) { fl = 0.0f; } else if ( fl > 1.0f ) { fl = 1.0f; } l = (ubyte)fl2i(fl*255.0f); } Interp_lighting->b[norm] = l; } Interp_light_applied[norm] = 1; } if ( D3D_enabled ) { Interp_list[i]->r = Interp_lighting->r[norm]; Interp_list[i]->g = Interp_lighting->g[norm]; Interp_list[i]->b = Interp_lighting->b[norm]; } else { Interp_list[i]->b = Interp_lighting->b[norm]; } } } } // Assert(verts[i].u >= 0.0f ); // Assert(verts[i].v >= 0.0f ); } #ifndef NDEBUG modelstats_num_polys_drawn++; #endif if (!(Interp_flags & MR_NO_POLYS) ) { if ( is_invisible ) { gr_set_color( 0, 255, 0 ); g3_draw_poly( nv, Interp_list, 0 ); } else if (Interp_thrust_scale_subobj) { if ((Interp_thrust_bitmap>-1) && (Interp_thrust_scale > 0.0f) && !Pofview_running) { gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f ); g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED ); } else if(!Pofview_running){ if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { gr_set_color( 128, 128, 255 ); } uint tflags = Interp_tmap_flags; tflags &= (~(TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT)); g3_draw_poly( nv, Interp_list, tflags ); } } else { // all textured polys go through here if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED ) { // subspace special case if ( Interp_subspace && D3D_enabled ) { gr_set_bitmap( pm->textures[w(p+40)], GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f ); } // all other textures else { int texture; // if we're rendering a nebula background pof, maybe select a custom texture if((Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0)){ texture = Interp_forced_bitmap; } else { texture = pm->textures[w(p+40)]; } // muzzle flashes draw xparent if(Interp_flags & MR_ALL_XPARENT){ gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha ); } else { gr_set_bitmap( texture ); } } } else { if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { gr_set_color( 128, 128, 128 ); } } if ( Interp_subspace ) { g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT ); } else { if(Interp_flags & MR_ALL_XPARENT){ g3_draw_poly( nv, Interp_list, Interp_tmap_flags ); } else { g3_draw_poly( nv, Interp_list, Interp_tmap_flags|TMAP_FLAG_NONDARKENING ); } } } } if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) ) { if ( Interp_flags & MR_SHOW_OUTLINE ) { gr_set_color_fast( &Interp_outline_color ); } for (i=0; iparent==-1) ) { // gr_set_color(128,128,0); // g3_draw_sphere_ez( &vmd_zero_vector, pm->core_radius ); // } // Draw a red pivot point gr_set_color(128,0,0); g3_draw_sphere_ez(&vmd_zero_vector, 2.0f ); // Draw a green center of mass when drawing the hull if ( submodel && (submodel->parent==-1) ) { gr_set_color(0,128,0); g3_draw_sphere_ez( &pm->center_of_mass, 1.0f ); } if ( submodel ) { // Draw a blue center point gr_set_color(0,0,128); g3_draw_sphere_ez( &submodel->geometric_center, 0.9f ); } // Draw the bounding box int i; vertex pts[8]; if ( submodel ) { for (i=0; i<8; i++ ) { g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] ); } gr_set_color(128,128,128); g3_draw_line( &pts[0], &pts[1] ); g3_draw_line( &pts[1], &pts[2] ); g3_draw_line( &pts[2], &pts[3] ); g3_draw_line( &pts[3], &pts[0] ); g3_draw_line( &pts[4], &pts[5] ); g3_draw_line( &pts[5], &pts[6] ); g3_draw_line( &pts[6], &pts[7] ); g3_draw_line( &pts[7], &pts[4] ); g3_draw_line( &pts[0], &pts[4] ); g3_draw_line( &pts[1], &pts[5] ); g3_draw_line( &pts[2], &pts[6] ); g3_draw_line( &pts[3], &pts[7] ); } else { //for (i=0; i<8; i++ ) { // g3_rotate_vertex( &pts[i], &pm->bounding_box[i] ); //} gr_set_color(0,255,0); int j; for (j=0; j<8; j++ ) { vector bounding_box[8]; // caclulated fron min/max model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max); for (i=0; i<8; i++ ) { g3_rotate_vertex( &pts[i], &bounding_box[i] ); } gr_set_color(128,0,0); g3_draw_line( &pts[0], &pts[1] ); g3_draw_line( &pts[1], &pts[2] ); g3_draw_line( &pts[2], &pts[3] ); g3_draw_line( &pts[3], &pts[0] ); g3_draw_line( &pts[4], &pts[5] ); g3_draw_line( &pts[5], &pts[6] ); g3_draw_line( &pts[6], &pts[7] ); g3_draw_line( &pts[7], &pts[4] ); g3_draw_line( &pts[0], &pts[4] ); g3_draw_line( &pts[1], &pts[5] ); g3_draw_line( &pts[2], &pts[6] ); g3_draw_line( &pts[3], &pts[7] ); } } } // Debug code to show all the paths of a model void model_draw_paths( int model_num ) { int i,j; vector pnt; polymodel * pm; if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) { return; } pm = model_get(model_num); if (pm->n_paths<1){ return; } for (i=0; in_paths; i++ ) { vertex prev_pnt; for (j=0; jpaths[i].nverts; j++ ) { // Rotate point into world coordinates pnt = pm->paths[i].verts[j].pos; // Pnt is now the x,y,z world coordinates of this vert. // For this example, I am just drawing a sphere at that // point. { vertex tmp; g3_rotate_vertex(&tmp,&pnt); if ( pm->paths[i].verts[j].nturrets > 0 ){ gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue } else { gr_set_color( 255, 0, 0 ); } // g3_draw_sphere( &tmp, pm->paths[i].verts[j].radius ); g3_draw_sphere( &tmp, 0.5f ); gr_set_color( 255, 0, 0 ); if (j){ g3_draw_line(&prev_pnt, &tmp); } prev_pnt = tmp; } } } } // docking bay and fighter bay paths void model_draw_bay_paths(int model_num) { int idx, s_idx; vector v1, v2; vertex l1, l2; polymodel *pm = model_get(model_num); if(pm == NULL){ return; } // render docking bay normals gr_set_color(0, 255, 0); for(idx=0; idxn_docks; idx++){ for(s_idx=0; s_idxdocking_bays[idx].num_slots; s_idx++){ v1 = pm->docking_bays[idx].pnt[s_idx]; vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f); // rotate the points g3_rotate_vertex(&l1, &v1); g3_rotate_vertex(&l2, &v2); // draw the point and normal g3_draw_sphere(&l1, 2.0); g3_draw_line(&l1, &l2); } } // render figher bay paths gr_set_color(0, 255, 255); // iterate through the paths that exist in the polymodel, searching for $bayN pathnames for (idx = 0; idxn_paths; idx++) { if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) { for(s_idx=0; s_idxpaths[idx].nverts-1; s_idx++){ v1 = pm->paths[idx].verts[s_idx].pos; v2 = pm->paths[idx].verts[s_idx+1].pos; // rotate and draw g3_rotate_vertex(&l1, &v1); g3_rotate_vertex(&l2, &v2); g3_draw_line(&l1, &l2); } } } } /* // struct that holds the indicies into path information associated with a fighter bay on a capital ship // NOTE: Fighter bay paths are identified by the path_name $bayN (where N is numbered from 1). // Capital ships only have ONE fighter bay on the entire ship #define MAX_SHIP_BAY_PATHS 10 typedef struct ship_bay { int num_paths; // how many paths are associated with the model's fighter bay int paths[MAX_SHIP_BAY_PATHS]; // index into polymodel->paths[] array int arrive_flags; // bitfield, set to 1 when that path number is reserved for an arrival int depart_flags; // bitfield, set to 1 when that path number is reserved for a departure } ship_bay; typedef struct mp_vert { vector pos; // xyz coordinates of vertex in object's frame of reference int nturrets; // number of turrets guarding this vertex int *turret_ids; // array of indices into ship_subsys linked list (can't index using [] though) float radius; // How far the closest obstruction is from this vertex } mp_vert; typedef struct model_path { char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF int parent_submodel; int nverts; mp_vert *verts; int goal; // Which of the verts is the one closest to the goal of this path int type; // What this path takes you to... See MP_TYPE_??? defines above for details int value; // This depends on the type. // For MP_TYPE_UNUSED, this means nothing. // For MP_TYPE_SUBSYS, this is the subsystem number this path takes you to. } model_path; */ void interp_render_arc_segment( vector *v1, vector *v2, int depth ) { float d = vm_vec_dist_quick( v1, v2 ); if ( d < 0.30f || (depth>4) ) { vertex p1, p2; g3_rotate_vertex( &p1, v1 ); g3_rotate_vertex( &p2, v2 ); //g3_draw_rod( v1, 0.2f, v2, 0.2f, NULL, 0); g3_draw_line( &p1, &p2 ); } else { // divide in half vector tmp; vm_vec_avg( &tmp, v1, v2 ); float scaler = 0.30f; tmp.xyz.x += (frand()-0.5f)*d*scaler; tmp.xyz.y += (frand()-0.5f)*d*scaler; tmp.xyz.z += (frand()-0.5f)*d*scaler; interp_render_arc_segment( v1, &tmp, depth+1 ); interp_render_arc_segment( &tmp, v2, depth+1 ); } } int Interp_lightning = 1; DCF_BOOL( Arcs, Interp_lightning ) int AR = 64; int AG = 64; int AB = 5; int AR2 = 128; int AG2 = 128; int AB2 = 10; void interp_render_lightning( polymodel *pm, bsp_info * sm ) { Assert( sm->num_arcs > 0 ); int i; if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) { return; } if (!Interp_lightning) return; // if ( keyd_pressed[KEY_LSHIFT] ) return; // if ( rad < 3.0f ) return; for (i=0; inum_arcs; i++ ) { // pick a color based upon arc type switch(sm->arc_type[i]){ // "normal", Freespace 1 style arcs case MARC_TYPE_NORMAL: if ( (rand()>>4) & 1 ) { gr_set_color( 64, 64, 255 ); } else { gr_set_color( 128, 128, 255 ); } break; // "EMP" style arcs case MARC_TYPE_EMP: if ( (rand()>>4) & 1 ) { gr_set_color( AR, AG, AB ); } else { gr_set_color( AR2, AG2, AB2 ); } break; default: Int3(); } // render the actual arc segment interp_render_arc_segment( &sm->arc_pts[i][0], &sm->arc_pts[i][1], 0 ); } } void model_interp_subcall(polymodel * pm, int mn, int detail_level) { int i; int zbuf_mode = gr_zbuffering_mode; if ( (mn < 0) || (mn>=pm->n_models) ) return; Assert( mn >= 0 ); Assert( mn < pm->n_models ); // mprintf(( "Name = '%s'\n", pm->submodel[mn].name )); // char * p = pm->submodel[mn].name; if (pm->submodel[mn].blown_off){ return; } if (pm->submodel[mn].is_thruster ) { if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){ return; } Interp_thrust_scale_subobj=1; } else { Interp_thrust_scale_subobj=0; } g3_start_instance_angles(&pm->submodel[mn].offset, &pm->submodel[mn].angs); if ( !(Interp_flags & MR_NO_LIGHTING ) ) { light_rotate_all(); } model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 ); if (Interp_flags & MR_SHOW_PIVOTS ) model_draw_debug_points( pm, &pm->submodel[mn] ); if ( pm->submodel[mn].num_arcs ) { interp_render_lightning( pm, &pm->submodel[mn]); } i = pm->submodel[mn].first_child; while( i>-1 ) { if (!pm->submodel[i].is_thruster ) { if(Interp_flags & MR_NO_ZBUFFER){ zbuf_mode = GR_ZBUFF_NONE; } else { zbuf_mode = GR_ZBUFF_FULL; // read only } gr_zbuffer_set(zbuf_mode); model_interp_subcall( pm, i, detail_level ); } i = pm->submodel[i].next_sibling; } g3_done_instance(); } // Returns one of the following #define IBOX_ALL_OFF 0 #define IBOX_ALL_ON 1 #define IBOX_SOME_ON_SOME_OFF 2 int interp_box_offscreen( vector *min, vector *max ) { if ( keyd_pressed[KEY_LSHIFT] ) { return IBOX_ALL_ON; } vector v[8]; v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z; v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z; v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z; v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z; v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z; v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z; v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z; v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z; ubyte and_codes = 0xff; ubyte or_codes = 0xff; int i; for (i=0; i<8; i++ ) { vertex tmp; ubyte codes=g3_rotate_vertex( &tmp, &v[i] ); // Early out which we cannot do because we want to differentiate btwn // IBOX_SOME_ON_SOME_OFF and IBOX_ALL_ON // if ( !codes ) { // //mprintf(( "A point is inside, so render it.\n" )); // return 0; // this point is in, so return 0 // } or_codes |= codes; and_codes &= codes; } // If and_codes is set this means that all points lie off to the // same side of the screen. if (and_codes) { //mprintf(( "All points offscreen, so don't render it.\n" )); return IBOX_ALL_OFF; //all points off screen } // If this is set it means at least one of the points is offscreen, // but they aren't all off to the same side. if (or_codes) { return IBOX_SOME_ON_SOME_OFF; } // They are all onscreen. return IBOX_ALL_ON; } //calls the object interpreter to render an object. //returns true if drew int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check ) { ubyte *p = (ubyte *)model_ptr; int chunk_type, chunk_size; int pushed = 0; chunk_type = w(p); chunk_size = w(p+4); while ( chunk_type != OP_EOF ) { // mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size )); switch (chunk_type) { case OP_EOF: return 1; case OP_DEFPOINTS: model_interp_defpoints(p,pm,sm); break; case OP_FLATPOLY: model_interp_flatpoly(p,pm); break; case OP_TMAPPOLY: model_interp_tmappoly(p,pm); break; case OP_SORTNORM: model_interp_sortnorm(p,pm,sm,do_box_check); break; case OP_BOUNDBOX: if ( do_box_check ) { int retval = interp_box_offscreen( vp(p+8), vp(p+20) ); switch( retval ) { case IBOX_ALL_OFF: goto DoneWithThis; // Don't need to draw any more polys from this box break; case IBOX_ALL_ON: do_box_check = 0; // Don't need to check boxes any more break; case IBOX_SOME_ON_SOME_OFF: // continue like we were break; default: Int3(); } } if (Interp_flags & MR_SHOW_PIVOTS ) { #ifndef NDEBUG modelstats_num_boxes++; #endif interp_draw_box( vp(p+8), vp(p+20) ); } if ( !(Interp_flags & MR_NO_LIGHTING ) ) { if ( pushed ) { light_filter_pop(); pushed = 0; } light_filter_push_box( vp(p+8), vp(p+20) ); pushed = 1; } break; default: mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size )); Int3(); // Bad chunk type! return 0; } p += chunk_size; chunk_type = w(p); chunk_size = w(p+4); } DoneWithThis: if ( !(Interp_flags & MR_NO_LIGHTING ) ) { if ( pushed ) { light_filter_pop(); pushed = 0; } } return 1; } void model_render_shields( polymodel * pm ) { int i, j; shield_tri *tri; vertex pnt0, tmp, prev_pnt; if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) { return; } gr_set_color(0, 0, 200 ); // Scan all the triangles in the mesh. for (i=0; ishield.ntris; i++ ) { tri = &pm->shield.tris[i]; if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) { // Process the vertices. // Note this rotates each vertex each time it's needed, very dumb. for (j=0; j<3; j++ ) { g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos ); if (j) g3_draw_line(&prev_pnt, &tmp); else pnt0 = tmp; prev_pnt = tmp; } g3_draw_line(&pnt0, &prev_pnt); } } } void model_render_insignias(polymodel *pm, int detail_level) { int idx, s_idx; vertex vecs[3]; vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] }; vector t1, t2, t3; int i1, i2, i3; // if the model has no insignias we're done if(pm->num_ins <= 0){ return; } // set the proper texture if(Interp_insignia_bitmap >= 0){ gr_set_bitmap(Interp_insignia_bitmap); } // otherwise don't even bother rendering else { return; } // otherwise render them for(idx=0; idxnum_ins; idx++){ // skip insignias not on our detail level if(pm->ins[idx].detail_level != detail_level){ continue; } for(s_idx=0; s_idxins[idx].num_faces; s_idx++){ // get vertex indices i1 = pm->ins[idx].faces[s_idx][0]; i2 = pm->ins[idx].faces[s_idx][1]; i3 = pm->ins[idx].faces[s_idx][2]; // transform vecs and setup vertices vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset); vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset); vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset); g3_rotate_vertex(&vecs[0], &t1); g3_rotate_vertex(&vecs[1], &t2); g3_rotate_vertex(&vecs[2], &t3); // setup texture coords vecs[0].u = pm->ins[idx].u[s_idx][0]; vecs[0].v = pm->ins[idx].v[s_idx][0]; vecs[1].u = pm->ins[idx].u[s_idx][1]; vecs[1].v = pm->ins[idx].v[s_idx][1]; vecs[2].u = pm->ins[idx].u[s_idx][2]; vecs[2].v = pm->ins[idx].v[s_idx][2]; // draw the polygon g3_draw_poly(3, vlist, TMAP_FLAG_TEXTURED); } } } int Model_texturing = 1; int Model_polys = 1; DCF_BOOL( model_texturing, Model_texturing ) DCF_BOOL( model_polys, Model_polys ) MONITOR( NumModelsRend ); MONITOR( NumHiModelsRend ); MONITOR( NumMedModelsRend ); MONITOR( NumLowModelsRend ); typedef struct model_cache { int model_num; //matrix orient; vector pos; int num_lights; float last_dot; float cr; int w, h; ubyte *data; int cached_valid; int bitmap_id; angles angs; // thrust stuff float thrust_scale; int thrust_bitmap; int thrust_glow_bitmap; float thrust_glow_noise; int last_frame_rendered; // last frame in which this model was rendered not from the cache } model_cache; #define MAX_MODEL_CACHE MAX_OBJECTS model_cache Model_cache[MAX_MODEL_CACHE]; // Indexed by objnum int Model_cache_inited = 0; // Returns 0 if not valid points int model_cache_calc_coords(vector *pnt,float rad, float *cx, float *cy, float *cr) { vertex pt; ubyte flags; flags = g3_rotate_vertex(&pt,pnt); if (flags == 0) { g3_project_vertex(&pt); if (!(pt.flags & (PF_OVERFLOW|CC_BEHIND))) { *cx = pt.sx; *cy = pt.sy; *cr = rad*Matrix_scale.xyz.x*Canv_w2/pt.z; if ( *cr < 1.0f ) { *cr = 1.0f; } int x1, x2, y1, y2; x1 = fl2i(*cx-*cr); if ( x1 < gr_screen.clip_left ) return 0; x2 = fl2i(*cx+*cr); if ( x2 > gr_screen.clip_right ) return 0; y1 = fl2i(*cy-*cr); if ( y1 < gr_screen.clip_top ) return 0; y2 = fl2i(*cy+*cr); if ( y2 > gr_screen.clip_bottom ) return 0; return 1; } } return 0; } void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id ); //draws a bitmap with the specified 3d width & height //returns 1 if off screen, 0 if not int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v) { float sa, ca; int i; Assert( G3_count == 1 ); // angle = 0.0f; sa = (float)sin(angle); ca = (float)cos(angle); float width, height; width = height = rad; v[0].x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x; v[0].y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y; v[0].z = pnt->z; v[0].sw = 0.0f; v[0].u = 0.0f; v[0].v = 0.0f; v[1].x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x; v[1].y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y; v[1].z = pnt->z; v[1].sw = 0.0f; v[1].u = 1.0f; v[1].v = 0.0f; v[2].x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x; v[2].y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y; v[2].z = pnt->z; v[2].sw = 0.0f; v[2].u = 1.0f; v[2].v = 1.0f; v[3].x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x; v[3].y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y; v[3].z = pnt->z; v[3].sw = 0.0f; v[3].u = 0.0f; v[3].v = 1.0f; ubyte codes_and=0xff; float sw,z; z = pnt->z - rad / 4.0f; if ( z < 0.0f ) z = 0.0f; sw = 1.0f / z; for (i=0; i<4; i++ ) { //now code the four points codes_and &= g3_code_vertex(&v[i]); v[i].flags = 0; // mark as not yet projected g3_project_vertex(&v[i]); v[i].sw = sw; } if (codes_and) return 1; //1 means off screen return 0; } int Model_caching = 1; DCF_BOOL( model_caching, Model_caching ); extern int Tmap_scan_read; // 0 = normal mapper, 1=read, 2=write #define MODEL_MAX_BITMAP_SIZE 128 ubyte tmp_bitmap[MODEL_MAX_BITMAP_SIZE*MODEL_MAX_BITMAP_SIZE]; void mc_get_bmp( ubyte *data, int x, int y, int w, int h ) { gr_lock(); int i,j; for (i = 0; i < h; i++) { ubyte *dptr = GR_SCREEN_PTR(ubyte,x,i+y); ubyte *sptr = data+(i*w); for (j=0; j v2 ) return max; v = (v - v1)/(v2-v1); v = v*(max-min)+min; return v; } void model_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int lighting_skip ) { polymodel *pm = model_get(model_num); // maybe turn off (hardware) culling if(flags & MR_NO_CULL){ gr_set_cull(0); } Interp_objnum = objnum; if ( flags & MR_NO_LIGHTING ) { Interp_light = 1.0f; // never use saved lighitng for this object model_set_saved_lighting(-1, -1); } else if ( flags & MR_IS_ASTEROID ) { // Dim it based on distance float depth = vm_vec_dist_quick( pos, &Eye_position ); if ( depth > Interp_depth_scale ) { Interp_light = Interp_depth_scale/depth; // If it is too far, exit if ( Interp_light < (1.0f/32.0f) ) { Interp_light = 0.0f; return; } else if ( Interp_light > 1.0f ) { Interp_light = 1.0f; } } else { Interp_light = 1.0f; } // never use saved lighitng for this object model_set_saved_lighting(-1, -1); } else { Interp_light = 1.0f; // maybe use saved lighting model_set_saved_lighting(objnum, hack_skip_max); } int num_lights = 0; if ( !(flags & MR_NO_LIGHTING ) ) { if ( D3D_enabled ) { num_lights = light_filter_push( objnum, pos, pm->rad ); } else { num_lights = light_filter_push( objnum, pos, pm->rad ); } } model_try_cache_render(model_num, orient, pos, flags, objnum, num_lights ); if ( !(flags & MR_NO_LIGHTING ) ) { light_filter_pop(); } // maybe turn culling back on if(flags & MR_NO_CULL){ gr_set_cull(1); } // turn off fog after each model renders if(The_mission.flags & MISSION_FLAG_FULLNEB){ gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); } } void model_cache_init() { if ( !Model_cache_inited ) { int i; Model_cache_inited = 1; for (i=0; i-1) && (objnum 3) ) { if ( mc ) { mc->cached_valid = 0; } model_really_render(model_num, orient, pos, flags, objnum ); return; } Assert( mc != NULL ); // Fake the detail level based on framerate. if ( 1.0f / flFrametime < Mc_framerate_lo[Model_object_caching_tmp] ) { Model_object_caching_tmp--; // mprintf(( "Model cache level bumped down to %d\n", Model_object_caching )); } else if ( 1.0f / flFrametime > Mc_framerate_hi[Model_object_caching_tmp] ) { Model_object_caching_tmp++; // mprintf(( "Model cache level bumped up to %d\n", Model_object_caching )); } int tmp_detail_level = Model_object_caching_tmp + Mc_detail_add[Detail.object_caching]; if ( tmp_detail_level < 0 ) { tmp_detail_level = 0; } else if (tmp_detail_level > MAX_DETAIL_LEVEL ) { tmp_detail_level = MAX_DETAIL_LEVEL; } if ( tmp_detail_level > 3 ) { if ( mc ) { mc->cached_valid = 0; } model_really_render(model_num, orient, pos, flags, objnum ); return; } // static int last_one = -1; // if ( last_one != tmp_detail_level ) { // last_one = tmp_detail_level; // mprintf(( "Detail level %d\n", tmp_detail_level )); // } // if ( keyd_pressed[KEY_LSHIFT] ) { // mc->cached_valid = 0; // model_really_render(model_num, orient, pos, flags, objnum ); // return; // } // mprintf(( "Rendering cache model\n" )); polymodel *pm = model_get(model_num); vertex v[4]; vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] }; float cx, cy, cr; vertex pt; ubyte ccflags; matrix tempm, tempm2; angles new_angles; vm_copy_transpose_matrix(&tempm2,orient); vm_matrix_x_matrix(&tempm,&tempm2,&Eye_matrix); vm_extract_angles_matrix(&new_angles, &tempm ); if ( !model_cache_calc_coords(pos,pm->rad, &cx, &cy, &cr) ) { // Not onscreen, do a real render and exit mc->cached_valid = 0; model_really_render(model_num, orient, pos, flags, objnum ); return; } //================================================================ // A bunch of checks to see if we need to redraw the model or not vector ship_to_eye; vm_vec_sub( &ship_to_eye, &Eye_position, pos ); vm_vec_normalize_safe(&ship_to_eye); float this_dot = vm_vec_dot( &ship_to_eye, &orient->fvec ); this_dot += vm_vec_dot( &ship_to_eye, &orient->rvec ); float diff = 0.0f; if ( !mc->cached_valid ) { // Nothing cached goto RedrawIt; } Assert( mc->data != NULL ); if (Framecount - mc->last_frame_rendered > 1 + 2*(MAX_DETAIL_LEVEL - Detail.object_caching - 1)) { goto RedrawIt; } diff = fl_abs( this_dot - mc->last_dot ); if ( diff > Mc_viewer_pos_factor[tmp_detail_level] ) { // mprintf(( "Redraw!!! %.4f\n", diff )); goto RedrawIt; } // if ( keyd_pressed[KEY_LSHIFT] ) { // goto RedrawIt; // } if (tmp_detail_level > 2) { if ( mc->thrust_glow_bitmap != Interp_thrust_glow_bitmap ) { // Engline glow bitmap changed // mprintf(( "MC: Glow bitmap changed! %d -> %d\n", mc->thrust_glow_bitmap, Interp_thrust_glow_bitmap )); goto RedrawIt; } } if (tmp_detail_level > 2) { if ( cr > 4.0f ) { float diff = fl_abs( mc->thrust_scale - Interp_thrust_scale ); if ( diff > 0.1f ) { // Thruster size has changed //mprintf(( "MC: Thruster size changed! %.2f -> %.2f\n", mc->thrust_scale, Interp_thrust_scale )); goto RedrawIt; } } } // if (0) { // float diff = fl_abs( mc->thrust_glow_noise - Interp_thrust_glow_noise ); // if ( diff > 0.1f ) { // Glow noise has changed //mprintf(( "MC: Thruster glow changed! %.2f -> %.2f\n", mc->thrust_glow_noise, Interp_thrust_glow_noise )); // goto RedrawIt; // } // } if ( mc->model_num != model_num ) { // Model changed goto RedrawIt; } if ( cr>mc->cr*Mc_size_factor[tmp_detail_level] ) { // Scaling up too far goto RedrawIt; } if (tmp_detail_level > 2) { if ( cr > 4.0f ) { if ( !(Interp_flags & MR_NO_LIGHTING ) ) { if (mc->num_lights != num_lights) { // Lighting changed goto RedrawIt; } } } } // This method is correct, but rotating ship makes things redraw which is too slow. #if 0 if ( cr > 4.0f ) { // Check orientation float angle_error = max( fl_abs( mc->angs.p-new_angles.p ),fl_abs( mc->angs.h-new_angles.h )); // Exact //if ( angle_error > 0.075f ) { // Rough if ( angle_error > 0.40f ) { // Ship/view turned too much //mprintf(( "Ship/view turned too much %.4f\n", angle_error )); goto RedrawIt; } } #endif // mprintf(( "Dot = %.5f\n", dot )); #if 0 if (0) { float dx, dy, dz; dx = vm_vec_dot( &orient->rvec, &mc->orient.rvec )+1.0f; dy = vm_vec_dot( &orient->uvec, &mc->orient.uvec )+1.0f; dz = vm_vec_dot( &orient->fvec, &mc->orient.fvec )+1.0f; float angle_error = (dx+dy+dz)*1000.0f/6.0f; //mprintf(( "Angle_error = %.4f\n", angle_error )); // Compare it to 999.75f at R = 64.0f // 0.0000f at R = 0.0f float cmp_val = 999.75f; // old // if ( is_asteroid ) { // cmp_val = scale_it( 0.0f, 999.75f, cr, 0.0f, 64.0f ); // } if ( angle_error < cmp_val ) { // Ship turned too much goto RedrawIt; } } #endif // Have a valid cache entry, mc ccflags = g3_rotate_vertex(&pt,pos); if ( ccflags ) { // offscreen goto RedrawIt; } if ( model_get_rotated_bitmap_points(&pt,mc->angs.b - new_angles.b, pm->rad, v )) { // offscreen goto RedrawIt; } gr_set_bitmap( mc->bitmap_id ); Tmap_scan_read = 2; g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED ); Tmap_scan_read = 0; // if ( keyd_pressed[KEY_LSHIFT] ) { // gr_set_color( 255, 0, 0 ); // gr_pixel( fl2i(v[0].sx), fl2i(v[0].sy) ); // } //if ( keyd_pressed[KEY_RSHIFT] ) { // gr_line( fl2i(v[0].sx), fl2i(v[0].sy), fl2i(v[1].sx), fl2i(v[1].sy) ); // gr_line( fl2i(v[1].sx), fl2i(v[1].sy), fl2i(v[2].sx), fl2i(v[2].sy) ); // gr_line( fl2i(v[2].sx), fl2i(v[2].sy), fl2i(v[3].sx), fl2i(v[3].sy) ); // gr_line( fl2i(v[3].sx), fl2i(v[3].sy), fl2i(v[0].sx), fl2i(v[0].sy) ); //} return; //========================================================== // Cache is bad for model, so draw it and save it RedrawIt: // if ( mc->data != NULL ) { // free(mc->data); // mc->data = NULL; // } if ( mc->bitmap_id != -1 ) { bm_release(mc->bitmap_id); mc->bitmap_id = -1; } mc->cached_valid = 0; mc->model_num = model_num; mc->pos = *pos; //mc->orient = *orient; mc->cr = cr; mc->angs = new_angles; //-Physics_viewer_bank; mc->thrust_scale = Interp_thrust_scale; mc->thrust_bitmap = Interp_thrust_bitmap; mc->thrust_glow_bitmap = Interp_thrust_glow_bitmap; mc->thrust_glow_noise = Interp_thrust_glow_noise; mc->last_dot = this_dot; if ( cr > MODEL_MAX_BITMAP_SIZE/2-1 ) goto JustDrawIt; //Physics_viewer_bank ccflags = g3_rotate_vertex(&pt,pos); if ( ccflags ) { goto JustDrawIt; } model_get_rotated_bitmap_points(&pt,0.0f, pm->rad, v ); int x1, y1, x2, y2, w, h; x1 = fl_round_2048( v[0].sx ); y1 = fl_round_2048( v[0].sy ); x2 = fl_round_2048( v[2].sx ); //+0.5f ); y2 = fl_round_2048( v[2].sy ); //+0.5f ); if ( x1 < gr_screen.clip_left) goto JustDrawIt; if ( y1 < gr_screen.clip_top ) goto JustDrawIt; if ( x2 > gr_screen.clip_right) goto JustDrawIt; if ( y2 > gr_screen.clip_bottom) goto JustDrawIt; w = x2 - x1 + 1; if ( w < 0 ) Int3(); if ( w < 2 ) w = 2; h = y2 - y1 + 1; if ( h < 0 ) Int3(); if ( h < 2 ) h = 2; if ( w > MODEL_MAX_BITMAP_SIZE ) goto JustDrawIt; if ( h > MODEL_MAX_BITMAP_SIZE ) goto JustDrawIt; mc->w = w; mc->h = h; // mprintf(( "Mallocing a %dx%d bitmap\n", w, h )); if ( mc->data == NULL ) { mc->data = (ubyte *)malloc( MODEL_MAX_BITMAP_SIZE * MODEL_MAX_BITMAP_SIZE ); } // mprintf(( "Done mallocing a %dx%d bitmap\n", w, h )); if ( mc->data == NULL ) { goto JustDrawIt; } for (i = 0; i < w*h; i++) { mc->data[i] = 255; } mc->bitmap_id = bm_create( 8, mc->w, mc->h, mc->data, 0 ); if ( mc->bitmap_id < 0 ) { goto JustDrawIt; } // Save stars and stuff on screen mc_get_bmp( tmp_bitmap, x1, y1, w, h ); mc->num_lights = num_lights; // Didn't render a cached one... so render it and then save it in the cache // Turn on stippling model_really_render(model_num, orient, pos, flags, objnum ); // Save screen to bitmap gr_set_bitmap( mc->bitmap_id ); Tmap_scan_read = 1; g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED ); Tmap_scan_read = 0; // Restore stars and stuff to screen mc_put_bmp( tmp_bitmap, x1, y1, w, h ); // Draw the model gr_set_bitmap( mc->bitmap_id ); Tmap_scan_read = 2; g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED ); Tmap_scan_read = 0; mc->cached_valid = 1; mc->last_frame_rendered = Framecount; return; JustDrawIt: // Too big to save model_really_render(model_num, orient, pos, flags, objnum ); */ } // Find the distance from p0 to the closest point on a box. // The box's dimensions from 'min' to 'max'. float interp_closest_dist_to_box( vector *hitpt, vector *p0, vector *min, vector *max ) { float *origin = (float *)&p0->xyz.x; float *minB = (float *)min; float *maxB = (float *)max; float *coord = (float *)&hitpt->xyz.x; int inside = 1; int i; for (i=0; i<3; i++ ) { if ( origin[i] < minB[i] ) { coord[i] = minB[i]; inside = 0; } else if (origin[i] > maxB[i] ) { coord[i] = maxB[i]; inside = 0; } else { coord[i] = origin[i]; } } if ( inside ) { return 0.0f; } return vm_vec_dist(hitpt,p0); } // Finds the closest point on a model to a point in space. Actually only finds a point // on the bounding box of the model. // Given: // model_num Which model // orient Orientation of the model // pos Position of the model // eye_pos Point that you want to find the closest point to // Returns: // distance from eye_pos to closest_point. 0 means eye_pos is // on or inside the bounding box. // Also fills in outpnt with the actual closest point. float model_find_closest_point( vector *outpnt, int model_num, int submodel_num, matrix *orient, vector * pos, vector *eye_pos ) { vector closest_pos, tempv, eye_rel_pos; polymodel *pm = model_get(model_num); if ( submodel_num < 0 ) { submodel_num = pm->detail[0]; } // Rotate eye pos into object coordinates vm_vec_sub(&tempv,pos,eye_pos ); vm_vec_rotate(&eye_rel_pos,&tempv,orient ); return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max ); } int tiling = 1; DCF(tiling, "") { tiling = !tiling; if(tiling){ dc_printf("Tiled textures\n"); } else { dc_printf("Non-tiled textures\n"); } } #ifndef NO_DIRECT3D extern void d3d_zbias(int bias); #endif void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id ) { int i, detail_level; polymodel * pm; uint save_gr_zbuffering_mode; int zbuf_mode; MONITOR_INC( NumModelsRend, 1 ); Interp_orient = orient; Interp_pos = pos; int tmp_detail_level = Game_detail_level; // if ( D3D_enabled ) { // tmp_detail_level = -1; // Force no hires models for Direct3D // } // Tmap_show_layers = 1; // model_set_detail_level(0); // flags |= MR_LOCK_DETAIL|MR_NO_TEXTURING|MR_NO_LIGHTING; //MR_LOCK_DETAIL | |MR_NO_LIGHTING|MR_NO_SMOOTHINGMR_NO_TEXTURING | // Turn off engine effect Interp_thrust_scale_subobj=0; if (!Model_texturing) flags |= MR_NO_TEXTURING; if ( !Model_polys ) { flags |= MR_NO_POLYS; } Interp_flags = flags; pm = model_get(model_num); // Set the flags we will pass to the tmapper if ( D3D_enabled ) { Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB; } else { Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP; } // if we're in nebula mode if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){ Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG; } if ( !(Interp_flags & MR_NO_TEXTURING) ) { Interp_tmap_flags |= TMAP_FLAG_TEXTURED; if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling) Interp_tmap_flags |= TMAP_FLAG_TILED; if ( !(Interp_flags & MR_NO_CORRECT) ) { Interp_tmap_flags |= TMAP_FLAG_CORRECT; } } save_gr_zbuffering_mode = gr_zbuffering_mode; zbuf_mode = gr_zbuffering_mode; if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) { gr_set_color(0,128,0); g3_draw_sphere_ez( pos, pm->rad ); return; } g3_start_instance_matrix(pos,orient); if ( Interp_flags & MR_SHOW_RADIUS ) { if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { gr_set_color(0,64,0); g3_draw_sphere_ez(&vmd_zero_vector,pm->rad); } } Assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS ); vector closest_pos; float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position ); if ( pm->n_detail_levels > 1 ) { if ( Interp_flags & MR_LOCK_DETAIL ) { i = Interp_detail_level+1; } else { //gr_set_color(0,128,0); //g3_draw_sphere_ez( &closest_pos, 2.0f ); #if MAX_DETAIL_LEVEL != 4 #error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4 #endif switch( Detail.detail_distance ) { case 0: // lowest depth *= 8.0f; break; case 1: // lower than normal depth *= 4.0f; break; case 2: // default (leave the same) break; case 3: // above normal depth /= 4.0f; break; case 4: // even more normal depth /= 8.0f; break; } // nebula ? if(The_mission.flags & MISSION_FLAG_FULLNEB){ depth *= neb2_get_lod_scale(Interp_objnum); } for ( i=0; in_detail_levels; i++ ) { if ( depth<=pm->detail_depth[i] ){ break; } } // If no valid detail depths specified, use highest. if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f)) { i = 1; } } // maybe force lower detail if (Interp_flags & MR_FORCE_LOWER_DETAIL) { i++; } //detail_level = fl2i(depth/10.0f); //detail_level = 0; detail_level = i-1-tmp_detail_level; if ( detail_level < 0 ) detail_level = 0; else if (detail_level >= pm->n_detail_levels ) detail_level = pm->n_detail_levels-1; //mprintf(( "Depth = %.2f, detail = %d\n", depth, detail_level )); } else { detail_level = 0; } #ifndef NDEBUG if ( detail_level==0 ) { MONITOR_INC( NumHiModelsRend, 1 ); } else if ( detail_level ==pm->n_detail_levels-1 ) { MONITOR_INC( NumLowModelsRend, 1 ); } else { MONITOR_INC( NumMedModelsRend, 1 ); } #endif if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){ vector auto_back = pm->autocenter; vm_vec_scale(&auto_back, -1.0f); g3_start_instance_matrix(&auto_back, NULL); } #ifndef NO_DIRECT3D if(gr_screen.mode == GR_DIRECT3D){ d3d_zbias(1); } #endif // Draw the subobjects i = pm->submodel[pm->detail[detail_level]].first_child; while( i>-1 ) { if (!pm->submodel[i].is_thruster ) { zbuf_mode = GR_ZBUFF_WRITE; // no zbuffering if(Interp_flags & MR_NO_ZBUFFER){ zbuf_mode = GR_ZBUFF_NONE; } gr_zbuffer_set(zbuf_mode); model_interp_subcall( pm, i, detail_level ); } i = pm->submodel[i].next_sibling; } // rotate lights for the hull if ( !(Interp_flags & MR_NO_LIGHTING ) ) { light_rotate_all(); } if ( pm->submodel[pm->detail[detail_level]].num_children > 0 ){ // zbuf_mode |= GR_ZBUFF_WRITE; // write only zbuf_mode = GR_ZBUFF_FULL; } // no zbuffering if(Interp_flags & MR_NO_ZBUFFER){ zbuf_mode = GR_ZBUFF_NONE; } gr_zbuffer_set(zbuf_mode); #ifndef NO_DIRECT3D if(gr_screen.mode == GR_DIRECT3D){ d3d_zbias(0); } #endif // draw the hull of the ship model_interp_sub( (ubyte *)pm->submodel[pm->detail[detail_level]].bsp_data, pm, &pm->submodel[pm->detail[detail_level]], 0 ); if (Interp_flags & MR_SHOW_PIVOTS ) { model_draw_debug_points( pm, NULL ); model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]] ); if(pm->flags & PM_FLAG_AUTOCEN){ gr_set_color(255, 255, 255); g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f); } } if ( pm->submodel[pm->detail[0]].num_arcs ) { interp_render_lightning( pm, &pm->submodel[pm->detail[0]]); } if ( Interp_flags & MR_SHOW_SHIELDS ) { model_render_shields(pm); } #ifndef NO_DIRECT3D // render model insignias if(gr_screen.mode == GR_DIRECT3D){ d3d_zbias(1); } #endif gr_zbuffer_set(GR_ZBUFF_READ); model_render_insignias(pm, detail_level); #ifndef NO_DIRECT3D // zbias back to 0 if(gr_screen.mode == GR_DIRECT3D){ d3d_zbias(0); } #endif //start rendering glow points -Bobboau if ( (pm->n_glows) /*&& (Interp_flags & MR_SHOW_THRUSTERS) /*&& (Detail.engine_glows)*/ ) { for (i = 0; i < pm->n_glows; i++ ) { glow_bank *bank = &pm->glows[i]; int j; for ( j=0; jnum_slots; j++ ) { float d; vector tempv; vm_vec_sub(&tempv,&View_position,&bank->pnt[j]); vm_vec_normalize(&tempv); d = vm_vec_dot(&tempv,&bank->norm[j]); if ( d > 0.0f) { vertex p; // Make glow bitmap fade in/out quicker from sides. d *= 3.0f; if ( d > 1.0f ) d = 1.0f; // fade them in the nebula as well if(The_mission.flags & MISSION_FLAG_FULLNEB){ d *= (1.0f - Interp_fog_level); } float w = bank->radius[j]; // disable fogging gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); g3_rotate_vertex( &p, &bank->pnt[j] ); gr_set_bitmap( bank->glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, d ); { extern int Gr_scaler_zbuffering; Gr_scaler_zbuffering = 1; g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED ); //g3_draw_rotated_bitmap(&p,0.0f,w,w, TMAP_FLAG_TEXTURED ); Gr_scaler_zbuffering = 0; } } } } } //end rendering glow points // Draw the thruster glow if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) /*&& (Detail.engine_glows)*/ ) { for (i = 0; i < pm->n_thrusters; i++ ) { thruster_bank *bank = &pm->thrusters[i]; int j; for ( j=0; jnum_slots; j++ ) { float d; vector tempv; vm_vec_sub(&tempv,&View_position,&bank->pnt[j]); vm_vec_normalize(&tempv); d = vm_vec_dot(&tempv,&bank->norm[j]); if ( d > 0.0f) { vertex p; // Make glow bitmap fade in/out quicker from sides. d *= 3.0f; if ( d > 1.0f ) d = 1.0f; // fade them in the nebula as well if(The_mission.flags & MISSION_FLAG_FULLNEB){ d *= (1.0f - Interp_fog_level); } //ADAM: Min throttle draws rad*MIN_SCALE, max uses max. #define NOISE_SCALE 0.5f #define MIN_SCALE 3.4f #define MAX_SCALE 4.7f float scale = MIN_SCALE; scale = (Interp_thrust_scale-0.1f)*(MAX_SCALE-MIN_SCALE)+MIN_SCALE; float w = bank->radius[j]*(scale+Interp_thrust_glow_noise*NOISE_SCALE ); // disable fogging gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); g3_rotate_vertex( &p, &bank->pnt[j] ); gr_set_bitmap( Interp_thrust_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, d ); { extern int Gr_scaler_zbuffering; Gr_scaler_zbuffering = 1; g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED ); //g3_draw_rotated_bitmap(&p,0.0f,w,w, TMAP_FLAG_TEXTURED ); Gr_scaler_zbuffering = 0; } } } } } Interp_thrust_glow_bitmap = -1; gr_set_cull(0); // Draw the thruster subobjects i = pm->submodel[pm->detail[detail_level]].first_child; while( i>-1 ) { if (pm->submodel[i].is_thruster ) { zbuf_mode = GR_ZBUFF_READ; // no zbuffering if(Interp_flags & MR_NO_ZBUFFER){ zbuf_mode = GR_ZBUFF_NONE; } gr_zbuffer_set(zbuf_mode); model_interp_subcall( pm, i, detail_level ); } i = pm->submodel[i].next_sibling; } gr_set_cull(1); if ( Interp_flags & MR_SHOW_PATHS ){ model_draw_paths(model_num); } if (Interp_flags & MR_BAY_PATHS ){ model_draw_bay_paths(model_num); } if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){ g3_done_instance(); } g3_done_instance(); gr_zbuffer_set(save_gr_zbuffering_mode); } void submodel_render(int model_num, int submodel_num, matrix *orient, vector * pos, uint flags, int light_ignore_id) { polymodel * pm; MONITOR_INC( NumModelsRend, 1 ); if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return; // Turn off engine effect Interp_thrust_scale_subobj=0; if (!Model_texturing) flags |= MR_NO_TEXTURING; Interp_flags = flags; pm = model_get(model_num); // Set the flags we will pass to the tmapper if ( D3D_enabled ) { Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB; } else { Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP; } // if we're in nebula mode if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){ Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG; } if ( !(Interp_flags & MR_NO_TEXTURING) ) { Interp_tmap_flags |= TMAP_FLAG_TEXTURED; if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling ) Interp_tmap_flags |= TMAP_FLAG_TILED; if ( !(Interp_flags & MR_NO_CORRECT) ) { Interp_tmap_flags |= TMAP_FLAG_CORRECT; } } if ( !(Interp_flags & MR_NO_LIGHTING ) ) { if ( D3D_enabled ) { light_filter_push( -1, pos, pm->submodel[submodel_num].rad ); } else { light_filter_push( light_ignore_id, pos, pm->submodel[submodel_num].rad ); } } g3_start_instance_matrix(pos,orient); if ( !(Interp_flags & MR_NO_LIGHTING ) ) { light_rotate_all(); } model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 ); if ( pm->submodel[submodel_num].num_arcs ) { interp_render_lightning( pm, &pm->submodel[submodel_num]); } if (Interp_flags & MR_SHOW_PIVOTS ) model_draw_debug_points( pm, &pm->submodel[submodel_num] ); if ( !(Interp_flags & MR_NO_LIGHTING ) ) { light_filter_pop(); } g3_done_instance(); } // Fills in an array with points from a model. // Only gets up to max_num verts; // Returns number of verts found; static int submodel_get_points_internal(int model_num, int submodel_num, int max_num, vector **pnts, vector **norms ) { polymodel * pm; pm = model_get(model_num); if ( submodel_num < 0 ) { submodel_num = pm->detail[0]; } ubyte *p = pm->submodel[submodel_num].bsp_data; int chunk_type, chunk_size; chunk_type = w(p); chunk_size = w(p+4); while (chunk_type != OP_EOF) { switch (chunk_type) { case OP_EOF: return 1; case OP_DEFPOINTS: { int n; int nverts = w(p+8); int offset = w(p+16); ubyte * normcount = p+20; vector *src = vp(p+offset); if ( nverts > max_num ) nverts = max_num; for (n=0; n>5) % nv; int vn2 = (myrand()>>5) % nv; *v1 = *Interp_verts[vn1]; *v2 = *Interp_verts[vn2]; if(n1 != NULL){ *n1 = *Interp_norms[vn1]; } if(n2 != NULL){ *n2 = *Interp_norms[vn2]; } } // If MR_FLAG_OUTLINE bit set this color will be used for outlines. // This defaults to black. void model_set_outline_color(int r, int g, int b ) { gr_init_color( &Interp_outline_color, r, g, b ); } // If MR_FLAG_OUTLINE bit set this color will be used for outlines. // This defaults to black. void model_set_outline_color_fast(void *outline_color) { Interp_outline_color = *((color*)(outline_color)); } // IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n' // This defaults to 0. (0=highest, larger=lower) void model_set_detail_level(int n) { Interp_detail_level = n; } // Returns number of verts in a submodel; int submodel_get_num_verts(int model_num, int submodel_num ) { polymodel * pm; pm = model_get(model_num); ubyte *p = pm->submodel[submodel_num].bsp_data; int chunk_type, chunk_size; chunk_type = w(p); chunk_size = w(p+4); while (chunk_type != OP_EOF) { switch (chunk_type) { case OP_EOF: return 0; case OP_DEFPOINTS: { int n=w(p+8); return n; // Read in 'n' points } break; case OP_FLATPOLY: break; case OP_TMAPPOLY: break; case OP_SORTNORM: break; case OP_BOUNDBOX: break; default: mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size )); Int3(); // Bad chunk type! return 0; } p += chunk_size; chunk_type = w(p); chunk_size = w(p+4); } return 0; // Couldn't find 'em } // Returns number of tmaps & flat polys in a submodel; int submodel_get_num_polys_sub( ubyte *p ) { int chunk_type = w(p); int chunk_size = w(p+4); int n = 0; while (chunk_type != OP_EOF) { switch (chunk_type) { case OP_EOF: return n; case OP_DEFPOINTS: break; case OP_FLATPOLY: n++; break; case OP_TMAPPOLY: n++; break; case OP_SORTNORM: { int frontlist = w(p+36); int backlist = w(p+40); int prelist = w(p+44); int postlist = w(p+48); int onlist = w(p+52); n += submodel_get_num_polys_sub(p+frontlist); n += submodel_get_num_polys_sub(p+backlist); n += submodel_get_num_polys_sub(p+prelist); n += submodel_get_num_polys_sub(p+postlist ); n += submodel_get_num_polys_sub(p+onlist ); } break; case OP_BOUNDBOX: break; default: mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size )); Int3(); // Bad chunk type! return 0; } p += chunk_size; chunk_type = w(p); chunk_size = w(p+4); } return n; } // Returns number of tmaps & flat polys in a submodel; int submodel_get_num_polys(int model_num, int submodel_num ) { polymodel * pm; pm = model_get(model_num); return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data ); } // Sets the submodel instance data in a submodel // If show_damaged is true it shows only damaged submodels. // If it is false it shows only undamaged submodels. void model_show_damaged(int model_num, int show_damaged ) { polymodel * pm; int i; pm = model_get(model_num); for (i=0; in_models; i++ ) { bsp_info *sm = &pm->submodel[i]; // Set the "blown out" flags sm->blown_off = 0; } for (i=0; in_models; i++ ) { bsp_info *sm = &pm->submodel[i]; // Set the "blown out" flags if ( show_damaged ) { if ( sm->my_replacement > -1 ) { pm->submodel[sm->my_replacement].blown_off = 0; sm->blown_off = 1; } } else { if ( sm->my_replacement > -1 ) { pm->submodel[sm->my_replacement].blown_off = 1; sm->blown_off = 0; } } } } // set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether) void model_set_insignia_bitmap(int bmap) { Interp_insignia_bitmap = bmap; } // set the forces bitmap void model_set_forced_texture(int bmap) { Interp_forced_bitmap = bmap; } // set model transparency for use with MR_ALL_XPARENT void model_set_alpha(float alpha) { Interp_xparent_alpha = alpha; } // see if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error int model_find_texture(int model_num, int bitmap) { polymodel * pm; int idx; // get a handle to the model pm = model_get(model_num); if(pm == NULL){ return -1; } // find the texture for(idx=0; idxn_textures; idx++){ if(pm->textures[idx] == bitmap){ return 1; } } // no texture return 0; } // find closest point on extended bounding box (the bounding box plus all the planes that make it up) // returns closest distance to extended box // positive return value means start_point is outside extended box // displaces closest point an optional amount delta to the outside of the box // closest_box_point can be NULL. float get_model_closest_box_point_with_delta(vector *closest_box_point, vector *start_point, int modelnum, int *is_inside, float delta) { int i, idx; vector box_point, ray_direction, *extremes; float dist, best_dist; polymodel *pm; int inside = 0; int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20}; int mask_inside = 0x3f; best_dist = FLT_MAX; pm = model_get(modelnum); for (i=0; i<6; i++) { idx = i / 2; // which row vector of Identity matrix memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vector)); // do negative, then positive plane for each axis if (2 * idx == i) { extremes = &pm->mins; vm_vec_negate(&ray_direction); } else { extremes = &pm->maxs; } // a negative distance means started outside the box dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f); if (dist > 0) { inside |= masks[i]; } if (fabs(dist) < fabs(best_dist)) { best_dist = dist; if (closest_box_point) { vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta); } } } // is start_point inside the box if (is_inside) { *is_inside = (inside == mask_inside); } return -best_dist; } // find closest point on extended bounding box (the bounding box plus all the planes that make it up) // returns closest distance to extended box // positive return value means start_point is outside extended box // displaces closest point an optional amount delta to the outside of the box // closest_box_point can be NULL. float get_world_closest_box_point_with_delta(vector *closest_box_point, object *box_obj, vector *start_point, int *is_inside, float delta) { vector temp, box_start; float dist; int modelnum; // get modelnum modelnum = Ships[box_obj->instance].modelnum; // rotate start_point to box_obj RF vm_vec_sub(&temp, start_point, &box_obj->pos); vm_vec_rotate(&box_start, &temp, &box_obj->orient); dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta); // rotate closest_box_point to world RF if (closest_box_point) { vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient); vm_vec_add(closest_box_point, &temp, &box_obj->pos); } return dist; } void model_set_fog_level(float l) { Interp_fog_level = l; } // given a newly loaded model, page in all textures void model_page_in_textures(int modelnum, int ship_info_index) { int idx; ship_info *sip; // valid ship type? if((ship_info_index < 0) || (ship_info_index >= Num_ship_types)){ return; } sip = &Ship_info[ship_info_index]; polymodel *pm = model_get(modelnum); // bogus if(pm == NULL){ return; } // set nondarkening pixels if(sip->num_nondark_colors){ palman_set_nondarkening(sip->nondark_colors, sip->num_nondark_colors); } // use the colors from the default table else { palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default); } for (idx=0; idxn_textures; idx++ ){ int bitmap_num = pm->original_textures[idx]; if ( bitmap_num > -1 ) { // if we're in Glide (and maybe later with D3D), use nondarkening textures if(gr_screen.mode == GR_GLIDE){ bm_lock(bitmap_num, 16, BMP_TEX_NONDARK); bm_unlock(bitmap_num); } else { bm_lock(bitmap_num, 16, BMP_TEX_OTHER); bm_unlock(bitmap_num); } } } } // is the given model a pirate ship? int model_is_pirate_ship(int modelnum) { return 0; }