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.7 2004/07/04 11:39:06 taylor
19 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
21 * Revision 1.6 2003/05/25 02:30:43 taylor
24 * Revision 1.5 2002/06/17 06:33:09 relnev
25 * ryan's struct patch for gcc 2.95
27 * Revision 1.4 2002/06/09 04:41:23 relnev
28 * added copyright header
30 * Revision 1.3 2002/06/01 03:32:00 relnev
31 * fix texture loading mistake.
33 * enable some d3d stuff for opengl also
35 * Revision 1.2 2002/05/07 03:16:46 theoddone33
36 * The Great Newline Fix
38 * Revision 1.1.1.1 2002/05/03 03:28:10 root
42 * 37 9/13/99 11:25p Dave
43 * Fixed problem with mode-switching and D3D movies.
45 * 36 9/13/99 10:09a Andsager
46 * Add debug console commands to lower model render detail and fireball
47 * LOD for big ship explosiosns.
49 * 35 9/08/99 12:03a Dave
50 * Make squad logos render properly in D3D all the time. Added intel anim
53 * 34 9/01/99 10:09a Dave
56 * 33 8/30/99 5:01p Dave
57 * Made d3d do less state changing in the nebula. Use new chat server for
60 * 32 8/24/99 8:55p Dave
61 * Make sure nondimming pixels work properly in tech menu.
63 * 31 7/29/99 10:47p Dave
64 * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
66 * 30 7/29/99 12:05a Dave
67 * Nebula speed optimizations.
69 * 29 7/27/99 3:09p Dave
70 * Made g400 work. Whee.
72 * 28 7/24/99 4:19p Dave
73 * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much
74 * better. Made model code use zbuffer more intelligently.
76 * 27 7/19/99 7:20p Dave
77 * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
80 * 26 7/18/99 5:20p Dave
81 * Jump node icon. Fixed debris fogging. Framerate warning stuff.
83 * 25 7/15/99 2:13p Dave
84 * Added 32 bit detection.
86 * 24 6/22/99 7:03p Dave
87 * New detail options screen.
89 * 23 6/18/99 5:16p Dave
90 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
91 * dialog to PXO screen.
93 * 22 5/26/99 11:46a Dave
94 * Added ship-blasting lighting and made the randomization of lighting
95 * much more customizable.
97 * 21 5/24/99 5:45p Dave
98 * Added detail levels to the nebula, with a decent speedup. Split nebula
99 * lightning into its own section.
101 * 20 5/12/99 10:05a Johnson
102 * DKA: Fix fred bug from engine wash
104 * 19 5/08/99 8:25p Dave
105 * Upped object pairs. First run of nebula lightning.
107 * 18 4/26/99 8:49p Dave
108 * Made all pof based nebula stuff full customizable through fred.
110 * 17 4/23/99 5:53p Dave
111 * Started putting in new pof nebula support into Fred.
113 * 16 4/20/99 3:30p Andsager
114 * Let get_model_closest_box_point_with_delta() take NULL as pointer to
117 * 15 4/19/99 12:51p Andsager
118 * Add function to find the nearest point on extneded bounding box and
119 * check if inside bounding box.
121 * 14 3/31/99 8:24p Dave
122 * Beefed up all kinds of stuff, incluging beam weapons, nebula effects
123 * and background nebulae. Added per-ship non-dimming pixel colors.
125 * 13 3/24/99 6:14p Dave
126 * Added position and orientation checksumming for multiplayer. Fixed LOD
127 * rendering bugs for squad insignias
129 * 12 3/23/99 5:17p Dave
130 * Changed model file format somewhat to account for LOD's on insignias
132 * 11 3/19/99 9:51a Dave
133 * Checkin to repair massive source safe crash. Also added support for
134 * pof-style nebulae, and some new weapons code.
136 * 10 3/08/99 7:03p Dave
137 * First run of new object update system. Looks very promising.
139 * 9 3/02/99 9:25p Dave
140 * Added a bunch of model rendering debug code. Started work on fixing
141 * beam weapon wacky firing.
143 * 8 2/19/99 11:42a Dave
144 * Put in model rendering autocentering.
146 * 7 1/14/99 6:06p Dave
147 * 100% full squad logo support for single player and multiplayer.
149 * 6 1/08/99 2:08p Dave
150 * Fixed software rendering for pofview. Super early support for AWACS and
153 * 5 1/06/99 2:24p Dave
154 * Stubs and release build fixes.
156 * 4 12/09/98 7:34p Dave
157 * Cleanup up nebula effect. Tweaked many values.
159 * 3 12/06/98 2:36p Dave
160 * Drastically improved nebula fogging.
162 * 2 10/07/98 10:53a Dave
165 * 1 10/07/98 10:50a Dave
167 * 193 8/28/98 3:29p Dave
168 * EMP effect done. AI effects may need some tweaking as required.
170 * 192 6/13/98 3:18p Hoffoss
171 * NOX()ed out a bunch of strings that shouldn't be translated.
173 * 191 5/24/98 4:53p John
174 * changed rounding for model caching to get rid of some empty lines in
177 * 190 5/16/98 4:47p John
178 * Put back in my version 188 changes that someone so rudely (but
179 * hopefully unintentionally) deleted.
181 * 189 5/13/98 11:34p Mike
182 * Model caching system.
184 * 187 5/12/98 11:32a Mike
185 * Support for weapon pof detail levels.
187 * 186 5/08/98 1:32p John
188 * Added code for using two layered subspace effects.
190 * 185 4/29/98 11:03a John
191 * Added code to show octants.
193 * 184 4/22/98 9:58p John
194 * Added code to view invisible faces.
196 * 183 4/22/98 9:43p John
197 * Added code to allow checking of invisible faces, flagged by any texture
198 * name with invisible in it.
200 * 182 4/20/98 4:44p John
201 * Fixed problems with black being xparent on model cache rneders. Made
202 * model cache key off of detail level setting and framerate.
204 * 181 4/18/98 4:00p John
205 * Made highest model caching detail level disable model caching.
207 * 180 4/14/98 11:11p John
208 * Made ships with < 50% hull left show electrical damage arcs.
210 * 179 4/13/98 4:54p John
211 * Made uv rotate independently on subspace effect. Put in DCF function
212 * for setting subspace speeds.
214 * 178 4/12/98 5:54p John
215 * Made models work with subspace. Made subspace rotate also.
217 * 177 4/12/98 9:56a John
218 * Made lighting detail flags work. Made explosions cast light on
221 * 176 4/11/98 6:53p John
222 * Added first rev of subspace effect.
224 * 175 4/10/98 5:20p John
225 * Changed RGB in lighting structure to be ubytes. Removed old
226 * not-necessary 24 bpp software stuff.
228 * 174 4/09/98 11:40p John
229 * Fixed some bugs with hardware lighting.
231 * 173 4/09/98 4:38p John
232 * Made non-darkening and transparent textures work under Glide. Fixed
233 * bug with Jim's computer not drawing any bitmaps.
246 #include "floating.h"
248 #include "lighting.h"
249 #include "modelsinc.h"
250 #include "fireballs.h"
253 #include "systemvars.h"
255 #include "3dinternal.h"
257 #include "grinternal.h"
259 #include "object.h" // For MAX_OBJECTS
260 #include "missionparse.h"
264 // Some debug variables used externally for displaying stats
266 int modelstats_num_polys = 0;
267 int modelstats_num_polys_drawn = 0;
268 int modelstats_num_verts = 0;
269 int modelstats_num_sortnorms = 0;
270 int modelstats_num_boxes = 0;
274 typedef struct model_light_object {
275 ubyte r[MAX_POLYGON_NORMS];
276 ubyte g[MAX_POLYGON_NORMS];
277 ubyte b[MAX_POLYGON_NORMS];
281 } model_light_object;
283 // -----------------------
287 // Vertices used internally to rotate model points
288 static vertex Interp_points[MAX_POLYGON_VECS];
289 vector *Interp_verts[MAX_POLYGON_VECS];
290 static int Interp_num_verts;
293 // -------------------------------------------------------------------
294 // lighting save stuff
296 #define MAX_MODEL_LIGHTING_SAVE 30
297 int hack_skip_max = 1;
301 hack_skip_max = Dc_arg_int;
303 // model_light_object Interp_lighting_save[MAX_MODEL_LIGHTING_SAVE];
304 model_light_object Interp_lighting_temp;
305 model_light_object *Interp_lighting = &Interp_lighting_temp;
306 int Interp_use_saved_lighting = 0;
307 int Interp_saved_lighting_full = 0;
309 // lighting save stuff
310 // -------------------------------------------------------------------
313 static ubyte Interp_light_applied[MAX_POLYGON_NORMS];
314 static vector *Interp_norms[MAX_POLYGON_NORMS];
315 static int Interp_num_norms = 0;
316 static ubyte *Interp_lights;
318 static float Interp_fog_level = 0.0f;
320 // Stuff to control rendering parameters
321 static color Interp_outline_color;
322 static int Interp_detail_level = 0;
323 static uint Interp_flags = 0;
324 static uint Interp_tmap_flags = 0;
326 // If non-zero, then the subobject gets scaled by Interp_thrust_scale.
327 static int Interp_thrust_scale_subobj=0;
328 static float Interp_thrust_scale = 0.1f;
329 static int Interp_thrust_bitmap = -1;
330 static int Interp_thrust_glow_bitmap = -1;
331 static float Interp_thrust_glow_noise = 1.0f;
333 static int Interp_objnum = -1;
335 // if != -1, use this bitmap when rendering ship insignias
336 static int Interp_insignia_bitmap = -1;
338 // if != -1, use this bitmap when rendering with a forced texture
339 static int Interp_forced_bitmap = -1;
341 // for rendering with the MR_ALL_XPARENT FLAG SET
342 static float Interp_xparent_alpha = 1.0f;
344 float Interp_light = 0.0f;
346 // forward references
347 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check );
349 // call at the beginning of a level. after the level has been loaded
350 void model_level_post_init()
355 // reset lighting stuff
356 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
357 Interp_lighting_save[idx].objnum = -1;
358 Interp_lighting_save[idx].skip = 0;
361 // saved lighting is not full
362 Interp_saved_lighting_full = 0;
366 // call to select an object for using "saved" lighting
367 void model_set_saved_lighting(int objnum, int skip_max)
372 // always set to not using saved light to start with
373 Interp_use_saved_lighting = 0;
374 Interp_lighting = &Interp_lighting_temp;
376 // if he passed a -1 for either value, no saved lighting
377 if((objnum == -1) || (skip_max == -1)){
381 // see if the object is already on the list
382 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
383 // ahha, he is on the list
384 if(Interp_lighting_save[idx].objnum == objnum){
385 // if he's entered a new skip max
386 if(Interp_lighting_save[idx].skip_max != skip_max){
387 Interp_lighting_save[idx].skip = 0;
388 Interp_lighting_save[idx].skip_max = skip_max;
389 Interp_use_saved_lighting = 0;
390 Interp_lighting = &Interp_lighting_save[idx];
392 // if we're reached the "skip" frame, re-render lighting for this guy
393 else if(Interp_lighting_save[idx].skip == Interp_lighting_save[idx].skip_max){
394 Interp_lighting_save[idx].skip = 0;
395 Interp_use_saved_lighting = 0;
396 Interp_lighting = &Interp_lighting_save[idx];
398 // otherwise, use his saved lighting values
400 Interp_lighting_save[idx].skip++;
401 Interp_use_saved_lighting = 1;
402 Interp_lighting = &Interp_lighting_save[idx];
410 // no free saved lighting slots
411 if(Interp_saved_lighting_full){
417 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
419 if(Interp_lighting_save[idx].objnum == -1){
420 Interp_lighting_save[idx].objnum = objnum;
421 Interp_lighting_save[idx].skip = 0;
422 Interp_lighting_save[idx].skip_max = skip_max;
424 Interp_use_saved_lighting = 0;
425 Interp_lighting = &Interp_lighting_save[idx];
432 // oops. out of free slots
434 Interp_saved_lighting_full = 1;
439 // notify the model system that a ship has died
440 void model_notify_dead_ship(int objnum)
445 // see if this guy was on the lighting list
446 for(idx=0; idx<MAX_MODEL_LIGHTING_SAVE; idx++){
448 if(objnum == Interp_lighting_save[idx].objnum){
449 Interp_lighting_save[idx].objnum = -1;
450 Interp_saved_lighting_full = 0;
457 void interp_clear_instance()
459 Interp_thrust_scale = 0.1f;
460 Interp_thrust_bitmap = -1;
461 Interp_thrust_glow_bitmap = -1;
462 Interp_thrust_glow_noise = 1.0f;
463 Interp_insignia_bitmap = -1;
466 // Scales the engines thrusters by this much
467 void model_set_thrust( int model_num, float length, int bitmap, int glow_bitmap, float glow_noise )
469 Interp_thrust_scale = length;
470 Interp_thrust_bitmap = bitmap;
471 Interp_thrust_glow_bitmap = glow_bitmap;
472 Interp_thrust_glow_noise = glow_noise;
474 if ( Interp_thrust_scale < 0.1f ) {
475 Interp_thrust_scale = 0.1f;
476 } else if ( Interp_thrust_scale > 1.0f ) {
477 Interp_thrust_scale = 1.0f;
480 polymodel * pm = model_get( model_num );
483 // If thrust is set up, use it.
484 for (i=0; i<pm->num_lights; i++ ) {
485 if ( pm->lights[i].type == BSP_LIGHT_TYPE_THRUSTER ) {
486 float scale = (Interp_thrust_scale-0.1f)*0.5f;
488 pm->lights[i].value += (scale+Interp_thrust_glow_noise*0.2f) / 255.0f;
499 // +16 int offset from start of chunk to vertex data
500 // +20 n_verts*char norm_counts
501 // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
502 void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm)
506 int offset = w(p+16);
509 ubyte * normcount = p+20;
510 vertex *dest = Interp_points;
511 vector *src = vp(p+offset);
513 // Get pointer to lights
514 Interp_lights = p+20+nverts;
516 Assert( nverts < MAX_POLYGON_VECS );
517 // Assert( nnorms < MAX_POLYGON_NORMS );
519 Interp_num_verts = nverts;
521 modelstats_num_verts += nverts;
525 static int Max_vecs = 0;
526 static int Max_norms = 0;
528 if ( Max_vecs < nverts ) {
530 mprintf(( "MAX NORMS = %d\n", Max_norms ));
531 mprintf(( "MAX VECS = %d\n", Max_vecs ));
534 if ( Max_norms < nnorms ) {
536 mprintf(( "MAX NORMS = %d\n", Max_norms ));
537 mprintf(( "MAX VECS = %d\n", Max_vecs ));
541 if (Interp_thrust_scale_subobj) {
543 // Only scale vertices that aren't on the "base" of
544 // the effect. Base is something Adam decided to be
545 // anything under 1.5 meters, hence the 1.5f.
546 float min_thruster_dist = -1.5f;
548 if ( Interp_flags & MR_IS_MISSILE ) {
549 min_thruster_dist = 0.5f;
552 for (n=0; n<nverts; n++ ) {
555 Interp_verts[n] = src;
557 // Only scale vertices that aren't on the "base" of
558 // the effect. Base is something Adam decided to be
559 // anything under 1.5 meters, hence the 1.5f.
560 if ( src->xyz.z < min_thruster_dist ) {
561 tmp.xyz.x = src->xyz.x * 1.0f;
562 tmp.xyz.y = src->xyz.y * 1.0f;
563 tmp.xyz.z = src->xyz.z * Interp_thrust_scale;
568 g3_rotate_vertex(dest,&tmp);
570 src++; // move to normal
572 for (i=0; i<normcount[n]; i++ ) {
573 Interp_light_applied[next_norm] = 0;
574 Interp_norms[next_norm] = src;
583 for (n=0; n<nverts; n++ ) {
584 Interp_verts[n] = src;
589 if(Interp_thrust_twist > 0.0f){
593 // determine theta for this vertex
594 theta = fl_radian(20.0f + Interp_thrust_twist2);
599 tmp.xyz.z = (src->xyz.z * ct) - (src->xyz.y * st);
600 tmp.xyz.y = (src->xyz.z * st) + (src->xyz.y * ct);
603 tmp.xyz.z += Interp_thrust_twist;
606 g3_rotate_vertex(dest, &tmp);
609 g3_rotate_vertex(dest, src);
611 src++; // move to normal
613 for (i=0; i<normcount[n]; i++ ) {
614 Interp_light_applied[next_norm] = 0;
615 Interp_norms[next_norm] = src;
624 Interp_num_norms = next_norm;
628 matrix *Interp_orient;
632 void interp_compute_environment_mapping( vector *nrm, vertex * pnt, vector *vert)
636 matrix * m = &View_matrix;
638 vm_vec_rotate( &R, nrm, &View_matrix );
639 vm_vec_normalize(&R);
642 R.xyz.x = a * R.xyz.x; // reflected R = 2N(N.E) -E; E = eye
643 R.xyz.y = a * R.xyz.y;
644 R.xyz.z = a * R.xyz.z;
645 vm_vec_normalize(&R);
646 a = (float)fl_sqrt( 1.0f - R.xyz.y * R.xyz.y);
647 pnt->u = (float)atan2( R.xyz.x, -R.xyz.z) / (2.0f * 3.14159f);
648 if (pnt->u < 0.0) pnt->u += 1.0f;
649 pnt->v = 1.0f - (float)atan2( a, R.xyz.y) / 3.14159f;
665 // +44 nverts*short*short vertlist, smoothlist
666 void model_interp_flatpoly(ubyte * p,polymodel * pm)
668 vertex *Interp_list[TMAP_MAX_VERTS];
671 if ( nv < 0 ) return;
674 modelstats_num_polys++;
677 if (!g3_check_normal_facing(vp(p+20),vp(p+8)) ) return;
681 short * verts = (short *)(p+44);
684 Interp_list[i] = &Interp_points[verts[i*2]];
686 if ( Interp_flags & MR_NO_LIGHTING ) {
688 Interp_list[i]->r = 191;
689 Interp_list[i]->g = 191;
690 Interp_list[i]->b = 191;
692 Interp_list[i]->b = 191;
695 int vertnum = verts[i*2+0];
696 int norm = verts[i*2+1];
698 if ( Interp_flags & MR_NO_SMOOTHING ) {
700 light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
702 Interp_list[i]->b = light_apply( Interp_verts[vertnum], vp(p+8), Interp_light );
705 // if we're not using saved lighting
706 if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
708 light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], vp(p+8), Interp_light );
710 Interp_lighting->b[norm] = light_apply( Interp_verts[vertnum], Interp_norms[norm], Interp_light );
712 Interp_light_applied[norm] = 1;
716 Interp_list[i]->r = Interp_lighting->r[norm];
717 Interp_list[i]->g = Interp_lighting->g[norm];
718 Interp_list[i]->b = Interp_lighting->b[norm];
720 Interp_list[i]->b = Interp_lighting->b[norm];
726 // HACK!!! FIX ME!!! I'M SLOW!!!!
727 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
728 gr_set_color( *(p+40), *(p+41), *(p+42) );
731 if ( !(Interp_flags & MR_NO_POLYS)) {
733 g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB );
735 g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP );
739 if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET)) {
742 if ( Interp_flags & MR_SHOW_OUTLINE ) {
743 gr_set_color_fast( &Interp_outline_color );
746 for (i=0; i<nv; i++ ) {
748 g3_draw_line(Interp_list[i], Interp_list[j] );
761 // +44 nverts*(model_tmap_vert) vertlist (n,u,v)
762 extern int Tmap_show_layers;
764 int Interp_subspace = 0;
765 float Interp_subspace_offset_u = 0.0f;
766 float Interp_subspace_offset_v = 0.0f;
767 ubyte Interp_subspace_r = 255;
768 ubyte Interp_subspace_g = 255;
769 ubyte Interp_subspace_b = 255;
771 void model_interp_tmappoly(ubyte * p,polymodel * pm)
773 vertex *Interp_list[TMAP_MAX_VERTS];
776 model_tmap_vert *verts;
778 int is_invisible = 0;
780 if ((!Interp_thrust_scale_subobj) && (pm->textures[w(p+40)]<0)) {
781 // Don't draw invisible polygons.
782 if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES)) {
791 // Tmap_show_layers = 1;
794 modelstats_num_polys++;
797 if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)) return;
799 if ( nv < 0 ) return;
801 verts = (model_tmap_vert *)(p+44);
804 Interp_list[i] = &Interp_points[verts[i].vertnum];
806 Interp_list[i]->u = verts[i].u;
807 Interp_list[i]->v = verts[i].v;
809 if ( Interp_subspace ) {
810 Interp_list[i]->v += Interp_subspace_offset_u;
811 Interp_list[i]->u += Interp_subspace_offset_v;
812 Interp_list[i]->r = Interp_subspace_r;
813 Interp_list[i]->g = Interp_subspace_g;
814 Interp_list[i]->b = Interp_subspace_b;
817 // if ( !(pm->flags & PM_FLAG_ALLOW_TILING) ) {
818 // Assert(verts[i].u <= 1.0f );
819 // Assert(verts[i].v <= 1.0f );
822 // Assert( verts[i].normnum == verts[i].vertnum );
824 if ( Interp_flags & MR_NO_LIGHTING ) {
826 Interp_list[i]->r = 191;
827 Interp_list[i]->g = 191;
828 Interp_list[i]->b = 191;
830 Interp_list[i]->b = 191;
833 int vertnum = verts[i].vertnum;
834 int norm = verts[i].normnum;
836 if ( Interp_flags & MR_NO_SMOOTHING ) {
838 light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
840 Interp_list[i]->b = light_apply( Interp_verts[vertnum], vp(p+8), Interp_light );
843 // if we're applying lighting as normal, and not using saved lighting
844 if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
847 light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], Interp_norms[norm], Interp_light );
852 l = light_apply( Interp_verts[vertnum], Interp_norms[norm], Interp_light );
855 if ( Detail.lighting > 1 ) {
856 // Add in precalculated muzzle flashes
857 float fl = i2fl(l)/255.0f;
858 ubyte *tmp = &Interp_lights[norm*pm->num_lights];
860 for ( li=0; li<pm->num_lights; li++ ) {
861 fl += i2fl(tmp[li])*pm->lights[li].value;
866 } else if ( fl > 1.0f ) {
870 l = (ubyte)fl2i(fl*255.0f);
874 Interp_lighting->b[norm] = l;
878 Interp_light_applied[norm] = 1;
882 Interp_list[i]->r = Interp_lighting->r[norm];
883 Interp_list[i]->g = Interp_lighting->g[norm];
884 Interp_list[i]->b = Interp_lighting->b[norm];
886 Interp_list[i]->b = Interp_lighting->b[norm];
892 // Assert(verts[i].u >= 0.0f );
893 // Assert(verts[i].v >= 0.0f );
897 modelstats_num_polys_drawn++;
900 if (!(Interp_flags & MR_NO_POLYS) ) {
901 if ( is_invisible ) {
902 gr_set_color( 0, 255, 0 );
903 g3_draw_poly( nv, Interp_list, 0 );
904 } else if (Interp_thrust_scale_subobj) {
905 if ((Interp_thrust_bitmap>-1) && (Interp_thrust_scale > 0.0f) && !Pofview_running) {
906 gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f );
907 g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );
908 } else if(!Pofview_running){
909 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
910 gr_set_color( 128, 128, 255 );
912 uint tflags = Interp_tmap_flags;
913 tflags &= (~(TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT));
914 g3_draw_poly( nv, Interp_list, tflags );
917 // all textured polys go through here
918 if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED ) {
919 // subspace special case
920 if ( Interp_subspace && D3D_enabled ) {
921 gr_set_bitmap( pm->textures[w(p+40)], GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f );
923 // all other textures
927 // if we're rendering a nebula background pof, maybe select a custom texture
928 if((Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0)){
929 texture = Interp_forced_bitmap;
931 texture = pm->textures[w(p+40)];
934 // muzzle flashes draw xparent
935 if(Interp_flags & MR_ALL_XPARENT){
936 gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha );
938 gr_set_bitmap( texture );
942 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
943 gr_set_color( 128, 128, 128 );
947 if ( Interp_subspace ) {
948 g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT );
950 if(Interp_flags & MR_ALL_XPARENT){
951 g3_draw_poly( nv, Interp_list, Interp_tmap_flags );
953 g3_draw_poly( nv, Interp_list, Interp_tmap_flags|TMAP_FLAG_NONDARKENING );
959 if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) ) {
961 if ( Interp_flags & MR_SHOW_OUTLINE ) {
962 gr_set_color_fast( &Interp_outline_color );
965 for (i=0; i<nv; i++ ) {
966 int j = (i + 1) % nv;
967 g3_draw_line(Interp_list[i], Interp_list[j] );
973 void interp_draw_box( vector *min, vector *max )
977 vector bounding_box[8];
981 model_calc_bound_box(bounding_box,min,max);
983 for (i=0; i<8; i++ ) {
984 g3_rotate_vertex( &pts[i], &bounding_box[i] );
987 gr_set_color(128,0,0);
989 Tmap_show_layers = 1;
991 l[3] = &pts[0]; l[2] = &pts[1]; l[1] = &pts[2]; l[0] = &pts[3];
992 g3_draw_poly( 4, l, 0 );
994 l[3] = &pts[3]; l[2] = &pts[2]; l[1] = &pts[6]; l[0] = &pts[7];
995 g3_draw_poly( 4, l, 0 );
997 l[3] = &pts[2]; l[2] = &pts[1]; l[1] = &pts[5]; l[0] = &pts[6];
998 g3_draw_poly( 4, l, 0 );
1000 l[3] = &pts[1]; l[2] = &pts[0]; l[1] = &pts[4]; l[0] = &pts[5];
1001 g3_draw_poly( 4, l, 0 );
1003 l[3] = &pts[0]; l[2] = &pts[3]; l[1] = &pts[7]; l[0] = &pts[4];
1004 g3_draw_poly( 4, l, 0 );
1006 l[3] = &pts[4]; l[2] = &pts[7]; l[1] = &pts[6]; l[0] = &pts[5];
1007 g3_draw_poly( 4, l, 0 );
1016 // +20 vector normal_point
1018 // 36 int front offset
1019 // 40 int back offset
1020 // 44 int prelist offset
1021 // 48 int postlist offset
1022 // 52 int online offset
1023 void model_interp_sortnorm(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
1026 modelstats_num_sortnorms++;
1029 // Assert( w(p+4) == 56 );
1031 int frontlist = w(p+36);
1032 int backlist = w(p+40);
1033 int prelist = w(p+44);
1034 int postlist = w(p+48);
1035 int onlist = w(p+52);
1039 // light_filter_push_box( vp(p+56), vp(p+68) );
1041 #if 1 //def BACK_TO_FRONT
1043 if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
1045 if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
1047 //draw back then front
1049 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1051 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1053 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1055 } else { //not facing. draw front then back
1057 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1059 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1061 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1064 if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
1067 if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
1069 if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
1073 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1075 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1077 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1079 } else { //not facing. draw front then back
1081 //draw back then front
1083 if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1085 if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1087 if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1091 if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
1095 // light_filter_pop();
1099 void model_draw_debug_points( polymodel *pm, bsp_info * submodel )
1101 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1105 // Draw the brown "core" mass
1106 // if ( submodel && (submodel->parent==-1) ) {
1107 // gr_set_color(128,128,0);
1108 // g3_draw_sphere_ez( &vmd_zero_vector, pm->core_radius );
1111 // Draw a red pivot point
1112 gr_set_color(128,0,0);
1113 g3_draw_sphere_ez(&vmd_zero_vector, 2.0f );
1115 // Draw a green center of mass when drawing the hull
1116 if ( submodel && (submodel->parent==-1) ) {
1117 gr_set_color(0,128,0);
1118 g3_draw_sphere_ez( &pm->center_of_mass, 1.0f );
1122 // Draw a blue center point
1123 gr_set_color(0,0,128);
1124 g3_draw_sphere_ez( &submodel->geometric_center, 0.9f );
1127 // Draw the bounding box
1132 for (i=0; i<8; i++ ) {
1133 g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] );
1135 gr_set_color(128,128,128);
1136 g3_draw_line( &pts[0], &pts[1] );
1137 g3_draw_line( &pts[1], &pts[2] );
1138 g3_draw_line( &pts[2], &pts[3] );
1139 g3_draw_line( &pts[3], &pts[0] );
1141 g3_draw_line( &pts[4], &pts[5] );
1142 g3_draw_line( &pts[5], &pts[6] );
1143 g3_draw_line( &pts[6], &pts[7] );
1144 g3_draw_line( &pts[7], &pts[4] );
1146 g3_draw_line( &pts[0], &pts[4] );
1147 g3_draw_line( &pts[1], &pts[5] );
1148 g3_draw_line( &pts[2], &pts[6] );
1149 g3_draw_line( &pts[3], &pts[7] );
1151 //for (i=0; i<8; i++ ) {
1152 // g3_rotate_vertex( &pts[i], &pm->bounding_box[i] );
1154 gr_set_color(0,255,0);
1157 for (j=0; j<8; j++ ) {
1159 vector bounding_box[8]; // caclulated fron min/max
1160 model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max);
1162 for (i=0; i<8; i++ ) {
1163 g3_rotate_vertex( &pts[i], &bounding_box[i] );
1165 gr_set_color(128,0,0);
1166 g3_draw_line( &pts[0], &pts[1] );
1167 g3_draw_line( &pts[1], &pts[2] );
1168 g3_draw_line( &pts[2], &pts[3] );
1169 g3_draw_line( &pts[3], &pts[0] );
1171 g3_draw_line( &pts[4], &pts[5] );
1172 g3_draw_line( &pts[5], &pts[6] );
1173 g3_draw_line( &pts[6], &pts[7] );
1174 g3_draw_line( &pts[7], &pts[4] );
1176 g3_draw_line( &pts[0], &pts[4] );
1177 g3_draw_line( &pts[1], &pts[5] );
1178 g3_draw_line( &pts[2], &pts[6] );
1179 g3_draw_line( &pts[3], &pts[7] );
1184 // Debug code to show all the paths of a model
1185 void model_draw_paths( int model_num )
1191 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1195 pm = model_get(model_num);
1201 for (i=0; i<pm->n_paths; i++ ) {
1204 for (j=0; j<pm->paths[i].nverts; j++ ) {
1205 // Rotate point into world coordinates
1206 pnt = pm->paths[i].verts[j].pos;
1208 // Pnt is now the x,y,z world coordinates of this vert.
1209 // For this example, I am just drawing a sphere at that
1213 g3_rotate_vertex(&tmp,&pnt);
1215 if ( pm->paths[i].verts[j].nturrets > 0 ){
1216 gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
1218 gr_set_color( 255, 0, 0 );
1221 // g3_draw_sphere( &tmp, pm->paths[i].verts[j].radius );
1222 g3_draw_sphere( &tmp, 0.5f );
1224 gr_set_color( 255, 0, 0 );
1226 g3_draw_line(&prev_pnt, &tmp);
1236 // docking bay and fighter bay paths
1237 void model_draw_bay_paths(int model_num)
1243 polymodel *pm = model_get(model_num);
1248 // render docking bay normals
1249 gr_set_color(0, 255, 0);
1250 for(idx=0; idx<pm->n_docks; idx++){
1251 for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
1252 v1 = pm->docking_bays[idx].pnt[s_idx];
1253 vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
1255 // rotate the points
1256 g3_rotate_vertex(&l1, &v1);
1257 g3_rotate_vertex(&l2, &v2);
1259 // draw the point and normal
1260 g3_draw_sphere(&l1, 2.0);
1261 g3_draw_line(&l1, &l2);
1265 // render figher bay paths
1266 gr_set_color(0, 255, 255);
1268 // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
1269 for (idx = 0; idx<pm->n_paths; idx++) {
1270 if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
1271 for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
1272 v1 = pm->paths[idx].verts[s_idx].pos;
1273 v2 = pm->paths[idx].verts[s_idx+1].pos;
1276 g3_rotate_vertex(&l1, &v1);
1277 g3_rotate_vertex(&l2, &v2);
1278 g3_draw_line(&l1, &l2);
1284 // struct that holds the indicies into path information associated with a fighter bay on a capital ship
1285 // NOTE: Fighter bay paths are identified by the path_name $bayN (where N is numbered from 1).
1286 // Capital ships only have ONE fighter bay on the entire ship
1287 #define MAX_SHIP_BAY_PATHS 10
1288 typedef struct ship_bay {
1289 int num_paths; // how many paths are associated with the model's fighter bay
1290 int paths[MAX_SHIP_BAY_PATHS]; // index into polymodel->paths[] array
1291 int arrive_flags; // bitfield, set to 1 when that path number is reserved for an arrival
1292 int depart_flags; // bitfield, set to 1 when that path number is reserved for a departure
1295 typedef struct mp_vert {
1296 vector pos; // xyz coordinates of vertex in object's frame of reference
1297 int nturrets; // number of turrets guarding this vertex
1298 int *turret_ids; // array of indices into ship_subsys linked list (can't index using [] though)
1299 float radius; // How far the closest obstruction is from this vertex
1302 typedef struct model_path {
1303 char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD
1304 char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF
1305 int parent_submodel;
1308 int goal; // Which of the verts is the one closest to the goal of this path
1309 int type; // What this path takes you to... See MP_TYPE_??? defines above for details
1310 int value; // This depends on the type.
1311 // For MP_TYPE_UNUSED, this means nothing.
1312 // For MP_TYPE_SUBSYS, this is the subsystem number this path takes you to.
1317 void interp_render_arc_segment( vector *v1, vector *v2, int depth )
1319 float d = vm_vec_dist_quick( v1, v2 );
1321 if ( d < 0.30f || (depth>4) ) {
1323 g3_rotate_vertex( &p1, v1 );
1324 g3_rotate_vertex( &p2, v2 );
1326 //g3_draw_rod( v1, 0.2f, v2, 0.2f, NULL, 0);
1327 g3_draw_line( &p1, &p2 );
1331 vm_vec_avg( &tmp, v1, v2 );
1333 float scaler = 0.30f;
1334 tmp.xyz.x += (frand()-0.5f)*d*scaler;
1335 tmp.xyz.y += (frand()-0.5f)*d*scaler;
1336 tmp.xyz.z += (frand()-0.5f)*d*scaler;
1338 interp_render_arc_segment( v1, &tmp, depth+1 );
1339 interp_render_arc_segment( &tmp, v2, depth+1 );
1343 int Interp_lightning = 1;
1344 DCF_BOOL( Arcs, Interp_lightning )
1352 void interp_render_lightning( polymodel *pm, bsp_info * sm )
1354 Assert( sm->num_arcs > 0 );
1358 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1362 if (!Interp_lightning) return;
1364 // if ( keyd_pressed[KEY_LSHIFT] ) return;
1365 // if ( rad < 3.0f ) return;
1367 for (i=0; i<sm->num_arcs; i++ ) {
1368 // pick a color based upon arc type
1369 switch(sm->arc_type[i]){
1370 // "normal", Freespace 1 style arcs
1371 case MARC_TYPE_NORMAL:
1372 if ( (rand()>>4) & 1 ) {
1373 gr_set_color( 64, 64, 255 );
1375 gr_set_color( 128, 128, 255 );
1381 if ( (rand()>>4) & 1 ) {
1382 gr_set_color( AR, AG, AB );
1384 gr_set_color( AR2, AG2, AB2 );
1392 // render the actual arc segment
1393 interp_render_arc_segment( &sm->arc_pts[i][0], &sm->arc_pts[i][1], 0 );
1397 void model_interp_subcall(polymodel * pm, int mn, int detail_level)
1400 int zbuf_mode = gr_zbuffering_mode;
1402 if ( (mn < 0) || (mn>=pm->n_models) )
1406 Assert( mn < pm->n_models );
1408 // mprintf(( "Name = '%s'\n", pm->submodel[mn].name ));
1409 // char * p = pm->submodel[mn].name;
1411 if (pm->submodel[mn].blown_off){
1415 if (pm->submodel[mn].is_thruster ) {
1416 if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){
1419 Interp_thrust_scale_subobj=1;
1421 Interp_thrust_scale_subobj=0;
1424 g3_start_instance_angles(&pm->submodel[mn].offset, &pm->submodel[mn].angs);
1425 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1429 model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 );
1431 if (Interp_flags & MR_SHOW_PIVOTS )
1432 model_draw_debug_points( pm, &pm->submodel[mn] );
1434 if ( pm->submodel[mn].num_arcs ) {
1435 interp_render_lightning( pm, &pm->submodel[mn]);
1438 i = pm->submodel[mn].first_child;
1440 if (!pm->submodel[i].is_thruster ) {
1441 if(Interp_flags & MR_NO_ZBUFFER){
1442 zbuf_mode = GR_ZBUFF_NONE;
1444 zbuf_mode = GR_ZBUFF_FULL; // read only
1447 gr_zbuffer_set(zbuf_mode);
1449 model_interp_subcall( pm, i, detail_level );
1451 i = pm->submodel[i].next_sibling;
1459 // Returns one of the following
1460 #define IBOX_ALL_OFF 0
1461 #define IBOX_ALL_ON 1
1462 #define IBOX_SOME_ON_SOME_OFF 2
1464 int interp_box_offscreen( vector *min, vector *max )
1466 if ( keyd_pressed[KEY_LSHIFT] ) {
1471 v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z;
1472 v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z;
1473 v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z;
1474 v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z;
1476 v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z;
1477 v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z;
1478 v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z;
1479 v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z;
1481 ubyte and_codes = 0xff;
1482 ubyte or_codes = 0xff;
1485 for (i=0; i<8; i++ ) {
1487 ubyte codes=g3_rotate_vertex( &tmp, &v[i] );
1488 // Early out which we cannot do because we want to differentiate btwn
1489 // IBOX_SOME_ON_SOME_OFF and IBOX_ALL_ON
1491 // //mprintf(( "A point is inside, so render it.\n" ));
1492 // return 0; // this point is in, so return 0
1498 // If and_codes is set this means that all points lie off to the
1499 // same side of the screen.
1501 //mprintf(( "All points offscreen, so don't render it.\n" ));
1502 return IBOX_ALL_OFF; //all points off screen
1505 // If this is set it means at least one of the points is offscreen,
1506 // but they aren't all off to the same side.
1508 return IBOX_SOME_ON_SOME_OFF;
1511 // They are all onscreen.
1516 //calls the object interpreter to render an object.
1517 //returns true if drew
1518 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check )
1520 ubyte *p = (ubyte *)model_ptr;
1521 int chunk_type, chunk_size;
1525 chunk_size = w(p+4);
1527 while ( chunk_type != OP_EOF ) {
1529 // mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size ));
1531 switch (chunk_type) {
1532 case OP_EOF: return 1;
1533 case OP_DEFPOINTS: model_interp_defpoints(p,pm,sm); break;
1534 case OP_FLATPOLY: model_interp_flatpoly(p,pm); break;
1535 case OP_TMAPPOLY: model_interp_tmappoly(p,pm); break;
1536 case OP_SORTNORM: model_interp_sortnorm(p,pm,sm,do_box_check); break;
1540 if ( do_box_check ) {
1541 int retval = interp_box_offscreen( vp(p+8), vp(p+20) );
1544 goto DoneWithThis; // Don't need to draw any more polys from this box
1548 do_box_check = 0; // Don't need to check boxes any more
1551 case IBOX_SOME_ON_SOME_OFF:
1552 // continue like we were
1560 if (Interp_flags & MR_SHOW_PIVOTS ) {
1562 modelstats_num_boxes++;
1564 interp_draw_box( vp(p+8), vp(p+20) );
1567 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1573 light_filter_push_box( vp(p+8), vp(p+20) );
1579 mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size ));
1580 Int3(); // Bad chunk type!
1585 chunk_size = w(p+4);
1590 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1601 void model_render_shields( polymodel * pm )
1605 vertex pnt0, tmp, prev_pnt;
1607 if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
1611 gr_set_color(0, 0, 200 );
1613 // Scan all the triangles in the mesh.
1614 for (i=0; i<pm->shield.ntris; i++ ) {
1616 tri = &pm->shield.tris[i];
1618 if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) {
1620 // Process the vertices.
1621 // Note this rotates each vertex each time it's needed, very dumb.
1622 for (j=0; j<3; j++ ) {
1624 g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos );
1627 g3_draw_line(&prev_pnt, &tmp);
1633 g3_draw_line(&pnt0, &prev_pnt);
1638 void model_render_insignias(polymodel *pm, int detail_level)
1642 vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] };
1646 // if the model has no insignias we're done
1647 if(pm->num_ins <= 0){
1651 // set the proper texture
1652 if(Interp_insignia_bitmap >= 0){
1653 gr_set_bitmap(Interp_insignia_bitmap);
1655 // otherwise don't even bother rendering
1660 // otherwise render them
1661 for(idx=0; idx<pm->num_ins; idx++){
1662 // skip insignias not on our detail level
1663 if(pm->ins[idx].detail_level != detail_level){
1667 for(s_idx=0; s_idx<pm->ins[idx].num_faces; s_idx++){
1668 // get vertex indices
1669 i1 = pm->ins[idx].faces[s_idx][0];
1670 i2 = pm->ins[idx].faces[s_idx][1];
1671 i3 = pm->ins[idx].faces[s_idx][2];
1673 // transform vecs and setup vertices
1674 vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset);
1675 vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset);
1676 vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset);
1677 g3_rotate_vertex(&vecs[0], &t1);
1678 g3_rotate_vertex(&vecs[1], &t2);
1679 g3_rotate_vertex(&vecs[2], &t3);
1681 // setup texture coords
1682 vecs[0].u = pm->ins[idx].u[s_idx][0]; vecs[0].v = pm->ins[idx].v[s_idx][0];
1683 vecs[1].u = pm->ins[idx].u[s_idx][1]; vecs[1].v = pm->ins[idx].v[s_idx][1];
1684 vecs[2].u = pm->ins[idx].u[s_idx][2]; vecs[2].v = pm->ins[idx].v[s_idx][2];
1687 g3_draw_poly(3, vlist, TMAP_FLAG_TEXTURED);
1692 int Model_texturing = 1;
1693 int Model_polys = 1;
1695 DCF_BOOL( model_texturing, Model_texturing )
1696 DCF_BOOL( model_polys, Model_polys )
1698 MONITOR( NumModelsRend );
1699 MONITOR( NumHiModelsRend );
1700 MONITOR( NumMedModelsRend );
1701 MONITOR( NumLowModelsRend );
1704 typedef struct model_cache {
1724 int thrust_glow_bitmap;
1725 float thrust_glow_noise;
1727 int last_frame_rendered; // last frame in which this model was rendered not from the cache
1730 #define MAX_MODEL_CACHE MAX_OBJECTS
1731 model_cache Model_cache[MAX_MODEL_CACHE]; // Indexed by objnum
1732 int Model_cache_inited = 0;
1736 // Returns 0 if not valid points
1737 int model_cache_calc_coords(vector *pnt,float rad, float *cx, float *cy, float *cr)
1742 flags = g3_rotate_vertex(&pt,pnt);
1746 g3_project_vertex(&pt);
1748 if (!(pt.flags & (PF_OVERFLOW|CC_BEHIND))) {
1752 *cr = rad*Matrix_scale.xyz.x*Canv_w2/pt.z;
1761 if ( x1 < gr_screen.clip_left ) return 0;
1763 if ( x2 > gr_screen.clip_right ) return 0;
1765 if ( y1 < gr_screen.clip_top ) return 0;
1767 if ( y2 > gr_screen.clip_bottom ) return 0;
1775 void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id );
1778 //draws a bitmap with the specified 3d width & height
1779 //returns 1 if off screen, 0 if not
1780 int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v)
1785 Assert( G3_count == 1 );
1791 sa = (float)sin(angle);
1792 ca = (float)cos(angle);
1794 float width, height;
1796 width = height = rad;
1798 v[0].x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
1799 v[0].y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
1805 v[1].x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->x;
1806 v[1].y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->y;
1812 v[2].x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
1813 v[2].y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
1819 v[3].x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->x;
1820 v[3].y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->y;
1826 ubyte codes_and=0xff;
1829 z = pnt->z - rad / 4.0f;
1830 if ( z < 0.0f ) z = 0.0f;
1833 for (i=0; i<4; i++ ) {
1834 //now code the four points
1835 codes_and &= g3_code_vertex(&v[i]);
1836 v[i].flags = 0; // mark as not yet projected
1837 g3_project_vertex(&v[i]);
1842 return 1; //1 means off screen
1848 int Model_caching = 1;
1849 DCF_BOOL( model_caching, Model_caching );
1851 extern int Tmap_scan_read; // 0 = normal mapper, 1=read, 2=write
1853 #define MODEL_MAX_BITMAP_SIZE 128
1854 ubyte tmp_bitmap[MODEL_MAX_BITMAP_SIZE*MODEL_MAX_BITMAP_SIZE];
1856 void mc_get_bmp( ubyte *data, int x, int y, int w, int h )
1862 for (i = 0; i < h; i++) {
1863 ubyte *dptr = GR_SCREEN_PTR(ubyte,x,i+y);
1864 ubyte *sptr = data+(i*w);
1865 for (j=0; j<w; j++ ) {
1867 *dptr++ = 255; // XPARENT!
1874 void mc_put_bmp( ubyte *data, int x, int y, int w, int h )
1880 for (i = 0; i < h; i++) {
1881 ubyte *dptr = GR_SCREEN_PTR(ubyte,x,i+y);
1882 ubyte *sptr = data+(i*w);
1883 for (j=0; j<w; j++ ) {
1891 float Interp_depth_scale = 1500.0f;
1893 DCF(model_darkening,"Makes models darker with distance")
1896 dc_get_arg(ARG_FLOAT);
1897 Interp_depth_scale = Dc_arg_float;
1901 dc_printf( "Usage: model_darkening float\n" );
1902 Dc_status = 0; // don't print status if help is printed. Too messy.
1906 dc_printf( "model_darkening = %.1f\n", Interp_depth_scale );
1910 void model_try_cache_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int num_lights );
1912 // Compare it to 999.75f at R = 64.0f
1913 // 0.0000f at R = 4.0f
1915 //float cmp_val = 999.75f; // old
1917 // Return a number from 'min' to 'max' where it is
1918 // 'v' is between v1 and v2.
1919 float scale_it( float min, float max, float v, float v1, float v2 )
1921 if ( v < v1 ) return min;
1922 if ( v > v2 ) return max;
1924 v = (v - v1)/(v2-v1);
1925 v = v*(max-min)+min;
1930 void model_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int lighting_skip )
1932 polymodel *pm = model_get(model_num);
1934 // maybe turn off (hardware) culling
1935 if(flags & MR_NO_CULL){
1939 Interp_objnum = objnum;
1941 if ( flags & MR_NO_LIGHTING ) {
1942 Interp_light = 1.0f;
1944 // never use saved lighitng for this object
1945 model_set_saved_lighting(-1, -1);
1946 } else if ( flags & MR_IS_ASTEROID ) {
1947 // Dim it based on distance
1948 float depth = vm_vec_dist_quick( pos, &Eye_position );
1949 if ( depth > Interp_depth_scale ) {
1950 Interp_light = Interp_depth_scale/depth;
1951 // If it is too far, exit
1952 if ( Interp_light < (1.0f/32.0f) ) {
1953 Interp_light = 0.0f;
1955 } else if ( Interp_light > 1.0f ) {
1956 Interp_light = 1.0f;
1959 Interp_light = 1.0f;
1962 // never use saved lighitng for this object
1963 model_set_saved_lighting(-1, -1);
1965 Interp_light = 1.0f;
1967 // maybe use saved lighting
1968 model_set_saved_lighting(objnum, hack_skip_max);
1973 if ( !(flags & MR_NO_LIGHTING ) ) {
1974 if ( D3D_enabled ) {
1975 num_lights = light_filter_push( objnum, pos, pm->rad );
1977 num_lights = light_filter_push( objnum, pos, pm->rad );
1981 model_try_cache_render(model_num, orient, pos, flags, objnum, num_lights );
1983 if ( !(flags & MR_NO_LIGHTING ) ) {
1987 // maybe turn culling back on
1988 if(flags & MR_NO_CULL){
1992 // turn off fog after each model renders
1993 if(The_mission.flags & MISSION_FLAG_FULLNEB){
1994 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
1999 void model_cache_init()
2001 if ( !Model_cache_inited ) {
2004 Model_cache_inited = 1;
2006 for (i=0; i<MAX_MODEL_CACHE; i++ ) {
2007 Model_cache[i].cached_valid = 0;
2008 Model_cache[i].data = NULL;
2009 Model_cache[i].bitmap_id = -1;
2010 Model_cache[i].last_frame_rendered = -1;
2015 void model_cache_reset()
2017 if ( Model_cache_inited ) {
2020 for (i=0; i<MAX_MODEL_CACHE; i++ ) {
2021 Model_cache[i].cached_valid = 0;
2022 if ( Model_cache[i].data ) {
2023 free(Model_cache[i].data);
2024 Model_cache[i].data = NULL;
2026 if ( Model_cache[i].bitmap_id != -1 ) {
2027 bm_release(Model_cache[i].bitmap_id);
2028 Model_cache[i].bitmap_id = -1;
2041 #if MAX_DETAIL_LEVEL != 4
2042 #error MAX_DETAIL_LEVEL is assumed to be 4 in ModelInterp.cpp
2045 // Given detail level, what is the threshold for how far viewer
2046 // can move in the object's frame of reference before a redraw.
2047 float Mc_viewer_pos_factor[MAX_DETAIL_LEVEL+1] = { 0.080f, 0.040f, 0.020f, 0.010f, 0.0f };
2048 float Mc_size_factor[MAX_DETAIL_LEVEL+1] = { 1.40f, 1.30f, 1.20f, 1.10f, 0.0f };
2050 int Model_object_caching_tmp = MAX_DETAIL_LEVEL;
2052 // When framerate goes below this, knock it down a notch.
2053 float Mc_framerate_lo[MAX_DETAIL_LEVEL+1] = { 0.0f, 10.0f, 15.0f, 20.0f, 25.0f };
2054 // When it goes above this, knock it up a notch.
2055 float Mc_framerate_hi[MAX_DETAIL_LEVEL+1] = { 15.0f, 20.0f, 25.0f, 30.0f, 100000.0f };
2057 int Mc_detail_add[MAX_DETAIL_LEVEL+1] = { -2, -1, +1, +2, +4 };
2059 extern float flFrametime;
2061 void model_try_cache_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int num_lights )
2063 model_really_render(model_num, orient, pos, flags, objnum);
2067 model_cache *mc = NULL;
2069 if ( (objnum>-1) && (objnum<MAX_MODEL_CACHE) ) {
2070 mc = &Model_cache[objnum];
2073 if ( (!mc) || (!Model_caching) || (D3D_enabled) || (!Model_cache_inited) || (flags & MR_ALWAYS_REDRAW) || (Detail.object_caching > 3) ) {
2075 mc->cached_valid = 0;
2077 model_really_render(model_num, orient, pos, flags, objnum );
2081 Assert( mc != NULL );
2083 // Fake the detail level based on framerate.
2084 if ( 1.0f / flFrametime < Mc_framerate_lo[Model_object_caching_tmp] ) {
2085 Model_object_caching_tmp--;
2086 // mprintf(( "Model cache level bumped down to %d\n", Model_object_caching ));
2087 } else if ( 1.0f / flFrametime > Mc_framerate_hi[Model_object_caching_tmp] ) {
2088 Model_object_caching_tmp++;
2089 // mprintf(( "Model cache level bumped up to %d\n", Model_object_caching ));
2092 int tmp_detail_level = Model_object_caching_tmp + Mc_detail_add[Detail.object_caching];
2094 if ( tmp_detail_level < 0 ) {
2095 tmp_detail_level = 0;
2096 } else if (tmp_detail_level > MAX_DETAIL_LEVEL ) {
2097 tmp_detail_level = MAX_DETAIL_LEVEL;
2100 if ( tmp_detail_level > 3 ) {
2102 mc->cached_valid = 0;
2104 model_really_render(model_num, orient, pos, flags, objnum );
2109 // static int last_one = -1;
2110 // if ( last_one != tmp_detail_level ) {
2111 // last_one = tmp_detail_level;
2112 // mprintf(( "Detail level %d\n", tmp_detail_level ));
2115 // if ( keyd_pressed[KEY_LSHIFT] ) {
2116 // mc->cached_valid = 0;
2117 // model_really_render(model_num, orient, pos, flags, objnum );
2122 // mprintf(( "Rendering cache model\n" ));
2124 polymodel *pm = model_get(model_num);
2126 vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] };
2131 matrix tempm, tempm2;
2134 vm_copy_transpose_matrix(&tempm2,orient);
2135 vm_matrix_x_matrix(&tempm,&tempm2,&Eye_matrix);
2136 vm_extract_angles_matrix(&new_angles, &tempm );
2138 if ( !model_cache_calc_coords(pos,pm->rad, &cx, &cy, &cr) ) {
2139 // Not onscreen, do a real render and exit
2140 mc->cached_valid = 0;
2141 model_really_render(model_num, orient, pos, flags, objnum );
2145 //================================================================
2146 // A bunch of checks to see if we need to redraw the model or not
2151 vm_vec_sub( &ship_to_eye, &Eye_position, pos );
2152 vm_vec_normalize_safe(&ship_to_eye);
2153 float this_dot = vm_vec_dot( &ship_to_eye, &orient->fvec );
2154 this_dot += vm_vec_dot( &ship_to_eye, &orient->rvec );
2158 if ( !mc->cached_valid ) {
2163 Assert( mc->data != NULL );
2165 if (Framecount - mc->last_frame_rendered > 1 + 2*(MAX_DETAIL_LEVEL - Detail.object_caching - 1)) {
2169 diff = fl_abs( this_dot - mc->last_dot );
2171 if ( diff > Mc_viewer_pos_factor[tmp_detail_level] ) {
2172 // mprintf(( "Redraw!!! %.4f\n", diff ));
2176 // if ( keyd_pressed[KEY_LSHIFT] ) {
2180 if (tmp_detail_level > 2) {
2181 if ( mc->thrust_glow_bitmap != Interp_thrust_glow_bitmap ) {
2182 // Engline glow bitmap changed
2183 // mprintf(( "MC: Glow bitmap changed! %d -> %d\n", mc->thrust_glow_bitmap, Interp_thrust_glow_bitmap ));
2188 if (tmp_detail_level > 2) {
2190 float diff = fl_abs( mc->thrust_scale - Interp_thrust_scale );
2192 if ( diff > 0.1f ) {
2193 // Thruster size has changed
2194 //mprintf(( "MC: Thruster size changed! %.2f -> %.2f\n", mc->thrust_scale, Interp_thrust_scale ));
2201 // float diff = fl_abs( mc->thrust_glow_noise - Interp_thrust_glow_noise );
2203 // if ( diff > 0.1f ) {
2204 // Glow noise has changed
2205 //mprintf(( "MC: Thruster glow changed! %.2f -> %.2f\n", mc->thrust_glow_noise, Interp_thrust_glow_noise ));
2211 if ( mc->model_num != model_num ) {
2216 if ( cr>mc->cr*Mc_size_factor[tmp_detail_level] ) {
2217 // Scaling up too far
2221 if (tmp_detail_level > 2) {
2223 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2224 if (mc->num_lights != num_lights) {
2232 // This method is correct, but rotating ship makes things redraw which is too slow.
2235 // Check orientation
2236 float angle_error = max( fl_abs( mc->angs.p-new_angles.p ),fl_abs( mc->angs.h-new_angles.h ));
2239 //if ( angle_error > 0.075f ) {
2242 if ( angle_error > 0.40f ) {
2243 // Ship/view turned too much
2244 //mprintf(( "Ship/view turned too much %.4f\n", angle_error ));
2252 // mprintf(( "Dot = %.5f\n", dot ));
2258 dx = vm_vec_dot( &orient->rvec, &mc->orient.rvec )+1.0f;
2259 dy = vm_vec_dot( &orient->uvec, &mc->orient.uvec )+1.0f;
2260 dz = vm_vec_dot( &orient->fvec, &mc->orient.fvec )+1.0f;
2262 float angle_error = (dx+dy+dz)*1000.0f/6.0f;
2264 //mprintf(( "Angle_error = %.4f\n", angle_error ));
2266 // Compare it to 999.75f at R = 64.0f
2267 // 0.0000f at R = 0.0f
2269 float cmp_val = 999.75f; // old
2270 // if ( is_asteroid ) {
2271 // cmp_val = scale_it( 0.0f, 999.75f, cr, 0.0f, 64.0f );
2274 if ( angle_error < cmp_val ) {
2275 // Ship turned too much
2282 // Have a valid cache entry, mc
2283 ccflags = g3_rotate_vertex(&pt,pos);
2290 if ( model_get_rotated_bitmap_points(&pt,mc->angs.b - new_angles.b, pm->rad, v )) {
2296 gr_set_bitmap( mc->bitmap_id );
2299 g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED );
2302 // if ( keyd_pressed[KEY_LSHIFT] ) {
2303 // gr_set_color( 255, 0, 0 );
2304 // gr_pixel( fl2i(v[0].sx), fl2i(v[0].sy) );
2307 //if ( keyd_pressed[KEY_RSHIFT] ) {
2308 // gr_line( fl2i(v[0].sx), fl2i(v[0].sy), fl2i(v[1].sx), fl2i(v[1].sy) );
2309 // gr_line( fl2i(v[1].sx), fl2i(v[1].sy), fl2i(v[2].sx), fl2i(v[2].sy) );
2310 // gr_line( fl2i(v[2].sx), fl2i(v[2].sy), fl2i(v[3].sx), fl2i(v[3].sy) );
2311 // gr_line( fl2i(v[3].sx), fl2i(v[3].sy), fl2i(v[0].sx), fl2i(v[0].sy) );
2318 //==========================================================
2319 // Cache is bad for model, so draw it and save it
2323 // if ( mc->data != NULL ) {
2328 if ( mc->bitmap_id != -1 ) {
2329 bm_release(mc->bitmap_id);
2333 mc->cached_valid = 0;
2334 mc->model_num = model_num;
2336 //mc->orient = *orient;
2338 mc->angs = new_angles; //-Physics_viewer_bank;
2340 mc->thrust_scale = Interp_thrust_scale;
2341 mc->thrust_bitmap = Interp_thrust_bitmap;
2342 mc->thrust_glow_bitmap = Interp_thrust_glow_bitmap;
2343 mc->thrust_glow_noise = Interp_thrust_glow_noise;
2345 mc->last_dot = this_dot;
2347 if ( cr > MODEL_MAX_BITMAP_SIZE/2-1 )
2350 //Physics_viewer_bank
2352 ccflags = g3_rotate_vertex(&pt,pos);
2358 model_get_rotated_bitmap_points(&pt,0.0f, pm->rad, v );
2360 int x1, y1, x2, y2, w, h;
2362 x1 = fl_round_2048( v[0].sx );
2363 y1 = fl_round_2048( v[0].sy );
2365 x2 = fl_round_2048( v[2].sx ); //+0.5f );
2366 y2 = fl_round_2048( v[2].sy ); //+0.5f );
2368 if ( x1 < gr_screen.clip_left)
2371 if ( y1 < gr_screen.clip_top )
2374 if ( x2 > gr_screen.clip_right)
2377 if ( y2 > gr_screen.clip_bottom)
2395 if ( w > MODEL_MAX_BITMAP_SIZE )
2398 if ( h > MODEL_MAX_BITMAP_SIZE )
2404 // mprintf(( "Mallocing a %dx%d bitmap\n", w, h ));
2406 if ( mc->data == NULL ) {
2407 mc->data = (ubyte *)malloc( MODEL_MAX_BITMAP_SIZE * MODEL_MAX_BITMAP_SIZE );
2410 // mprintf(( "Done mallocing a %dx%d bitmap\n", w, h ));
2412 if ( mc->data == NULL ) {
2415 for (i = 0; i < w*h; i++) {
2420 mc->bitmap_id = bm_create( 8, mc->w, mc->h, mc->data, 0 );
2422 if ( mc->bitmap_id < 0 ) {
2426 // Save stars and stuff on screen
2427 mc_get_bmp( tmp_bitmap, x1, y1, w, h );
2429 mc->num_lights = num_lights;
2431 // Didn't render a cached one... so render it and then save it in the cache
2433 // Turn on stippling
2434 model_really_render(model_num, orient, pos, flags, objnum );
2436 // Save screen to bitmap
2437 gr_set_bitmap( mc->bitmap_id );
2439 g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED );
2442 // Restore stars and stuff to screen
2443 mc_put_bmp( tmp_bitmap, x1, y1, w, h );
2446 gr_set_bitmap( mc->bitmap_id );
2448 g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED );
2451 mc->cached_valid = 1;
2452 mc->last_frame_rendered = Framecount;
2458 model_really_render(model_num, orient, pos, flags, objnum );
2462 // Find the distance from p0 to the closest point on a box.
2463 // The box's dimensions from 'min' to 'max'.
2464 float interp_closest_dist_to_box( vector *hitpt, vector *p0, vector *min, vector *max )
2466 float *origin = (float *)&p0->xyz.x;
2467 float *minB = (float *)min;
2468 float *maxB = (float *)max;
2469 float *coord = (float *)&hitpt->xyz.x;
2473 for (i=0; i<3; i++ ) {
2474 if ( origin[i] < minB[i] ) {
2477 } else if (origin[i] > maxB[i] ) {
2481 coord[i] = origin[i];
2489 return vm_vec_dist(hitpt,p0);
2493 // Finds the closest point on a model to a point in space. Actually only finds a point
2494 // on the bounding box of the model.
2496 // model_num Which model
2497 // orient Orientation of the model
2498 // pos Position of the model
2499 // eye_pos Point that you want to find the closest point to
2501 // distance from eye_pos to closest_point. 0 means eye_pos is
2502 // on or inside the bounding box.
2503 // Also fills in outpnt with the actual closest point.
2504 float model_find_closest_point( vector *outpnt, int model_num, int submodel_num, matrix *orient, vector * pos, vector *eye_pos )
2506 vector closest_pos, tempv, eye_rel_pos;
2508 polymodel *pm = model_get(model_num);
2510 if ( submodel_num < 0 ) {
2511 submodel_num = pm->detail[0];
2514 // Rotate eye pos into object coordinates
2515 vm_vec_sub(&tempv,pos,eye_pos );
2516 vm_vec_rotate(&eye_rel_pos,&tempv,orient );
2518 return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max );
2526 dc_printf("Tiled textures\n");
2528 dc_printf("Non-tiled textures\n");
2532 extern void d3d_zbias(int bias);
2533 extern void opengl_zbias(int bias);
2534 void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id )
2536 int i, detail_level;
2538 uint save_gr_zbuffering_mode;
2541 MONITOR_INC( NumModelsRend, 1 );
2543 Interp_orient = orient;
2546 int tmp_detail_level = Game_detail_level;
2548 // if ( D3D_enabled ) {
2549 // tmp_detail_level = -1; // Force no hires models for Direct3D
2552 // Tmap_show_layers = 1;
2553 // model_set_detail_level(0);
2554 // flags |= MR_LOCK_DETAIL|MR_NO_TEXTURING|MR_NO_LIGHTING; //MR_LOCK_DETAIL | |MR_NO_LIGHTING|MR_NO_SMOOTHINGMR_NO_TEXTURING |
2556 // Turn off engine effect
2557 Interp_thrust_scale_subobj=0;
2559 if (!Model_texturing)
2560 flags |= MR_NO_TEXTURING;
2562 if ( !Model_polys ) {
2563 flags |= MR_NO_POLYS;
2566 Interp_flags = flags;
2568 pm = model_get(model_num);
2570 // Set the flags we will pass to the tmapper
2571 if ( D3D_enabled ) {
2572 Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
2574 Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP;
2577 // if we're in nebula mode
2578 if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
2579 Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
2582 if ( !(Interp_flags & MR_NO_TEXTURING) ) {
2583 Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
2585 if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
2586 Interp_tmap_flags |= TMAP_FLAG_TILED;
2588 if ( !(Interp_flags & MR_NO_CORRECT) ) {
2589 Interp_tmap_flags |= TMAP_FLAG_CORRECT;
2593 save_gr_zbuffering_mode = gr_zbuffering_mode;
2594 zbuf_mode = gr_zbuffering_mode;
2596 if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) {
2597 gr_set_color(0,128,0);
2598 g3_draw_sphere_ez( pos, pm->rad );
2602 g3_start_instance_matrix(pos,orient);
2604 if ( Interp_flags & MR_SHOW_RADIUS ) {
2605 if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
2606 gr_set_color(0,64,0);
2607 g3_draw_sphere_ez(&vmd_zero_vector,pm->rad);
2611 Assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS );
2614 float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
2615 if ( pm->n_detail_levels > 1 ) {
2617 if ( Interp_flags & MR_LOCK_DETAIL ) {
2618 i = Interp_detail_level+1;
2621 //gr_set_color(0,128,0);
2622 //g3_draw_sphere_ez( &closest_pos, 2.0f );
2624 #if MAX_DETAIL_LEVEL != 4
2625 #error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4
2628 switch( Detail.detail_distance ) {
2632 case 1: // lower than normal
2635 case 2: // default (leave the same)
2637 case 3: // above normal
2640 case 4: // even more normal
2646 if(The_mission.flags & MISSION_FLAG_FULLNEB){
2647 depth *= neb2_get_lod_scale(Interp_objnum);
2650 for ( i=0; i<pm->n_detail_levels; i++ ) {
2651 if ( depth<=pm->detail_depth[i] ){
2656 // If no valid detail depths specified, use highest.
2657 if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f)) {
2663 // maybe force lower detail
2664 if (Interp_flags & MR_FORCE_LOWER_DETAIL) {
2668 //detail_level = fl2i(depth/10.0f);
2670 detail_level = i-1-tmp_detail_level;
2672 if ( detail_level < 0 )
2674 else if (detail_level >= pm->n_detail_levels )
2675 detail_level = pm->n_detail_levels-1;
2677 //mprintf(( "Depth = %.2f, detail = %d\n", depth, detail_level ));
2684 if ( detail_level==0 ) {
2685 MONITOR_INC( NumHiModelsRend, 1 );
2686 } else if ( detail_level ==pm->n_detail_levels-1 ) {
2687 MONITOR_INC( NumLowModelsRend, 1 );
2689 MONITOR_INC( NumMedModelsRend, 1 );
2693 if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){
2694 vector auto_back = pm->autocenter;
2695 vm_vec_scale(&auto_back, -1.0f);
2696 g3_start_instance_matrix(&auto_back, NULL);
2699 if (gr_screen.mode == GR_DIRECT3D){
2701 } else if (gr_screen.mode == GR_OPENGL) {
2705 // Draw the subobjects
2706 i = pm->submodel[pm->detail[detail_level]].first_child;
2709 if (!pm->submodel[i].is_thruster ) {
2710 zbuf_mode = GR_ZBUFF_WRITE;
2713 if(Interp_flags & MR_NO_ZBUFFER){
2714 zbuf_mode = GR_ZBUFF_NONE;
2717 gr_zbuffer_set(zbuf_mode);
2719 model_interp_subcall( pm, i, detail_level );
2721 i = pm->submodel[i].next_sibling;
2724 // rotate lights for the hull
2725 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2729 if ( pm->submodel[pm->detail[detail_level]].num_children > 0 ){
2730 // zbuf_mode |= GR_ZBUFF_WRITE; // write only
2731 zbuf_mode = GR_ZBUFF_FULL;
2735 if(Interp_flags & MR_NO_ZBUFFER){
2736 zbuf_mode = GR_ZBUFF_NONE;
2739 gr_zbuffer_set(zbuf_mode);
2741 if(gr_screen.mode == GR_DIRECT3D){
2743 } else if (gr_screen.mode == GR_OPENGL) {
2747 // draw the hull of the ship
2748 model_interp_sub( (ubyte *)pm->submodel[pm->detail[detail_level]].bsp_data, pm, &pm->submodel[pm->detail[detail_level]], 0 );
2750 if (Interp_flags & MR_SHOW_PIVOTS ) {
2751 model_draw_debug_points( pm, NULL );
2752 model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]] );
2754 if(pm->flags & PM_FLAG_AUTOCEN){
2755 gr_set_color(255, 255, 255);
2756 g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
2760 if ( pm->submodel[pm->detail[0]].num_arcs ) {
2761 interp_render_lightning( pm, &pm->submodel[pm->detail[0]]);
2764 if ( Interp_flags & MR_SHOW_SHIELDS ) {
2765 model_render_shields(pm);
2768 // render model insignias
2769 if(gr_screen.mode == GR_DIRECT3D){
2771 } else if (gr_screen.mode == GR_OPENGL) {
2775 gr_zbuffer_set(GR_ZBUFF_READ);
2776 model_render_insignias(pm, detail_level);
2779 if(gr_screen.mode == GR_DIRECT3D){
2781 } else if (gr_screen.mode == GR_OPENGL) {
2785 // Draw the thruster glow
2787 if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) /*&& (Detail.engine_glows)*/ ) {
2789 if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) && (Detail.engine_glows) ) {
2792 for (i = 0; i < pm->n_thrusters; i++ ) {
2793 thruster_bank *bank = &pm->thrusters[i];
2796 for ( j=0; j<bank->num_slots; j++ ) {
2799 vm_vec_sub(&tempv,&View_position,&bank->pnt[j]);
2800 vm_vec_normalize(&tempv);
2802 d = vm_vec_dot(&tempv,&bank->norm[j]);
2807 // Make glow bitmap fade in/out quicker from sides.
2809 if ( d > 1.0f ) d = 1.0f;
2811 // fade them in the nebula as well
2812 if(The_mission.flags & MISSION_FLAG_FULLNEB){
2813 d *= (1.0f - Interp_fog_level);
2816 //ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
2817 #define NOISE_SCALE 0.5f
2818 #define MIN_SCALE 3.4f
2819 #define MAX_SCALE 4.7f
2820 float scale = MIN_SCALE;
2822 scale = (Interp_thrust_scale-0.1f)*(MAX_SCALE-MIN_SCALE)+MIN_SCALE;
2824 float w = bank->radius[j]*(scale+Interp_thrust_glow_noise*NOISE_SCALE );
2827 gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
2829 g3_rotate_vertex( &p, &bank->pnt[j] );
2830 gr_set_bitmap( Interp_thrust_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, d );
2833 extern int Gr_scaler_zbuffering;
2834 Gr_scaler_zbuffering = 1;
2835 g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED );
2836 //g3_draw_rotated_bitmap(&p,0.0f,w,w, TMAP_FLAG_TEXTURED );
2837 Gr_scaler_zbuffering = 0;
2839 g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED );
2848 Interp_thrust_glow_bitmap = -1;
2852 // Draw the thruster subobjects
2853 i = pm->submodel[pm->detail[detail_level]].first_child;
2855 if (pm->submodel[i].is_thruster ) {
2856 zbuf_mode = GR_ZBUFF_READ;
2859 if(Interp_flags & MR_NO_ZBUFFER){
2860 zbuf_mode = GR_ZBUFF_NONE;
2863 gr_zbuffer_set(zbuf_mode);
2865 model_interp_subcall( pm, i, detail_level );
2867 i = pm->submodel[i].next_sibling;
2872 if ( Interp_flags & MR_SHOW_PATHS ){
2873 model_draw_paths(model_num);
2876 if (Interp_flags & MR_BAY_PATHS ){
2877 model_draw_bay_paths(model_num);
2880 if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){
2885 gr_zbuffer_set(save_gr_zbuffering_mode);
2889 void submodel_render(int model_num, int submodel_num, matrix *orient, vector * pos, uint flags, int light_ignore_id)
2893 MONITOR_INC( NumModelsRend, 1 );
2895 if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return;
2897 // Turn off engine effect
2898 Interp_thrust_scale_subobj=0;
2900 if (!Model_texturing)
2901 flags |= MR_NO_TEXTURING;
2903 Interp_flags = flags;
2905 pm = model_get(model_num);
2907 // Set the flags we will pass to the tmapper
2908 if ( D3D_enabled ) {
2909 Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
2911 Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP;
2914 // if we're in nebula mode
2915 if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
2916 Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
2919 if ( !(Interp_flags & MR_NO_TEXTURING) ) {
2920 Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
2922 if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling )
2923 Interp_tmap_flags |= TMAP_FLAG_TILED;
2925 if ( !(Interp_flags & MR_NO_CORRECT) ) {
2926 Interp_tmap_flags |= TMAP_FLAG_CORRECT;
2930 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2931 if ( D3D_enabled ) {
2932 light_filter_push( -1, pos, pm->submodel[submodel_num].rad );
2934 light_filter_push( light_ignore_id, pos, pm->submodel[submodel_num].rad );
2938 g3_start_instance_matrix(pos,orient);
2940 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2944 model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 );
2945 if ( pm->submodel[submodel_num].num_arcs ) {
2946 interp_render_lightning( pm, &pm->submodel[submodel_num]);
2949 if (Interp_flags & MR_SHOW_PIVOTS )
2950 model_draw_debug_points( pm, &pm->submodel[submodel_num] );
2952 if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
2961 // Fills in an array with points from a model.
2962 // Only gets up to max_num verts;
2963 // Returns number of verts found;
2964 static int submodel_get_points_internal(int model_num, int submodel_num, int max_num, vector **pnts, vector **norms )
2968 pm = model_get(model_num);
2970 if ( submodel_num < 0 ) {
2971 submodel_num = pm->detail[0];
2974 ubyte *p = pm->submodel[submodel_num].bsp_data;
2975 int chunk_type, chunk_size;
2978 chunk_size = w(p+4);
2980 while (chunk_type != OP_EOF) {
2981 switch (chunk_type) {
2982 case OP_EOF: return 1;
2983 case OP_DEFPOINTS: {
2985 int nverts = w(p+8);
2986 int offset = w(p+16);
2988 ubyte * normcount = p+20;
2989 vector *src = vp(p+offset);
2991 if ( nverts > max_num )
2994 for (n=0; n<nverts; n++ ) {
2996 *norms++ = src + 1; // first normal associated with the point
2998 src += normcount[n]+1;
3000 return nverts; // Read in 'n' points
3003 case OP_FLATPOLY: break;
3004 case OP_TMAPPOLY: break;
3005 case OP_SORTNORM: break;
3006 case OP_BOUNDBOX: break;
3008 mprintf(( "Bad chunk type %d, len=%d in submodel_get_points\n", chunk_type, chunk_size ));
3009 Int3(); // Bad chunk type!
3014 chunk_size = w(p+4);
3016 return 0; // Couldn't find 'em
3019 // Gets two random points on a model
3020 void submodel_get_two_random_points(int model_num, int submodel_num, vector *v1, vector *v2, vector *n1, vector *n2 )
3022 int nv = submodel_get_points_internal(model_num, submodel_num, MAX_POLYGON_VECS, Interp_verts, Interp_norms );
3024 int vn1 = (myrand()>>5) % nv;
3025 int vn2 = (myrand()>>5) % nv;
3027 *v1 = *Interp_verts[vn1];
3028 *v2 = *Interp_verts[vn2];
3031 *n1 = *Interp_norms[vn1];
3034 *n2 = *Interp_norms[vn2];
3038 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
3039 // This defaults to black.
3040 void model_set_outline_color(int r, int g, int b )
3042 gr_init_color( &Interp_outline_color, r, g, b );
3046 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
3047 // This defaults to black.
3048 void model_set_outline_color_fast(void *outline_color)
3050 Interp_outline_color = *((color*)(outline_color));
3053 // IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n'
3054 // This defaults to 0. (0=highest, larger=lower)
3055 void model_set_detail_level(int n)
3057 Interp_detail_level = n;
3061 // Returns number of verts in a submodel;
3062 int submodel_get_num_verts(int model_num, int submodel_num )
3066 pm = model_get(model_num);
3068 ubyte *p = pm->submodel[submodel_num].bsp_data;
3069 int chunk_type, chunk_size;
3072 chunk_size = w(p+4);
3074 while (chunk_type != OP_EOF) {
3075 switch (chunk_type) {
3076 case OP_EOF: return 0;
3077 case OP_DEFPOINTS: {
3079 return n; // Read in 'n' points
3082 case OP_FLATPOLY: break;
3083 case OP_TMAPPOLY: break;
3084 case OP_SORTNORM: break;
3085 case OP_BOUNDBOX: break;
3087 mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size ));
3088 Int3(); // Bad chunk type!
3093 chunk_size = w(p+4);
3095 return 0; // Couldn't find 'em
3098 // Returns number of tmaps & flat polys in a submodel;
3099 int submodel_get_num_polys_sub( ubyte *p )
3101 int chunk_type = w(p);
3102 int chunk_size = w(p+4);
3105 while (chunk_type != OP_EOF) {
3106 switch (chunk_type) {
3107 case OP_EOF: return n;
3108 case OP_DEFPOINTS: break;
3109 case OP_FLATPOLY: n++; break;
3110 case OP_TMAPPOLY: n++; break;
3112 int frontlist = w(p+36);
3113 int backlist = w(p+40);
3114 int prelist = w(p+44);
3115 int postlist = w(p+48);
3116 int onlist = w(p+52);
3117 n += submodel_get_num_polys_sub(p+frontlist);
3118 n += submodel_get_num_polys_sub(p+backlist);
3119 n += submodel_get_num_polys_sub(p+prelist);
3120 n += submodel_get_num_polys_sub(p+postlist );
3121 n += submodel_get_num_polys_sub(p+onlist );
3124 case OP_BOUNDBOX: break;
3126 mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size ));
3127 Int3(); // Bad chunk type!
3132 chunk_size = w(p+4);
3137 // Returns number of tmaps & flat polys in a submodel;
3138 int submodel_get_num_polys(int model_num, int submodel_num )
3142 pm = model_get(model_num);
3144 return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data );
3148 // Sets the submodel instance data in a submodel
3149 // If show_damaged is true it shows only damaged submodels.
3150 // If it is false it shows only undamaged submodels.
3151 void model_show_damaged(int model_num, int show_damaged )
3156 pm = model_get(model_num);
3158 for (i=0; i<pm->n_models; i++ ) {
3159 bsp_info *sm = &pm->submodel[i];
3161 // Set the "blown out" flags
3165 for (i=0; i<pm->n_models; i++ ) {
3166 bsp_info *sm = &pm->submodel[i];
3168 // Set the "blown out" flags
3169 if ( show_damaged ) {
3170 if ( sm->my_replacement > -1 ) {
3171 pm->submodel[sm->my_replacement].blown_off = 0;
3175 if ( sm->my_replacement > -1 ) {
3176 pm->submodel[sm->my_replacement].blown_off = 1;
3183 // set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether)
3184 void model_set_insignia_bitmap(int bmap)
3186 Interp_insignia_bitmap = bmap;
3189 // set the forces bitmap
3190 void model_set_forced_texture(int bmap)
3192 Interp_forced_bitmap = bmap;
3195 // set model transparency for use with MR_ALL_XPARENT
3196 void model_set_alpha(float alpha)
3198 Interp_xparent_alpha = alpha;
3201 // see if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error
3202 int model_find_texture(int model_num, int bitmap)
3207 // get a handle to the model
3208 pm = model_get(model_num);
3214 for(idx=0; idx<pm->n_textures; idx++){
3215 if(pm->textures[idx] == bitmap){
3224 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
3225 // returns closest distance to extended box
3226 // positive return value means start_point is outside extended box
3227 // displaces closest point an optional amount delta to the outside of the box
3228 // closest_box_point can be NULL.
3229 float get_model_closest_box_point_with_delta(vector *closest_box_point, vector *start_point, int modelnum, int *is_inside, float delta)
3232 vector box_point, ray_direction, *extremes;
3233 float dist, best_dist;
3236 int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
3237 int mask_inside = 0x3f;
3239 best_dist = FLT_MAX;
3240 pm = model_get(modelnum);
3242 for (i=0; i<6; i++) {
3243 idx = i / 2; // which row vector of Identity matrix
3245 memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vector));
3247 // do negative, then positive plane for each axis
3249 extremes = &pm->mins;
3250 vm_vec_negate(&ray_direction);
3252 extremes = &pm->maxs;
3255 // a negative distance means started outside the box
3256 dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f);
3260 if (fabs(dist) < fabs(best_dist)) {
3262 if (closest_box_point) {
3263 vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta);
3268 // is start_point inside the box
3270 *is_inside = (inside == mask_inside);
3276 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
3277 // returns closest distance to extended box
3278 // positive return value means start_point is outside extended box
3279 // displaces closest point an optional amount delta to the outside of the box
3280 // closest_box_point can be NULL.
3281 float get_world_closest_box_point_with_delta(vector *closest_box_point, object *box_obj, vector *start_point, int *is_inside, float delta)
3283 vector temp, box_start;
3288 modelnum = Ships[box_obj->instance].modelnum;
3290 // rotate start_point to box_obj RF
3291 vm_vec_sub(&temp, start_point, &box_obj->pos);
3292 vm_vec_rotate(&box_start, &temp, &box_obj->orient);
3294 dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta);
3296 // rotate closest_box_point to world RF
3297 if (closest_box_point) {
3298 vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient);
3299 vm_vec_add(closest_box_point, &temp, &box_obj->pos);
3305 void model_set_fog_level(float l)
3307 Interp_fog_level = l;
3310 // given a newly loaded model, page in all textures
3311 void model_page_in_textures(int modelnum, int ship_info_index)
3317 if((ship_info_index < 0) || (ship_info_index >= Num_ship_types)){
3320 sip = &Ship_info[ship_info_index];
3322 polymodel *pm = model_get(modelnum);
3329 // set nondarkening pixels
3330 if(sip->num_nondark_colors){
3331 palman_set_nondarkening(sip->nondark_colors, sip->num_nondark_colors);
3333 // use the colors from the default table
3335 palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default);
3338 for (idx=0; idx<pm->n_textures; idx++ ){
3339 int bitmap_num = pm->original_textures[idx];
3341 if ( bitmap_num > -1 ) {
3342 // if we're in Glide (and maybe later with D3D), use nondarkening textures
3343 if(gr_screen.mode == GR_GLIDE){
3344 bm_lock(bitmap_num, 16, BMP_TEX_NONDARK);
3345 bm_unlock(bitmap_num);
3347 bm_lock(bitmap_num, 16, BMP_TEX_OTHER);
3348 bm_unlock(bitmap_num);
3354 // is the given model a pirate ship?
3355 int model_is_pirate_ship(int modelnum)