FS2_Open
Open source remastering of the Freespace 2 engine
modelinterp.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #define MODEL_LIB
13 
14 #include "bmpman/bmpman.h"
15 #include "cmdline/cmdline.h"
16 #include "debugconsole/console.h"
18 #include "gamesnd/gamesnd.h"
19 #include "globalincs/alphacolors.h"
20 #include "globalincs/linklist.h"
21 #include "graphics/2d.h"
22 #include "graphics/gropengllight.h"
23 #include "io/key.h"
24 #include "io/timer.h"
25 #include "math/fvi.h"
26 #include "math/staticrand.h"
27 #include "mission/missionparse.h"
28 #include "model/modelrender.h"
29 #include "model/modelsinc.h"
30 #include "nebula/neb.h"
31 #include "parse/parselo.h"
32 #include "particle/particle.h"
33 #include "render/3dinternal.h"
34 #include "ship/ship.h"
35 #include "ship/shipfx.h"
36 #include "weapon/shockwave.h"
37 
38 #include <limits.h>
39 
40 
41 float model_radius = 0;
42 
43 // Some debug variables used externally for displaying stats
44 #ifndef NDEBUG
50 #endif
51 
52 extern fix game_get_overall_frametime(); // for texture animation
53 
54 typedef struct model_light {
55  ubyte r, g, b;
57 } model_light;
58 
59 // a lighting object
60 typedef struct model_light_object {
62 
63  int objnum;
64  int skip;
65  int skip_max;
67 
68 struct bsp_vertex
69 {
73  ubyte r, g, b, a;
74 };
75 
77 {
80 
81  int texture;
82 };
83 
85 {
86  SCP_vector<vec3d> Vertex_list;
87  SCP_vector<vec3d> Normal_list;
88 
89  SCP_vector<bsp_vertex> Polygon_vertices;
90  SCP_vector<bsp_polygon> Polygons;
91 
92  ubyte* Lights;
93 
94  int Num_polies[MAX_MODEL_TEXTURES];
95  int Num_verts[MAX_MODEL_TEXTURES];
96 
97  int Num_flat_polies;
98  int Num_flat_verts;
99 
100  void process_bsp(int offset, ubyte* bsp_data);
101  void process_defpoints(int off, ubyte* bsp_data);
102  void process_sortnorm(int offset, ubyte* bsp_data);
103  void process_tmap(int offset, ubyte* bsp_data);
104  void process_flat(int offset, ubyte* bsp_data);
105 public:
106  bsp_polygon_data(ubyte* bsp_data);
107 
108  int get_num_triangles(int texture);
109  int get_num_lines(int texture);
110 
111  void generate_triangles(int texture, vertex *vert_ptr, vec3d* norm_ptr);
112  void generate_lines(int texture, vertex *vert_ptr);
113 };
114 
115 // -----------------------
116 // Local variables
117 //
118 
119 static int Num_interp_verts_allocated = 0;
121 static vertex *Interp_points = NULL;
122 static vertex *Interp_splode_points = NULL;
124 static int Interp_num_verts = 0;
125 
126 static vertex **Interp_list = NULL;
127 static int Num_interp_list_verts_allocated = 0;
128 
129 static float Interp_box_scale = 1.0f; // this is used to scale both detail boxes and spheres
130 
131 // -------------------------------------------------------------------
132 // lighting save stuff
133 //
134 
139 //
140 // lighting save stuff
141 // -------------------------------------------------------------------
142 
143 
144 static int Num_interp_norms_allocated = 0;
145 static vec3d **Interp_norms = NULL;
146 static ubyte *Interp_light_applied = NULL;
147 static int Interp_num_norms = 0;
148 static ubyte *Interp_lights;
149 
150 static float Interp_fog_level = 0.0f;
151 
152 // Stuff to control rendering parameters
153 static color Interp_outline_color;
154 static int Interp_detail_level_locked = -1;
155 static uint Interp_flags = 0;
156 static uint Interp_tmap_flags = 0;
157 bool Interp_desaturate = false;
158 
159 // If non-zero, then the subobject gets scaled by Interp_thrust_scale.
161 float Interp_thrust_scale = 0.1f;
162 static float Interp_thrust_scale_x = 0.0f;//added -bobboau
163 static float Interp_thrust_scale_y = 0.0f;//added -bobboau
164 
165 static int Interp_thrust_bitmap = -1;
166 static int Interp_thrust_glow_bitmap = -1;
167 static float Interp_thrust_glow_noise = 1.0f;
168 static bool Interp_afterburner = false;
169 
170 // Bobboau's thruster stuff
171 static int Interp_secondary_thrust_glow_bitmap = -1;
172 static int Interp_tertiary_thrust_glow_bitmap = -1;
173 static int Interp_distortion_thrust_bitmap = -1;
174 static float Interp_thrust_glow_rad_factor = 1.0f;
175 static float Interp_secondary_thrust_glow_rad_factor = 1.0f;
176 static float Interp_tertiary_thrust_glow_rad_factor = 1.0f;
177 static float Interp_distortion_thrust_rad_factor = 1.0f;
178 static float Interp_distortion_thrust_length_factor = 1.0f;
179 static float Interp_thrust_glow_len_factor = 1.0f;
180 static vec3d Interp_thrust_rotvel = ZERO_VECTOR;
181 static bool Interp_draw_distortion = true;
182 
183 // Bobboau's warp stuff
184 static float Interp_warp_scale_x = 1.0f;
185 static float Interp_warp_scale_y = 1.0f;
186 static float Interp_warp_scale_z = 1.0f;
187 static int Interp_warp_bitmap = -1;
188 static float Interp_warp_alpha = -1.0f;
189 
190 static int Interp_objnum = -1;
191 
192 // if != -1, use this bitmap when rendering ship insignias
193 static int Interp_insignia_bitmap = -1;
194 
195 // replacement - Goober5000
196 // updated - WMC
197 static int *Interp_new_replacement_textures = NULL;
198 
199 static fix Interp_base_frametime = 0;
200 
201 // if != -1, use this bitmap when rendering with a forced texture
202 static int Interp_forced_bitmap = -1;
203 
204 // for rendering with the MR_ALL_XPARENT FLAG SET
205 static float Interp_xparent_alpha = 1.0f;
206 
207 float Interp_light = 0.0f;
208 
211 
212 // our current level of detail (LOD)
214 
215 static int FULLCLOAK = -1;
216 
217 // clip planes
221 
222 // team colors
225 
226 // animated shader effects
229 
231 
232 // forward references
233 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check);
234 void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, int render, int objnum = -1);
235 void model_interp_sortnorm_b2f(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check);
236 void model_interp_sortnorm_f2b(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check);
238 int model_should_render_engine_glow(int objnum, int bank_obj);
239 void model_render_buffers_DEPRECATED(polymodel *pm, int mn,int render, bool is_child = false);
240 void model_render_children_buffers_DEPRECATED(polymodel * pm, int mn, int detail_level, int render);
241 int model_interp_get_texture(texture_info *tinfo, fix base_frametime);
242 
244 {
245  if (Interp_verts != NULL)
246  vm_free(Interp_verts);
247 
248  if (Interp_points != NULL)
249  vm_free(Interp_points);
250 
251  if (Interp_splode_points != NULL)
252  vm_free(Interp_splode_points);
253 
254  if (Interp_splode_verts != NULL)
255  vm_free(Interp_splode_verts);
256 
257  if (Interp_norms != NULL)
258  vm_free(Interp_norms);
259 
260  if (Interp_light_applied != NULL)
261  vm_free(Interp_light_applied);
262 
263  if (Interp_lighting_temp.lights != NULL)
264  vm_free(Interp_lighting_temp.lights);
265 
266  Num_interp_verts_allocated = 0;
267  Num_interp_norms_allocated = 0;
268 }
269 
270 extern void model_collide_allocate_point_list(int n_points);
271 extern void model_collide_free_point_list();
272 
273 void model_allocate_interp_data(int n_verts = 0, int n_norms = 0, int n_list_verts = 0)
274 {
275  static ubyte dealloc = 0;
276 
277  if (!dealloc) {
280  dealloc = 1;
281  }
282 
283  Assert( (n_verts >= 0) && (n_norms >= 0) && (n_list_verts >= 0) );
284  Assert( (n_verts || Num_interp_verts_allocated) && (n_norms || Num_interp_norms_allocated) /*&& (n_list_verts || Num_interp_list_verts_allocated)*/ );
285 
286  if (n_verts > Num_interp_verts_allocated) {
287  if (Interp_verts != NULL) {
288  vm_free(Interp_verts);
289  Interp_verts = NULL;
290  }
291  // Interp_verts can't be reliably realloc'd so free and malloc it on each resize (no data needs to be carried over)
292  Interp_verts = (vec3d**) vm_malloc( n_verts * sizeof(vec3d *) );
293 
294  Interp_points = (vertex*) vm_realloc( Interp_points, n_verts * sizeof(vertex) );
295  Interp_splode_points = (vertex*) vm_realloc( Interp_splode_points, n_verts * sizeof(vertex) );
296  Interp_splode_verts = (vec3d*) vm_realloc( Interp_splode_verts, n_verts * sizeof(vec3d) );
297 
298  Num_interp_verts_allocated = n_verts;
299 
300  // model collide needs a similar size to resize it based on this new value
302  }
303 
304  if (n_norms > Num_interp_norms_allocated) {
305  if (Interp_norms != NULL) {
306  vm_free(Interp_norms);
307  Interp_norms = NULL;
308  }
309  // Interp_norms can't be reliably realloc'd so free and malloc it on each resize (no data needs to be carried over)
310  Interp_norms = (vec3d**) vm_malloc( n_norms * sizeof(vec3d *) );
311 
312  // these next two lighting things aren't values that need to be carried over, but we need to make sure they are 0 by default
313  if (Interp_light_applied != NULL) {
314  vm_free(Interp_light_applied);
315  Interp_light_applied = NULL;
316  }
317 
318  if (Interp_lighting_temp.lights != NULL) {
319  vm_free(Interp_lighting_temp.lights);
320  Interp_lighting_temp.lights = NULL;
321  }
322 
323  Interp_light_applied = (ubyte*) vm_malloc( n_norms * sizeof(ubyte) );
324  Interp_lighting_temp.lights = (model_light*) vm_malloc( n_norms * sizeof(model_light) );
325 
326  memset( Interp_light_applied, 0, n_norms * sizeof(ubyte) );
327  memset( Interp_lighting_temp.lights, 0, n_norms * sizeof(model_light) );
328 
329  Num_interp_norms_allocated = n_norms;
330  }
331 
332  // we should only get here if we are not in HTL mode
333  if ( n_list_verts > Num_interp_list_verts_allocated ) {
334  if (Interp_list != NULL) {
335  vm_free(Interp_list);
336  Interp_list = NULL;
337  }
338 
339  Interp_list = (vertex**) vm_malloc( n_list_verts * sizeof(vertex) );
340  Verify( Interp_list != NULL );
341 
342  Num_interp_list_verts_allocated = n_list_verts;
343  }
344 
345 
346  Interp_num_verts = n_verts;
347  Interp_num_norms = n_norms;
348 
349  // check that everything is still usable (works in release and debug builds)
350  Verify( Interp_points != NULL );
351  Verify( Interp_splode_points != NULL );
352  Verify( Interp_verts != NULL );
353  Verify( Interp_splode_verts != NULL );
354  Verify( Interp_norms != NULL );
355  Verify( Interp_light_applied != NULL );
356 }
357 
358 void model_setup_cloak(vec3d *shift, int full_cloak, int alpha)
359 {
360  FULLCLOAK=full_cloak;
361  int unit;
362  if (full_cloak)
363  {
364  unit=0;
368  gr_set_cull(1);
370  }
371  else
372  {
373  unit=2;
375  }
376 
379  gr_translate_texture_matrix(unit,shift);
380 }
381 
382 void model_finish_cloak(int full_cloak)
383 {
384  int unit;
385  if (full_cloak){ unit=0; gr_set_cull(0);}
386  else unit=2;
387 
388  gr_pop_texture_matrix(unit);
391  FULLCLOAK=-1;
392 }
393 
395 {
396  Interp_thrust_scale = 0.1f;
397  Interp_thrust_scale_x = 0.0f;//added-Bobboau
398  Interp_thrust_scale_y = 0.0f;//added-Bobboau
399  Interp_thrust_bitmap = -1;
400  Interp_thrust_glow_bitmap = -1;
401  Interp_thrust_glow_noise = 1.0f;
402  Interp_insignia_bitmap = -1;
403  Interp_afterburner = false;
404 
405  // Bobboau's thruster stuff
406  {
407  Interp_thrust_glow_rad_factor = 1.0f;
408 
409  Interp_secondary_thrust_glow_bitmap = -1;
410  Interp_secondary_thrust_glow_rad_factor = 1.0f;
411 
412  Interp_tertiary_thrust_glow_bitmap = -1;
413  Interp_tertiary_thrust_glow_rad_factor = 1.0f;
414 
415  Interp_thrust_glow_len_factor = 1.0f;
416 
417  vm_vec_zero(&Interp_thrust_rotvel);
418  }
419 
420  Interp_box_scale = 1.0f;
421 
422  Interp_team_color_set = false;
423 
424  Interp_clip_plane = false;
425 
427  Interp_animated_timer = 0.0f;
428 
429  Interp_detail_level_locked = -1;
430 
431  Interp_forced_bitmap = -1;
432 }
433 
438 {
439  if (mst == NULL) {
440  Int3();
441  return;
442  }
443 
444  Interp_thrust_scale = mst->length.xyz.z;
445  Interp_thrust_scale_x = mst->length.xyz.x;
446  Interp_thrust_scale_y = mst->length.xyz.y;
447 
448  CLAMP(Interp_thrust_scale, 0.1f, 1.0f);
449 
450  Interp_thrust_bitmap = mst->primary_bitmap;
451  Interp_thrust_glow_bitmap = mst->primary_glow_bitmap;
452  Interp_secondary_thrust_glow_bitmap = mst->secondary_glow_bitmap;
453  Interp_tertiary_thrust_glow_bitmap = mst->tertiary_glow_bitmap;
454  Interp_distortion_thrust_bitmap = mst->distortion_bitmap;
455 
456  Interp_thrust_glow_noise = mst->glow_noise;
457  Interp_afterburner = mst->use_ab;
458 
459  Interp_thrust_rotvel = mst->rotvel;
460 
461  Interp_thrust_glow_rad_factor = mst->glow_rad_factor;
462  Interp_secondary_thrust_glow_rad_factor = mst->secondary_glow_rad_factor;
463  Interp_tertiary_thrust_glow_rad_factor = mst->tertiary_glow_rad_factor;
464  Interp_thrust_glow_len_factor = mst->glow_length_factor;
465  Interp_distortion_thrust_rad_factor = mst->distortion_rad_factor;
466  Interp_distortion_thrust_length_factor = mst->distortion_length_factor;
467 
468  Interp_draw_distortion = mst->draw_distortion;
469 }
470 
471 bool splodeing = false;
473 float splode_level = 0.0f;
474 
475 float GEOMETRY_NOISE = 0.0f;
476 
477 // Point list
478 // +0 int id
479 // +4 int size
480 // +8 int n_verts
481 // +12 int n_norms
482 // +16 int offset from start of chunk to vertex data
483 // +20 n_verts*char norm_counts
484 // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
486 {
487  if(dist==0.0f)return;
488 
489  if(dist<0.0f)dist*=-1.0f;
490 
491  int n;
492  int nverts = w(p+8);
493  int offset = w(p+16);
494  int nnorms = 0;
495 
496  ubyte * normcount = p+20;
497  vertex *dest = Interp_splode_points;
498  vec3d *src = vp(p+offset);
499 
500  for (n = 0; n < nverts; n++) {
501  nnorms += normcount[n];
502  }
503 
504  model_allocate_interp_data(nverts, nnorms);
505 
506  vec3d dir;
507 
508  for (n=0; n<nverts; n++ ) {
509  Interp_splode_verts[n] = *src;
510 
511  src++;
512 
513  vm_vec_avg_n(&dir, normcount[n], src);
514  vm_vec_normalize(&dir);
515 
516  for(int i=0; i<normcount[n]; i++)src++;
517 
518  vm_vec_scale_add2(&Interp_splode_verts[n], &dir, dist);
519 
520  g3_rotate_vertex(dest, &Interp_splode_verts[n]);
521 
522  dest++;
523 
524  }
525 
526 }
527 
528 // Point list
529 // +0 int id
530 // +4 int size
531 // +8 int n_verts
532 // +12 int n_norms
533 // +16 int offset from start of chunk to vertex data
534 // +20 n_verts*char norm_counts
535 // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
537 {
539 
540  int i, n;
541  int nverts = w(p+8);
542  int offset = w(p+16);
543  int next_norm = 0;
544  int nnorms = 0;
545 
546  ubyte * normcount = p+20;
547  vertex *dest = NULL;
548  vec3d *src = vp(p+offset);
549 
550  // Get pointer to lights
551  Interp_lights = p+20+nverts;
552 
553  for (i = 0; i < nverts; i++) {
554  nnorms += normcount[i];
555  }
556 
557  // allocate new Interp data if size is greater than we already have ready to use
558  model_allocate_interp_data(nverts, nnorms);
559 
560  dest = Interp_points;
561 
562  Assert( dest != NULL );
563 
564  #ifndef NDEBUG
565  modelstats_num_verts += nverts;
566  #endif
567 
568 
570 
571  // Only scale vertices that aren't on the "base" of
572  // the effect. Base is something Adam decided to be
573  // anything under 1.5 meters, hence the 1.5f.
574  float min_thruster_dist = -1.5f;
575 
576  if ( Interp_flags & MR_IS_MISSILE ) {
577  min_thruster_dist = 0.5f;
578  }
579 
580  for (n=0; n<nverts; n++ ) {
581  vec3d tmp;
582 
583  Interp_verts[n] = src;
584 
585  // Only scale vertices that aren't on the "base" of
586  // the effect. Base is something Adam decided to be
587  // anything under 1.5 meters, hence the 1.5f.
588  if ( src->xyz.z < min_thruster_dist ) {
589  tmp.xyz.x = src->xyz.x * 1.0f;
590  tmp.xyz.y = src->xyz.y * 1.0f;
591  tmp.xyz.z = src->xyz.z * Interp_thrust_scale;
592  } else {
593  tmp = *src;
594  }
595 
596  g3_rotate_vertex(dest,&tmp);
597 
598  src++; // move to normal
599 
600  for (i=0; i<normcount[n]; i++ ) {
601  Interp_light_applied[next_norm] = 0;
602  Interp_norms[next_norm] = src;
603 
604  next_norm++;
605  src++;
606  }
607  dest++;
608  }
609  } else if ( (Interp_warp_scale_x != 1.0f) || (Interp_warp_scale_y != 1.0f) || (Interp_warp_scale_z != 1.0f)) {
610  for (n=0; n<nverts; n++ ) {
611  vec3d tmp;
612 
613  Interp_verts[n] = src;
614 
615  tmp.xyz.x = (src->xyz.x) * Interp_warp_scale_x;
616  tmp.xyz.y = (src->xyz.y) * Interp_warp_scale_y;
617  tmp.xyz.z = (src->xyz.z) * Interp_warp_scale_z;
618 
619  g3_rotate_vertex(dest,&tmp);
620 
621  src++; // move to normal
622 
623  for (i=0; i<normcount[n]; i++ ) {
624  Interp_light_applied[next_norm] = 0;
625  Interp_norms[next_norm] = src;
626 
627  next_norm++;
628  src++;
629  }
630  dest++;
631  }
632  } else {
633  vec3d point;
634 
635  for (n=0; n<nverts; n++ ) {
636 
637  if(!Cmdline_nohtl) {
638  if(GEOMETRY_NOISE!=0.0f){
639  GEOMETRY_NOISE = model_radius / 50;
640 
641  Interp_verts[n] = src;
642  point.xyz.x = src->xyz.x + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
643  point.xyz.y = src->xyz.y + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
644  point.xyz.z = src->xyz.z + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
645 
646  g3_rotate_vertex(dest, &point);
647  }else{
648  Interp_verts[n] = src;
649  g3_rotate_vertex(dest, src);
650  }
651  }
652  else {
653  Interp_verts[n] = src;
654 
655  g3_rotate_vertex(dest, src);
656  }
657 
658  src++; // move to normal
659 
660  for (i=0; i<normcount[n]; i++ ) {
661  Interp_light_applied[next_norm] = 0;
662  Interp_norms[next_norm] = src;
663 
664  next_norm++;
665  src++;
666  }
667  dest++;
668  }
669  }
670 
671  Interp_num_norms = next_norm;
672 }
673 
676 
677 
678 // Flat Poly
679 // +0 int id
680 // +4 int size
681 // +8 vec3d normal
682 // +20 vec3d center
683 // +32 float radius
684 // +36 int nverts
685 // +40 byte red
686 // +41 byte green
687 // +42 byte blue
688 // +43 byte pad
689 // +44 nverts*short*short vertlist, smoothlist
691 {
692  int nv = w(p+36);
693 
694  if ( nv < 0 )
695  return;
696 
697  #ifndef NDEBUG
699  #endif
700 
701  if ( !g3_check_normal_facing(vp(p+20), vp(p+8)) )
702  return;
703 
704 
705  int i;
706  short * verts = (short *)(p+44);
707 
708  int max_n_verts = 0;
709  int max_n_norms = 0;
710 
711  // slow? yes. safe? yes.
712  for (i = 0; i < nv; i++) {
713  max_n_verts = MAX(verts[i*2+0] + 1, max_n_verts);
714  max_n_norms = MAX(verts[i*2+1] + 1, max_n_norms);
715  }
716 
717  model_allocate_interp_data(max_n_verts, max_n_norms, nv);
718 
719  for (i = 0; i < nv; i++) {
720  Interp_list[i] = &Interp_points[verts[i*2]];
721 
722  if ( Interp_flags & MR_NO_LIGHTING ) {
723  Interp_list[i]->r = 191;
724  Interp_list[i]->g = 191;
725  Interp_list[i]->b = 191;
726  } else {
727  int vertnum = verts[i*2+0];
728  int norm = verts[i*2+1];
729 
730  if ( Interp_flags & MR_NO_SMOOTHING ) {
731  light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
732  } else {
733  // if we're not using saved lighting
734  if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
735  light_apply_rgb( &Interp_lighting->lights[norm].r, &Interp_lighting->lights[norm].g, &Interp_lighting->lights[norm].b, Interp_verts[vertnum], vp(p+8), Interp_light );
736  Interp_light_applied[norm] = 1;
737  }
738 
739  Interp_list[i]->r = Interp_lighting->lights[norm].r;
740  Interp_list[i]->g = Interp_lighting->lights[norm].g;
741  Interp_list[i]->b = Interp_lighting->lights[norm].b;
742  }
743  }
744  }
745 
746  // HACK!!! FIX ME!!! I'M SLOW!!!!
747  if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
748  gr_set_color( *(p+40), *(p+41), *(p+42) );
749  }
750 
751  if ( !(Interp_flags & MR_NO_POLYS)) {
752  g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB );
753  }
754 
755  if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET)) {
756  int j;
757 
758  if ( Interp_flags & MR_SHOW_OUTLINE ) {
759  gr_set_color_fast( &Interp_outline_color );
760  }
761 
762  for (i = 0; i < nv; i++) {
763  j = (i + 1) % nv;
764  g3_draw_line(Interp_list[i], Interp_list[j]);
765  }
766  }
767 }
768 
769 void model_interp_edge_alpha( ubyte *param_r, ubyte *param_g, ubyte *param_b, vec3d *pnt, vec3d *norm, float alpha, bool invert = false)
770 {
771  vec3d r;
772 
773  vm_vec_sub(&r, &View_position, pnt);
774  vm_vec_normalize(&r);
775 
776  float d = vm_vec_dot(&r, norm);
777 
778  if (d < 0.0f)
779  d = -d;
780 
781  if (invert)
782  *param_r = *param_g = *param_b = ubyte( fl2i((1.0f - d) * 254.0f * alpha));
783  else
784  *param_r = *param_g = *param_b = ubyte( fl2i(d * 254.0f * alpha) );
785 }
786 
787 // Textured Poly
788 // +0 int id
789 // +4 int size
790 // +8 vec3d normal
791 // +20 vec3d center
792 // +32 float radius
793 // +36 int nverts
794 // +40 int tmap_num
795 // +44 nverts*(model_tmap_vert) vertlist (n,u,v)
796 extern int Tmap_show_layers;
797 
804 
806 {
807  int i;
808  int nv;
809  model_tmap_vert *verts;
810  int cull = 0;
811 
812  // Goober5000
813  int tmap_num = w(p+40);
814  texture_map *tmap = &pm->maps[tmap_num];
815  texture_info *tbase = &tmap->textures[TM_BASE_TYPE];
816  texture_info *tglow = &tmap->textures[TM_GLOW_TYPE];
817  int rt_begin_index = tmap_num*TM_NUM_TYPES;
818 
819  int is_invisible = 0;
820 
821  if (Interp_warp_bitmap < 0) {
822  if ( (!Interp_thrust_scale_subobj) && (tbase->GetTexture() < 0) ) {
823  // Ignore the following if we're drawing in outline mode. Fixes Mantis #2931.
824  if (!(Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET))) {
825  // Don't draw invisible polygons.
826  if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES) )
827  return;
828  else
829  is_invisible = 1;
830  }
831  }
832  }
833 
834  nv = w(p+36);
835 
836  #ifndef NDEBUG
838  #endif
839 
840  if ( nv < 0 )
841  return;
842 
843  verts = (model_tmap_vert *)(p+44);
844 
845  int max_n_verts = 0;
846  int max_n_norms = 0;
847 
848  for (i = 0; i < nv; i++) {
849  max_n_verts = MAX(verts[i].vertnum + 1, max_n_verts);
850  max_n_norms = MAX(verts[i].normnum + 1, max_n_norms);
851  }
852 
853  model_allocate_interp_data(max_n_verts, max_n_norms, nv);
854 
855  if ( !Cmdline_nohtl ) {
856  if (Interp_warp_bitmap < 0) {
857  if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)){
858  if(!splodeing) return;
859  }
860  }
861 
862  if(splodeing){
863  float salpha = 1.0f - splode_level;
864  for (i=0;i<nv;i++){
865  Interp_list[i] = &Interp_splode_points[verts[i].vertnum];
866  Interp_list[i]->texture_position.u = verts[i].u*2;
867  Interp_list[i]->texture_position.v = verts[i].v*2;
868  Interp_list[i]->r = (unsigned char)(255*salpha);
869  Interp_list[i]->g = (unsigned char)(250*salpha);
870  Interp_list[i]->b = (unsigned char)(200*salpha);
871  model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], salpha, false);
872  }
873  cull = gr_set_cull(0);
876  gr_set_cull(cull);
877  return;
878  }
879  }
880 
881  for (i=0;i<nv;i++) {
882  Interp_list[i] = &Interp_points[verts[i].vertnum];
883 
884  Interp_list[i]->texture_position.u = verts[i].u;
885  Interp_list[i]->texture_position.v = verts[i].v;
886 
887  if ( Interp_subspace ) {
888  Interp_list[i]->texture_position.v += Interp_subspace_offset_u;
889  Interp_list[i]->texture_position.u += Interp_subspace_offset_v;
890  Interp_list[i]->r = Interp_subspace_r;
891  Interp_list[i]->g = Interp_subspace_g;
892  Interp_list[i]->b = Interp_subspace_b;
893  } else {
894  if ( (Interp_flags & MR_NO_LIGHTING) || (tmap->is_ambient)) { //gets the ambient glow to work
895  Interp_list[i]->r = 191;
896  Interp_list[i]->g = 191;
897  Interp_list[i]->b = 191;
898 
899  Interp_list[i]->spec_r = 0;
900  Interp_list[i]->spec_g = 0;
901  Interp_list[i]->spec_b = 0;
902 
903  if (Interp_flags & MR_EDGE_ALPHA) {
904  model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], Interp_warp_alpha, false);
905  }
906 
907  if (Interp_flags & MR_CENTER_ALPHA) {
908  model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], Interp_warp_alpha, true);
909  }
910 
911  SPECMAP = -1;
912  NORMMAP = -1;
913  HEIGHTMAP = -1;
914  MISCMAP = -1;
915  } else {
916 
917  int vertnum = verts[i].vertnum;
918  int norm = verts[i].normnum;
919 
920  if ( Interp_flags & MR_NO_SMOOTHING ) {
921  light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
922  if((Detail.lighting > 2) && (Interp_detail_level < 2) && Cmdline_spec )
923  light_apply_specular( &Interp_list[i]->spec_r, &Interp_list[i]->spec_g, &Interp_list[i]->spec_b, Interp_verts[vertnum], vp(p+8), &View_position);
924  } else {
925  // if we're applying lighting as normal, and not using saved lighting
926  if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
927 
928  light_apply_rgb( &Interp_lighting->lights[norm].r, &Interp_lighting->lights[norm].g, &Interp_lighting->lights[norm].b, Interp_verts[vertnum], Interp_norms[norm], Interp_light );
929  if((Detail.lighting > 2) && (Interp_detail_level < 2) && Cmdline_spec )
930  light_apply_specular( &Interp_lighting->lights[norm].spec_r, &Interp_lighting->lights[norm].spec_g, &Interp_lighting->lights[norm].spec_b, Interp_verts[vertnum], Interp_norms[norm], &View_position);
931 
932  Interp_light_applied[norm] = 1;
933  }
934 
935  Interp_list[i]->spec_r = Interp_lighting->lights[norm].spec_r;
936  Interp_list[i]->spec_g = Interp_lighting->lights[norm].spec_g;
937  Interp_list[i]->spec_b = Interp_lighting->lights[norm].spec_b;
938 
939  Interp_list[i]->r = Interp_lighting->lights[norm].r;
940  Interp_list[i]->g = Interp_lighting->lights[norm].g;
941  Interp_list[i]->b = Interp_lighting->lights[norm].b;
942  }
943  }
944  }
945  }
946 
947  #ifndef NDEBUG
949  #endif
950 
951  if (!(Interp_flags & MR_NO_POLYS) ) {
952  if ( is_invisible ) {
953  gr_set_color( 0, 255, 0 );
954  g3_draw_poly( nv, Interp_list, 0 );
955  } else if (Interp_thrust_scale_subobj) {
956  if ( (Interp_thrust_bitmap >= 0) && (Interp_thrust_scale > 0.0f) ) {
957  gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f );
958  g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );
959  } else {
960  if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
961  gr_set_color( 128, 128, 255 );
962  }
963  uint tflags = Interp_tmap_flags;
965  g3_draw_poly( nv, Interp_list, tflags );
966  }
967  } else if (Interp_warp_bitmap >= 0) { //warpin effect-Bobboau
968  gr_set_bitmap( Interp_warp_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_warp_alpha );
969  g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );
970  }else{
971  // all textured polys go through here
972  if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED ) {
973  // subspace special case
974  if (Interp_subspace) {
976  }
977  // all other textures
978  else {
979  int texture;
980 
981  // if we're rendering a nebula background pof, maybe select a custom texture
982  if(Interp_forced_bitmap >= 0){
983  texture = Interp_forced_bitmap;
984  }else if((Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0)){
985  texture_info tt = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE]);
986  texture = model_interp_get_texture(&tt, Interp_base_frametime);
987  } else {
988  // pick the texture, animating it if necessary
989  texture = model_interp_get_texture(tbase, Interp_base_frametime);
990 
991  // doing glow maps?
992  if ( !(Interp_flags & MR_NO_GLOWMAPS) && (tglow->GetTexture() >= 0) ) {
993  // shockwaves are special, their current frame has to come out of the shockwave code to get the timing correct
994  if ( (Interp_objnum >= 0) && (Objects[Interp_objnum].type == OBJ_SHOCKWAVE) && (tglow->GetNumFrames() > 1) ) {
995  GLOWMAP = tglow->GetTexture() + shockwave_get_framenum(Objects[Interp_objnum].instance, tglow->GetNumFrames());
996  } else {
997  GLOWMAP = model_interp_get_texture(tglow, Interp_base_frametime);
998  }
999  }
1000 
1001  if((Detail.lighting > 2) && (Interp_detail_level < 2))
1002  {
1003  // likewise, etc.
1004  SPECMAP = model_interp_get_texture(&tmap->textures[TM_SPECULAR_TYPE], Interp_base_frametime);
1005  NORMMAP = model_interp_get_texture(&tmap->textures[TM_NORMAL_TYPE], Interp_base_frametime);
1006  HEIGHTMAP = model_interp_get_texture(&tmap->textures[TM_HEIGHT_TYPE], Interp_base_frametime);
1007  }
1008 
1009  MISCMAP = model_interp_get_texture(&tmap->textures[TM_MISC_TYPE], Interp_base_frametime);
1010  }
1011 
1012  //*****
1013  //WMC - now do other replacements
1014  if(Interp_new_replacement_textures != NULL)
1015  {
1016  texture_info tinfo;
1017  for(int tmn = TM_BASE_TYPE+1; tmn < TM_NUM_TYPES; tmn++)
1018  {
1019  int tex = Interp_new_replacement_textures[rt_begin_index + tmn];
1020  if(tex < 0)
1021  continue;
1022 
1023  tinfo = texture_info(tex);
1024 
1025  //Figure out actual texture to use
1026  tex = model_interp_get_texture(&tinfo, Interp_base_frametime);
1027  switch(tmn)
1028  {
1029  case TM_GLOW_TYPE:
1030  GLOWMAP = tex;
1031  break;
1032  case TM_SPECULAR_TYPE:
1033  SPECMAP = tex;
1034  break;
1035  case TM_NORMAL_TYPE:
1036  NORMMAP = tex;
1037  break;
1038  case TM_HEIGHT_TYPE:
1039  HEIGHTMAP = tex;
1040  break;
1041  case TM_MISC_TYPE:
1042  MISCMAP = tex;
1043  default:
1044  break;
1045  }
1046  }
1047  }
1048 
1049  // muzzle flashes draw xparent
1050  if(Interp_flags & MR_ALL_XPARENT){
1051  gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha );
1052  } else {
1053  if(tmap->is_transparent) { //trying to get transperent textures-Bobboau
1055  }else{
1056  gr_set_bitmap( texture );
1057  }
1058  }
1059  }
1060  } else {
1061  if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
1062  gr_set_color( 128, 128, 128 );
1063  }
1064  }
1065 
1066  if ( Interp_subspace ) {
1067  g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT );
1068  } else {
1069  if(Interp_flags & MR_ALL_XPARENT){
1070  g3_draw_poly( nv, Interp_list, Interp_tmap_flags );
1071  } else {
1072  g3_draw_poly( nv, Interp_list, Interp_tmap_flags );
1073  }
1074  }
1075  }
1076  }
1077 
1078  GLOWMAP = -1;
1079  SPECMAP = -1;
1080  NORMMAP = -1;
1081  HEIGHTMAP = -1;
1082  MISCMAP = -1;
1083 
1084  if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) ) {
1085 
1086  if ( Interp_flags & MR_SHOW_OUTLINE ) {
1087  gr_set_color_fast( &Interp_outline_color );
1088  }
1089 
1090  for (i=0; i<nv; i++ ) {
1091  int j = (i + 1) % nv;
1092  g3_draw_line(Interp_list[i], Interp_list[j]);
1093  }
1094  }
1095 
1096 }
1097 
1098 // Sortnorms
1099 // +0 int id
1100 // +4 int size
1101 // +8 vec3d normal
1102 // +20 vec3d normal_point
1103 // +32 int tmp=0
1104 // 36 int front offset
1105 // 40 int back offset
1106 // 44 int prelist offset
1107 // 48 int postlist offset
1108 // 52 int online offset
1109 void model_interp_sortnorm_b2f(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
1110 {
1111  #ifndef NDEBUG
1113  #endif
1114 
1115  int frontlist = w(p+36);
1116  int backlist = w(p+40);
1117  int prelist = w(p+44);
1118  int postlist = w(p+48);
1119  int onlist = w(p+52);
1120 
1121  if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
1122 
1123  if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
1124 
1125  //draw back then front
1126 
1127  if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1128 
1129  if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1130 
1131  if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1132 
1133  } else { //not facing. draw front then back
1134 
1135  if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1136 
1137  if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1138 
1139  if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1140  }
1141 
1142  if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
1143 
1144 }
1145 
1146 // Sortnorms
1147 // +0 int id
1148 // +4 int size
1149 // +8 vec3d normal
1150 // +20 vec3d normal_point
1151 // +32 int tmp=0
1152 // 36 int front offset
1153 // 40 int back offset
1154 // 44 int prelist offset
1155 // 48 int postlist offset
1156 // 52 int online offset
1157 void model_interp_sortnorm_f2b(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
1158 {
1159  #ifndef NDEBUG
1161  #endif
1162 
1163  int frontlist = w(p+36);
1164  int backlist = w(p+40);
1165  int prelist = w(p+44);
1166  int postlist = w(p+48);
1167  int onlist = w(p+52);
1168 
1169  if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
1170 
1171  if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
1172 
1173  if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1174 
1175  if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1176 
1177  if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1178 
1179  } else { //not facing. draw front then back
1180 
1181  //draw back then front
1182 
1183  if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
1184 
1185  if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
1186 
1187  if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
1188 
1189  }
1190 
1191  if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
1192 }
1193 
1195 {
1196  if ( flags & MR_SHOW_OUTLINE_PRESET ) {
1197  return;
1198  }
1199 
1200  // Draw a red pivot point
1201  gr_set_color(128,0,0);
1203 
1204  // Draw a green center of mass when drawing the hull
1205  if ( submodel && (submodel->parent==-1) ) {
1206  gr_set_color(0,128,0);
1207  g3_draw_sphere_ez( &pm->center_of_mass, 1.0f );
1208  }
1209 
1210  if ( submodel ) {
1211  // Draw a blue center point
1212  gr_set_color(0,0,128);
1213  g3_draw_sphere_ez( &submodel->geometric_center, 0.9f );
1214  }
1215 
1216  // Draw the bounding box
1217  int i;
1218  vertex pts[8];
1219 
1220  if ( submodel ) {
1221  for (i=0; i<8; i++ ) {
1222  g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] );
1223  }
1224  gr_set_color(128,128,128);
1225  g3_draw_line( &pts[0], &pts[1] );
1226  g3_draw_line( &pts[1], &pts[2] );
1227  g3_draw_line( &pts[2], &pts[3] );
1228  g3_draw_line( &pts[3], &pts[0] );
1229 
1230  g3_draw_line( &pts[4], &pts[5] );
1231  g3_draw_line( &pts[5], &pts[6] );
1232  g3_draw_line( &pts[6], &pts[7] );
1233  g3_draw_line( &pts[7], &pts[4] );
1234 
1235  g3_draw_line( &pts[0], &pts[4] );
1236  g3_draw_line( &pts[1], &pts[5] );
1237  g3_draw_line( &pts[2], &pts[6] );
1238  g3_draw_line( &pts[3], &pts[7] );
1239  } else {
1240  gr_set_color(0,255,0);
1241 
1242  int j;
1243  for (j=0; j<8; j++ ) {
1244 
1245  vec3d bounding_box[8]; // caclulated fron min/max
1246  model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max);
1247 
1248  for (i=0; i<8; i++ ) {
1249  g3_rotate_vertex( &pts[i], &bounding_box[i] );
1250  }
1251  gr_set_color(128,0,0);
1252  g3_draw_line( &pts[0], &pts[1] );
1253  g3_draw_line( &pts[1], &pts[2] );
1254  g3_draw_line( &pts[2], &pts[3] );
1255  g3_draw_line( &pts[3], &pts[0] );
1256 
1257  g3_draw_line( &pts[4], &pts[5] );
1258  g3_draw_line( &pts[5], &pts[6] );
1259  g3_draw_line( &pts[6], &pts[7] );
1260  g3_draw_line( &pts[7], &pts[4] );
1261 
1262  g3_draw_line( &pts[0], &pts[4] );
1263  g3_draw_line( &pts[1], &pts[5] );
1264  g3_draw_line( &pts[2], &pts[6] );
1265  g3_draw_line( &pts[3], &pts[7] );
1266  }
1267  }
1268 }
1269 
1274 {
1275  int i,j;
1276  vec3d pnt;
1277  polymodel * pm;
1278 
1279  if ( flags & MR_SHOW_OUTLINE_PRESET ) {
1280  return;
1281  }
1282 
1283  pm = model_get(model_num);
1284 
1285  if (pm->n_paths<1){
1286  return;
1287  }
1288 
1289  for (i=0; i<pm->n_paths; i++ ) {
1290  vertex prev_pnt;
1291 
1292  for (j=0; j<pm->paths[i].nverts; j++ ) {
1293  // Rotate point into world coordinates
1294  pnt = pm->paths[i].verts[j].pos;
1295 
1296  // Pnt is now the x,y,z world coordinates of this vert.
1297  // For this example, I am just drawing a sphere at that
1298  // point.
1299  {
1300  vertex tmp = vertex();
1301  g3_rotate_vertex(&tmp,&pnt);
1302 
1303  if ( pm->paths[i].verts[j].nturrets > 0 ){
1304  gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
1305  } else {
1306  gr_set_color( 255, 0, 0 );
1307  }
1308 
1309  g3_draw_sphere( &tmp, 0.5f );
1310 
1311  if (j){
1312  g3_draw_line(&prev_pnt, &tmp);
1313  }
1314 
1315  prev_pnt = tmp;
1316  }
1317  }
1318  }
1319 }
1320 
1325 {
1326  int i,j;
1327  vec3d pnt;
1328  vec3d prev_pnt;
1329  polymodel * pm;
1330 
1331  if ( flags & MR_SHOW_OUTLINE_PRESET ) {
1332  return;
1333  }
1334 
1335  pm = model_get(model_num);
1336 
1337  if (pm->n_paths<1){
1338  return;
1339  }
1340 
1341  int cull = gr_set_cull(0);
1342  for (i=0; i<pm->n_paths; i++ ) {
1343  for (j=0; j<pm->paths[i].nverts; j++ )
1344  {
1345  // Rotate point into world coordinates
1346  pnt = pm->paths[i].verts[j].pos;
1347 
1348  // Pnt is now the x,y,z world coordinates of this vert.
1349  // For this example, I am just drawing a sphere at that
1350  // point.
1351 
1352  vertex tmp;
1353  g3_rotate_vertex(&tmp,&pnt);
1354 
1355  if ( pm->paths[i].verts[j].nturrets > 0 ){
1356  gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
1357  } else {
1358  gr_set_color( 255, 0, 0 );
1359  }
1360 
1361  g3_draw_htl_sphere(&pnt, 0.5f);
1362 
1363  if (j)
1364  {
1365  g3_draw_htl_line(&prev_pnt, &pnt);
1366  }
1367 
1368  prev_pnt = pnt;
1369  }
1370  }
1371 
1372  gr_set_cull(cull);
1373 }
1374 
1379 {
1380  int idx, s_idx;
1381  vec3d v1, v2;
1382 
1383  polymodel *pm = model_get(model_num);
1384  if(pm == NULL){
1385  return;
1386  }
1387 
1388  int cull = gr_set_cull(0);
1389  // render docking bay normals
1390  gr_set_color(0, 255, 0);
1391  for(idx=0; idx<pm->n_docks; idx++){
1392  for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
1393  v1 = pm->docking_bays[idx].pnt[s_idx];
1394  vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
1395 
1396  // draw the point and normal
1397  g3_draw_htl_sphere(&v1, 2.0);
1398  g3_draw_htl_line(&v1, &v2);
1399  }
1400  }
1401 
1402  // render figher bay paths
1403  gr_set_color(0, 255, 255);
1404 
1405  // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
1406  for (idx = 0; idx<pm->n_paths; idx++) {
1407  if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
1408  for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
1409  v1 = pm->paths[idx].verts[s_idx].pos;
1410  v2 = pm->paths[idx].verts[s_idx+1].pos;
1411 
1412  g3_draw_htl_line(&v1, &v2);
1413  }
1414  }
1415  }
1416 
1417  gr_set_cull(cull);
1418 }
1419 
1424 {
1425  int idx, s_idx;
1426  vec3d v1, v2;
1427  vertex l1, l2;
1428 
1429  polymodel *pm = model_get(model_num);
1430  if(pm == NULL){
1431  return;
1432  }
1433 
1434  // render docking bay normals
1435  gr_set_color(0, 255, 0);
1436  for(idx=0; idx<pm->n_docks; idx++){
1437  for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
1438  v1 = pm->docking_bays[idx].pnt[s_idx];
1439  vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
1440 
1441  // rotate the points
1442  g3_rotate_vertex(&l1, &v1);
1443  g3_rotate_vertex(&l2, &v2);
1444 
1445  // draw the point and normal
1446  g3_draw_sphere(&l1, 2.0);
1447  g3_draw_line(&l1, &l2);
1448  }
1449  }
1450 
1451  // render figher bay paths
1452  gr_set_color(0, 255, 255);
1453 
1454  // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
1455  for (idx = 0; idx<pm->n_paths; idx++) {
1456  if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
1457  for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
1458  v1 = pm->paths[idx].verts[s_idx].pos;
1459  v2 = pm->paths[idx].verts[s_idx+1].pos;
1460 
1461  // rotate and draw
1462  g3_rotate_vertex(&l1, &v1);
1463  g3_rotate_vertex(&l2, &v2);
1464  g3_draw_line(&l1, &l2);
1465  }
1466  }
1467  }
1468 }
1469 
1470 
1471 static const int MAX_ARC_SEGMENT_POINTS = 50;
1474 
1475 extern int g3_draw_rod(int num_points, const vec3d *vecs, float width, uint tmap_flags);
1476 
1478 {
1479  float d = vm_vec_dist_quick( v1, v2 );
1480  const float scaler = 0.30f;
1481 
1482  if ( (d < scaler) || (depth > 4) ) {
1483  // the real limit appears to be 33, so we should never hit this unless the code changes
1485 
1486  memcpy( &Arc_segment_points[Num_arc_segment_points++], v2, sizeof(vec3d) );
1487  } else {
1488  // divide in half
1489  vec3d tmp;
1490  vm_vec_avg( &tmp, v1, v2 );
1491 
1492  tmp.xyz.x += (frand() - 0.5f) * d * scaler;
1493  tmp.xyz.y += (frand() - 0.5f) * d * scaler;
1494  tmp.xyz.z += (frand() - 0.5f) * d * scaler;
1495 
1496  // add additional point
1497  interp_render_arc_segment( v1, &tmp, depth+1 );
1498  interp_render_arc_segment( &tmp, v2, depth+1 );
1499  }
1500 }
1501 
1502 void interp_render_arc(vec3d *v1, vec3d *v2, color *primary, color *secondary, float arc_width)
1503 {
1505 
1506  // need need to add the first point
1507  memcpy( &Arc_segment_points[Num_arc_segment_points++], v1, sizeof(vec3d) );
1508 
1509  // this should fill in all of the middle, and the last, points
1510  interp_render_arc_segment(v1, v2, 0);
1511 
1513 
1515 
1516  // use primary color for fist pass
1517  Assert( primary );
1518  gr_set_color_fast(primary);
1519 
1520  g3_draw_rod(Num_arc_segment_points, Arc_segment_points, arc_width, tmap_flags);
1521 
1522  if (secondary) {
1523  // now render again with a secondary center color
1524  gr_set_color_fast(secondary);
1525 
1526  g3_draw_rod(Num_arc_segment_points, Arc_segment_points, arc_width * 0.33f, tmap_flags);
1527  }
1528 
1529  gr_zbuffer_set(mode);
1530 }
1531 
1533 DCF_BOOL( Arcs, Interp_lightning )
1534 
1535 const int AR = 64;
1536 const int AG = 64;
1537 const int AB = 5;
1538 const int AR2 = 128;
1539 const int AG2 = 128;
1540 const int AB2 = 10;
1541 
1543 {
1544  int i;
1545  float width = 0.9f;
1546  color primary, secondary;
1547 
1548  Assert( sm->num_arcs > 0 );
1549 
1550  if (Interp_flags & MR_SHOW_OUTLINE_PRESET) {
1551  return;
1552  }
1553 
1554  if ( !Interp_lightning ) {
1555  return;
1556  }
1557 
1558  // try and scale the size a bit so that it looks equally well on smaller vessels
1559  if (pm->rad < 500.0f) {
1560  width *= (pm->rad * 0.01f);
1561 
1562  if (width < 0.2f)
1563  width = 0.2f;
1564  }
1565 
1566  for (i=0; i<sm->num_arcs; i++ ) {
1567  // pick a color based upon arc type
1568  switch(sm->arc_type[i]){
1569  // "normal", FreeSpace 1 style arcs
1570  case MARC_TYPE_NORMAL:
1571  if ( (rand()>>4) & 1 ) {
1572  gr_init_color(&primary, 64, 64, 255);
1573  } else {
1574  gr_init_color(&primary, 128, 128, 255);
1575  }
1576 
1577  gr_init_color(&secondary, 200, 200, 255);
1578  break;
1579 
1580  // "EMP" style arcs
1581  case MARC_TYPE_EMP:
1582  if ( (rand()>>4) & 1 ) {
1583  gr_init_color(&primary, AR, AG, AB);
1584  } else {
1585  gr_init_color(&primary, AR2, AG2, AB2);
1586  }
1587 
1588  gr_init_color(&secondary, 255, 255, 10);
1589  break;
1590 
1591  default:
1592  Int3();
1593  }
1594 
1595  // render the actual arc segment
1596  interp_render_arc( &sm->arc_pts[i][0], &sm->arc_pts[i][1], &primary, &secondary, width );
1597  }
1598 }
1599 
1600 void model_interp_subcall(polymodel * pm, int mn, int detail_level)
1601 {
1602  int i;
1603  int zbuf_mode = gr_zbuffering_mode;
1604 
1605  if ( (mn < 0) || (mn>=pm->n_models) )
1606  return;
1607 
1608  Assert( mn >= 0 );
1609  Assert( mn < pm->n_models );
1610 
1611  if (pm->submodel[mn].blown_off){
1612  return;
1613  }
1614 
1615  if (pm->submodel[mn].is_thruster ) {
1616  if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){
1617  return;
1618  }
1620  } else {
1622  }
1623 
1624  // Compute final submodel orientation by using the orientation matrix and the rotation angles.
1625  // By using this kind of computation, the rotational angles can always be computed relative
1626  // to the submodel itself, instead of relative to the parent - KeldorKatarn
1627  matrix rotation_matrix = pm->submodel[mn].orientation;
1628  vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[mn].angs);
1629 
1630  matrix inv_orientation;
1631  vm_copy_transpose(&inv_orientation, &pm->submodel[mn].orientation);
1632 
1633  matrix submodel_matrix;
1634  vm_matrix_x_matrix(&submodel_matrix, &rotation_matrix, &inv_orientation);
1635 
1636  g3_start_instance_matrix(&pm->submodel[mn].offset, &submodel_matrix, true);
1637 
1638  if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1639  light_rotate_all();
1640  }
1641 
1642  model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 );
1643 
1644  if (Interp_flags & MR_DEPRECATED_SHOW_PIVOTS )
1645  model_draw_debug_points( pm, &pm->submodel[mn], Interp_flags );
1646 
1647  if ( pm->submodel[mn].num_arcs ) {
1648  interp_render_lightning( pm, &pm->submodel[mn]);
1649  }
1650 
1651  i = pm->submodel[mn].first_child;
1652  while( i >= 0 ) {
1653  if (!pm->submodel[i].is_thruster ) {
1654  if(Interp_flags & MR_NO_ZBUFFER){
1655  zbuf_mode = GR_ZBUFF_NONE;
1656  } else {
1657  zbuf_mode = GR_ZBUFF_FULL; // read only
1658  }
1659 
1660  gr_zbuffer_set(zbuf_mode);
1661 
1662  model_interp_subcall( pm, i, detail_level );
1663  }
1664  i = pm->submodel[i].next_sibling;
1665  }
1666 
1667  g3_done_instance(true);
1668 }
1669 
1670 // Returns one of the following
1671 #define IBOX_ALL_OFF 0
1672 #define IBOX_ALL_ON 1
1673 #define IBOX_SOME_ON_SOME_OFF 2
1674 
1676 {
1677  if ( keyd_pressed[KEY_LSHIFT] ) {
1678  return IBOX_ALL_ON;
1679  }
1680 
1681  vec3d v[8];
1682  v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z;
1683  v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z;
1684  v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z;
1685  v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z;
1686 
1687  v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z;
1688  v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z;
1689  v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z;
1690  v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z;
1691 
1692  ubyte and_codes = 0xff;
1693  ubyte or_codes = 0xff;
1694  int i;
1695 
1696  for (i=0; i<8; i++ ) {
1697  vertex tmp;
1698  ubyte codes=g3_rotate_vertex( &tmp, &v[i] );
1699 
1700  or_codes |= codes;
1701  and_codes &= codes;
1702  }
1703 
1704  // If and_codes is set this means that all points lie off to the
1705  // same side of the screen.
1706  if (and_codes) {
1707  return IBOX_ALL_OFF; //all points off screen
1708  }
1709 
1710  // If this is set it means at least one of the points is offscreen,
1711  // but they aren't all off to the same side.
1712  if (or_codes) {
1713  return IBOX_SOME_ON_SOME_OFF;
1714  }
1715 
1716  // They are all onscreen.
1717  return IBOX_ALL_ON;
1718 }
1719 
1720 
1725 int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check )
1726 {
1727  ubyte *p = (ubyte *)model_ptr;
1728  int chunk_type, chunk_size;
1729  int pushed = 0;
1730 
1731  chunk_type = w(p);
1732  chunk_size = w(p+4);
1733 
1734 
1735  while ( chunk_type != OP_EOF ) {
1736 
1737  switch (chunk_type) {
1738  case OP_DEFPOINTS: model_interp_defpoints(p,pm,sm); break;
1739  case OP_FLATPOLY: model_interp_flatpoly(p,pm); break;
1740  case OP_TMAPPOLY: model_interp_tmappoly(p,pm); break;
1741  case OP_SORTNORM: model_interp_sortnorm(p,pm,sm,do_box_check); break;
1742 
1743  case OP_BOUNDBOX:
1744 
1745  if ( do_box_check ) {
1746  int retval = interp_box_offscreen( vp(p+8), vp(p+20) );
1747  switch( retval ) {
1748  case IBOX_ALL_OFF:
1749  goto DoneWithThis; // Don't need to draw any more polys from this box
1750  break;
1751 
1752  case IBOX_ALL_ON:
1753  do_box_check = 0; // Don't need to check boxes any more
1754  break;
1755 
1756  case IBOX_SOME_ON_SOME_OFF:
1757  // continue like we were
1758  break;
1759  default:
1760  Int3();
1761  }
1762  }
1763 
1764  if (Interp_flags & MR_DEPRECATED_SHOW_PIVOTS ) {
1765  #ifndef NDEBUG
1767  #endif
1768  }
1769 
1770  if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1771  if ( pushed ) {
1772  light_filter_pop();
1773  pushed = 0;
1774 
1775  }
1776  light_filter_push_box( vp(p+8), vp(p+20) );
1777  pushed = 1;
1778  }
1779  break;
1780 
1781  default:
1782  mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size ));
1783  Int3(); // Bad chunk type!
1784  return 0;
1785  }
1786  p += chunk_size;
1787  chunk_type = w(p);
1788  chunk_size = w(p+4);
1789  }
1790 
1791 DoneWithThis:
1792 
1793  if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
1794  if ( pushed ) {
1795  light_filter_pop();
1796  pushed = 0;
1797  }
1798  }
1799 
1800  return 1;
1801 }
1802 
1803 
1805 {
1806  int i, j;
1807  shield_tri *tri;
1808  vertex pnt0, prev_pnt, tmp = vertex();
1809 
1810  if ( flags & MR_SHOW_OUTLINE_PRESET ) {
1811  return;
1812  }
1813 
1814  gr_set_color(0, 0, 200 );
1815 
1816  // Scan all the triangles in the mesh.
1817  for (i=0; i<pm->shield.ntris; i++ ) {
1818 
1819  tri = &pm->shield.tris[i];
1820 
1821  if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) {
1822 
1823  // Process the vertices.
1824  // Note this rotates each vertex each time it's needed, very dumb.
1825  for (j=0; j<3; j++ ) {
1826 
1827  g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos );
1828 
1829  if (j)
1830  g3_draw_line(&prev_pnt, &tmp);
1831  else
1832  pnt0 = tmp;
1833  prev_pnt = tmp;
1834  }
1835 
1836  g3_draw_line(&pnt0, &prev_pnt);
1837  }
1838  }
1839 }
1840 
1841 void model_render_insignias(polymodel *pm, int detail_level, int bitmap_num)
1842 {
1843  // if the model has no insignias, or we don't have a texture, then bail
1844  if ( (pm->num_ins <= 0) || (bitmap_num < 0) )
1845  return;
1846 
1847  int idx, s_idx;
1848  vertex vecs[3];
1849  vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] };
1850  vec3d t1, t2, t3;
1851  int i1, i2, i3;
1853 
1854  // set the proper texture
1856 
1857  // otherwise render them
1858  for(idx=0; idx<pm->num_ins; idx++){
1859  // skip insignias not on our detail level
1860  if(pm->ins[idx].detail_level != detail_level){
1861  continue;
1862  }
1863 
1864  for(s_idx=0; s_idx<pm->ins[idx].num_faces; s_idx++){
1865  // get vertex indices
1866  i1 = pm->ins[idx].faces[s_idx][0];
1867  i2 = pm->ins[idx].faces[s_idx][1];
1868  i3 = pm->ins[idx].faces[s_idx][2];
1869 
1870  // transform vecs and setup vertices
1871  vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset);
1872  vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset);
1873  vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset);
1874 
1875  if(Cmdline_nohtl){
1876  g3_rotate_vertex(&vecs[0], &t1);
1877  g3_rotate_vertex(&vecs[1], &t2);
1878  g3_rotate_vertex(&vecs[2], &t3);
1879  }else{
1880  g3_transfer_vertex(&vecs[0], &t1);
1881  g3_transfer_vertex(&vecs[1], &t2);
1882  g3_transfer_vertex(&vecs[2], &t3);
1883  }
1884 
1885  // setup texture coords
1886  vecs[0].texture_position.u = pm->ins[idx].u[s_idx][0];
1887  vecs[0].texture_position.v = pm->ins[idx].v[s_idx][0];
1888 
1889  vecs[1].texture_position.u = pm->ins[idx].u[s_idx][1];
1890  vecs[1].texture_position.v = pm->ins[idx].v[s_idx][1];
1891 
1892  vecs[2].texture_position.u = pm->ins[idx].u[s_idx][2];
1893  vecs[2].texture_position.v = pm->ins[idx].v[s_idx][2];
1894 
1895  if (!Cmdline_nohtl) {
1896  light_apply_rgb( &vecs[0].r, &vecs[0].g, &vecs[0].b, &pm->ins[idx].vecs[i1], &pm->ins[idx].norm[i1], 1.5f );
1897  light_apply_rgb( &vecs[1].r, &vecs[1].g, &vecs[1].b, &pm->ins[idx].vecs[i2], &pm->ins[idx].norm[i2], 1.5f );
1898  light_apply_rgb( &vecs[2].r, &vecs[2].g, &vecs[2].b, &pm->ins[idx].vecs[i3], &pm->ins[idx].norm[i3], 1.5f );
1899  tmap_flags |= (TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD);
1900  }
1901 
1902  // draw the polygon
1903  g3_draw_poly(3, vlist, tmap_flags);
1904  }
1905  }
1906 }
1907 
1909 int Model_polys = 1;
1910 
1911 DCF_BOOL( model_texturing, Model_texturing )
1912 DCF_BOOL( model_polys, Model_polys )
1913 
1914 MONITOR( NumModelsRend )
1915 MONITOR( NumHiModelsRend )
1916 MONITOR( NumMedModelsRend )
1917 MONITOR( NumLowModelsRend )
1918 
1924 {
1925  float sa, ca;
1926  int i;
1927 
1928  Assert( G3_count == 1 );
1929 
1930  sa = sinf(angle);
1931  ca = cosf(angle);
1932 
1933  float width, height;
1934 
1935  width = height = rad;
1936 
1937  v[0].world.xyz.x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
1938  v[0].world.xyz.y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
1939  v[0].world.xyz.z = pnt->world.xyz.z;
1940  v[0].screen.xyw.w = 0.0f;
1941  v[0].texture_position.u = 0.0f;
1942  v[0].texture_position.v = 0.0f;
1943 
1944  v[1].world.xyz.x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
1945  v[1].world.xyz.y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
1946  v[1].world.xyz.z = pnt->world.xyz.z;
1947  v[1].screen.xyw.w = 0.0f;
1948  v[1].texture_position.u = 1.0f;
1949  v[1].texture_position.v = 0.0f;
1950 
1951  v[2].world.xyz.x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
1952  v[2].world.xyz.y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
1953  v[2].world.xyz.z = pnt->world.xyz.z;
1954  v[2].screen.xyw.w = 0.0f;
1955  v[2].texture_position.u = 1.0f;
1956  v[2].texture_position.v = 1.0f;
1957 
1958  v[3].world.xyz.x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
1959  v[3].world.xyz.y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
1960  v[3].world.xyz.z = pnt->world.xyz.z;
1961  v[3].screen.xyw.w = 0.0f;
1962  v[3].texture_position.u = 0.0f;
1963  v[3].texture_position.v = 1.0f;
1964 
1965  ubyte codes_and=0xff;
1966 
1967  float sw,z;
1968  z = pnt->world.xyz.z - rad / 4.0f;
1969  if ( z < 0.0f ) z = 0.0f;
1970  sw = 1.0f / z;
1971 
1972  for (i=0; i<4; i++ ) {
1973  //now code the four points
1974  codes_and &= g3_code_vertex(&v[i]);
1975  v[i].flags = 0; // mark as not yet projected
1976  g3_project_vertex(&v[i]);
1977  v[i].screen.xyw.w = sw;
1978  }
1979 
1980  if (codes_and)
1981  return 1; //1 means off screen
1982 
1983  return 0;
1984 }
1985 
1986 float Interp_depth_scale = 1500.0f;
1987 
1988 DCF(model_darkening,"Makes models darker with distance")
1989 {
1990  if (dc_optional_string_either("help", "--help")) {
1991  dc_printf( "Usage: model_darkening <float>\n" );
1992  dc_printf("Sets the distance at which to start blacking out models (namely asteroids).\n");
1993  return;
1994  }
1995 
1996  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1997  dc_printf( "model_darkening = %.1f\n", Interp_depth_scale );
1998  return;
1999  }
2000 
2002 
2003  dc_printf("model_darkening set to %.1f\n", Interp_depth_scale);
2004 }
2005 
2006 void model_render_DEPRECATED(int model_num, matrix *orient, vec3d * pos, uint flags, int objnum, int lighting_skip, int *replacement_textures, int render, const bool is_skybox)
2007 {
2008  int cull = 0;
2009  // replacement textures - Goober5000
2010  model_set_replacement_textures(replacement_textures);
2011 
2012  polymodel *pm = model_get(model_num);
2013 
2014  if (flags & MR_FORCE_CLAMP)
2016 
2017  int time = timestamp();
2018 
2019  glow_point_bank_override *gpo = NULL;
2020  bool override_all = false;
2022  ship_info *sip = NULL;
2023  ship *shipp = NULL;
2024 
2025  if(!Glowpoint_override) {
2026  if(objnum>-1)
2027  {
2028  shipp = &Ships[Objects[objnum].instance];
2029  sip = &Ship_info[shipp->ship_info_index];
2030  gpoi = sip->glowpoint_bank_override_map.find(-1);
2031 
2032  if(gpoi != sip->glowpoint_bank_override_map.end()) {
2033  override_all = true;
2035  }
2036  }
2037 
2038  for (int i = 0; i < pm->n_glow_point_banks; i++ ) { //glow point blink code -Bobboau
2039  glow_point_bank *bank = &pm->glow_point_banks[i];
2040 
2041  if(!override_all && sip) {
2042  gpoi = sip->glowpoint_bank_override_map.find(i);
2043  if(gpoi != sip->glowpoint_bank_override_map.end()) {
2045  } else {
2046  gpo = NULL;
2047  }
2048  }
2049 
2050  if (bank->glow_timestamp == 0)
2051  bank->glow_timestamp=time;
2052  if(((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time)){
2053  if(bank->is_on){
2054  if( ((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) > ((time - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % (((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time))) ){
2055  bank->glow_timestamp=time;
2056  bank->is_on=false;
2057  }
2058  }else{
2059  if( ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) < ((time - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % (((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time))) ){
2060  bank->glow_timestamp=time;
2061  bank->is_on=true;
2062  }
2063  }
2064  }
2065  }
2066  }
2067 
2068  // maybe turn off (hardware) culling
2069  if(flags & MR_NO_CULL){
2070  cull = gr_set_cull(0);
2071  }
2072 
2073  // Goober5000
2074  Interp_base_frametime = 0;
2075 
2076  if (objnum >= 0) {
2077  object *objp = &Objects[objnum];
2078 
2079  if (objp->type == OBJ_SHIP) {
2080  Interp_base_frametime = Ships[objp->instance].base_texture_anim_frametime;
2081  }
2082  } else if (is_skybox) {
2083  Interp_base_frametime = Skybox_timestamp;
2084  }
2085 
2086 
2087  if (flags & MR_ATTACHED_MODEL)
2088  Interp_objnum = -1;
2089  else
2090  Interp_objnum = objnum;
2091 
2092  if ( flags & MR_NO_LIGHTING ) {
2093  Interp_light = 1.0f;
2094  } else if ( flags & MR_IS_ASTEROID ) {
2095  // Dim it based on distance
2096  float depth = vm_vec_dist_quick( pos, &Eye_position );
2097  if ( depth > Interp_depth_scale ) {
2099  // If it is too far, exit
2100  if ( Interp_light < (1.0f/32.0f) ) {
2101  Interp_light = 0.0f;
2102  return;
2103  } else if ( Interp_light > 1.0f ) {
2104  Interp_light = 1.0f;
2105  }
2106  } else {
2107  Interp_light = 1.0f;
2108  }
2109  } else {
2110  Interp_light = 1.0f;
2111  }
2112 
2114 
2115  extern bool Deferred_lighting;
2116 
2117  if ( !(flags & MR_NO_LIGHTING ) && !Deferred_lighting ) {
2118  light_filter_push( objnum, pos, pm->rad );
2119  }
2120 
2121  model_really_render(model_num, orient, pos, flags, render, objnum);
2122 
2123  if ( !(flags & MR_NO_LIGHTING ) && !Deferred_lighting ) {
2124  light_filter_pop();
2125  }
2126 
2127  // maybe turn culling back on
2128  if(flags & MR_NO_CULL){
2129  gr_set_cull(cull);
2130  }
2131 
2132  // turn off fog after each model renders
2134  gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
2135  }
2136 
2137  if (flags & MR_FORCE_CLAMP)
2139 }
2140 
2141 // tmp_detail_level
2142 // 0 - Max
2143 // 1
2144 // 2
2145 // 3
2146 // 4 - None
2147 
2148 #if MAX_DETAIL_LEVEL != 4
2149 #error MAX_DETAIL_LEVEL is assumed to be 4 in ModelInterp.cpp
2150 #endif
2151 
2152 
2157 float interp_closest_dist_to_box( vec3d *hitpt, vec3d *p0, vec3d *min, vec3d *max )
2158 {
2159  float *origin = (float *)&p0->xyz.x;
2160  float *minB = (float *)min;
2161  float *maxB = (float *)max;
2162  float *coord = (float *)&hitpt->xyz.x;
2163  int inside = 1;
2164  int i;
2165 
2166  for (i=0; i<3; i++ ) {
2167  if ( origin[i] < minB[i] ) {
2168  coord[i] = minB[i];
2169  inside = 0;
2170  } else if (origin[i] > maxB[i] ) {
2171  coord[i] = maxB[i];
2172  inside = 0;
2173  } else {
2174  coord[i] = origin[i];
2175  }
2176  }
2177 
2178  if ( inside ) {
2179  return 0.0f;
2180  }
2181 
2182  return vm_vec_dist(hitpt,p0);
2183 }
2184 
2185 
2186 // Finds the closest point on a model to a point in space. Actually only finds a point
2187 // on the bounding box of the model.
2188 // Given:
2189 // model_num Which model
2190 // orient Orientation of the model
2191 // pos Position of the model
2192 // eye_pos Point that you want to find the closest point to
2193 // Returns:
2194 // distance from eye_pos to closest_point. 0 means eye_pos is
2195 // on or inside the bounding box.
2196 // Also fills in outpnt with the actual closest point.
2198 {
2199  vec3d closest_pos, tempv, eye_rel_pos;
2200 
2201  polymodel *pm = model_get(model_num);
2202 
2203  if ( submodel_num < 0 ) {
2204  submodel_num = pm->detail[0];
2205  }
2206 
2207  // Rotate eye pos into object coordinates
2208  vm_vec_sub(&tempv,pos,eye_pos );
2209  vm_vec_rotate(&eye_rel_pos,&tempv,orient );
2210 
2211  return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max );
2212 }
2213 
2214 int tiling = 1;
2215 DCF(tiling, "Toggles rendering of tiled textures (default is on)")
2216 {
2217  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
2218  dc_printf("Tiled textures are %s", tiling ? "ON" : "OFF");
2219  return;
2220  }
2221 
2222  tiling = !tiling;
2223  if(tiling){
2224  dc_printf("Tiled textures\n");
2225  } else {
2226  dc_printf("Non-tiled textures\n");
2227  }
2228 }
2229 
2230 void moldel_calc_facing_pts( vec3d *top, vec3d *bot, vec3d *fvec, vec3d *pos, float w, float z_add, vec3d *Eyeposition )
2231 {
2232  vec3d uvec, rvec;
2233  vec3d temp;
2234 
2235  temp = *pos;
2236 
2237  vm_vec_sub( &rvec, Eyeposition, &temp );
2238  vm_vec_normalize( &rvec );
2239 
2240  vm_vec_cross(&uvec,fvec,&rvec);
2241  vm_vec_normalize(&uvec);
2242 
2243  vm_vec_scale_add( top, &temp, &uvec, w/2.0f );
2244  vm_vec_scale_add( bot, &temp, &uvec, -w/2.0f );
2245 }
2246 
2247 extern bool Scene_framebuffer_in_frame;
2248 
2253 {
2254  int i, j;
2255  int n_q = 0;
2256  size_t k;
2257  vec3d norm, norm2, fvec, pnt, npnt;
2258  thruster_bank *bank = NULL;
2259  vertex p;
2260  bool do_render = false;
2261 
2262  if ( pm == NULL ) {
2263  Int3();
2264  return;
2265  }
2266 
2267  if ( !(Interp_flags & MR_SHOW_THRUSTERS) ) {
2268  return;
2269  }
2270 
2271  // get an initial count to figure out how man geo batchers we need allocated
2272  for (i = 0; i < pm->n_thrusters; i++ ) {
2273  bank = &pm->thrusters[i];
2274  n_q += bank->num_points;
2275  }
2276 
2277  if (n_q <= 0) {
2278  return;
2279  }
2280 
2281  // primary_thruster_batcher
2282  if (Interp_thrust_glow_bitmap >= 0) {
2283  do_render = true;
2284  }
2285 
2286  // secondary_thruster_batcher
2287  if (Interp_secondary_thrust_glow_bitmap >= 0) {
2288  do_render = true;
2289  }
2290 
2291  // tertiary_thruster_batcher
2292  if (Interp_tertiary_thrust_glow_bitmap >= 0) {
2293  do_render = true;
2294  }
2295 
2296  if (do_render == false) {
2297  return;
2298  }
2299 
2300  // this is used for the secondary thruster glows
2301  // it only needs to be calculated once so I'm doing it here -Bobboau
2302  norm.xyz.z = -1.0f;
2303  norm.xyz.x = 1.0f;
2304  norm.xyz.y = -1.0f;
2305  norm.xyz.x *= Interp_thrust_rotvel.xyz.y/2;
2306  norm.xyz.y *= Interp_thrust_rotvel.xyz.x/2;
2307  vm_vec_normalize(&norm);
2308 
2309  // we need to disable fogging
2311  gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
2312 
2313  for (i = 0; i < pm->n_thrusters; i++ ) {
2314  vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
2315  bool submodel_rotation = false;
2316 
2317  bank = &pm->thrusters[i];
2318 
2319  // don't draw this thruster if the engine is destroyed or just not on
2320  if ( !model_should_render_engine_glow(objnum, bank->obj_num) )
2321  continue;
2322 
2323  // If bank is attached to a submodel, prepare to account for rotations
2324  //
2325  // TODO: This won't work in the ship lab, because the lab code doesn't
2326  // set the the necessary submodel instance info needed here. The second
2327  // condition is thus a hack to disable the feature while in the lab, and
2328  // can be removed if the lab is re-structured accordingly. -zookeeper
2329  if ( shipp && bank->submodel_num > -1 && pm->submodel[bank->submodel_num].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
2330  model_find_submodel_offset(&submodel_static_offset, Ship_info[shipp->ship_info_index].model_num, bank->submodel_num);
2331 
2332  submodel_rotation = true;
2333  }
2334 
2335  for (j = 0; j < bank->num_points; j++) {
2336  Assert( bank->points != NULL );
2337 
2338  float d, D;
2339  vec3d tempv;
2340  glow_point *gpt = &bank->points[j];
2341  vec3d loc_offset = gpt->pnt;
2342  vec3d loc_norm = gpt->norm;
2343  vec3d world_pnt;
2344  vec3d world_norm;
2345 
2346  if ( submodel_rotation ) {
2347  vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
2348 
2349  tempv = loc_offset;
2350  find_submodel_instance_point_normal(&loc_offset, &loc_norm, shipp->model_instance_num, bank->submodel_num, &tempv, &loc_norm);
2351  }
2352 
2353  vm_vec_unrotate(&world_pnt, &loc_offset, orient);
2354  vm_vec_add2(&world_pnt, pos);
2355 
2356  if (shipp) {
2357  // if ship is warping out, check position of the engine glow to the warp plane
2358  if ( (shipp->flags & (SF_ARRIVING) ) && (shipp->warpin_effect) && Ship_info[shipp->ship_info_index].warpin_type != WT_HYPERSPACE) {
2359  vec3d warp_pnt, tmp;
2360  matrix warp_orient;
2361 
2362  shipp->warpin_effect->getWarpPosition(&warp_pnt);
2363  shipp->warpin_effect->getWarpOrientation(&warp_orient);
2364  vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
2365 
2366  if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) < 0.0f ) {
2367  break;
2368  }
2369  }
2370 
2371  if ( (shipp->flags & (SF_DEPART_WARP) ) && (shipp->warpout_effect) && Ship_info[shipp->ship_info_index].warpout_type != WT_HYPERSPACE) {
2372  vec3d warp_pnt, tmp;
2373  matrix warp_orient;
2374 
2375  shipp->warpout_effect->getWarpPosition(&warp_pnt);
2376  shipp->warpout_effect->getWarpOrientation(&warp_orient);
2377  vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
2378 
2379  if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) > 0.0f ) {
2380  break;
2381  }
2382  }
2383  }
2384 
2385  vm_vec_sub(&tempv, &View_position, &world_pnt);
2386  vm_vec_normalize(&tempv);
2387  vm_vec_unrotate(&world_norm, &loc_norm, orient);
2388  D = d = vm_vec_dot(&tempv, &world_norm);
2389 
2390  // ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
2391  #define NOISE_SCALE 0.5f
2392  #define MIN_SCALE 3.4f
2393  #define MAX_SCALE 4.7f
2394 
2395  float magnitude;
2396  vec3d scale_vec = { { { 1.0f, 0.0f, 0.0f } } };
2397 
2398  // normalize banks, in case of incredibly big normals
2399  if ( !IS_VEC_NULL_SQ_SAFE(&world_norm) )
2400  vm_vec_copy_normalize(&scale_vec, &world_norm);
2401 
2402  // adjust for thrust
2403  (scale_vec.xyz.x *= Interp_thrust_scale_x) -= 0.1f;
2404  (scale_vec.xyz.y *= Interp_thrust_scale_y) -= 0.1f;
2405  (scale_vec.xyz.z *= Interp_thrust_scale) -= 0.1f;
2406 
2407  // get magnitude, which we will use as the scaling reference
2408  magnitude = vm_vec_normalize(&scale_vec);
2409 
2410  // get absolute value
2411  if (magnitude < 0.0f)
2412  magnitude *= -1.0f;
2413 
2414  float scale = magnitude * (MAX_SCALE - MIN_SCALE) + MIN_SCALE;
2415 
2416  if (d > 0.0f){
2417  // Make glow bitmap fade in/out quicker from sides.
2418  d *= 3.0f;
2419 
2420  if (d > 1.0f)
2421  d = 1.0f;
2422  }
2423 
2424  float fog_int = 1.0f;
2425 
2426  // fade them in the nebula as well
2428  vm_vec_unrotate(&npnt, &gpt->pnt, orient);
2429  vm_vec_add2(&npnt, pos);
2430 
2431  fog_int = (1.0f - (neb2_get_fog_intensity(&npnt)));
2432 
2433  if (fog_int > 1.0f)
2434  fog_int = 1.0f;
2435 
2436  d *= fog_int;
2437 
2438  if (d > 1.0f)
2439  d = 1.0f;
2440  }
2441 
2442  float w = gpt->radius * (scale + Interp_thrust_glow_noise * NOISE_SCALE);
2443 
2444  // these lines are used by the tertiary glows, thus we will need to project this all of the time
2445  if (Cmdline_nohtl) {
2446  g3_rotate_vertex( &p, &world_pnt );
2447  } else {
2448  g3_transfer_vertex( &p, &world_pnt );
2449  }
2450 
2451  // start primary thruster glows
2452  if ( (Interp_thrust_glow_bitmap >= 0) && (d > 0.0f) ) {
2453  p.r = p.g = p.b = p.a = (ubyte)(255.0f * d);
2455  Interp_thrust_glow_bitmap,
2457  &p,
2458  0,
2459  (w * 0.5f * Interp_thrust_glow_rad_factor),
2460  1.0f,
2461  (w * 0.325f)
2462  );
2463  }
2464 
2465  // start tertiary thruster glows
2466  if (Interp_tertiary_thrust_glow_bitmap >= 0) {
2467  p.screen.xyw.w -= w;
2468  p.r = p.g = p.b = p.a = (ubyte)(255.0f * fog_int);
2470  Interp_tertiary_thrust_glow_bitmap,
2472  &p,
2473  (magnitude * 4),
2474  (w * 0.6f * Interp_tertiary_thrust_glow_rad_factor),
2475  1.0f,
2476  (-(D > 0) ? D : -D)
2477  );
2478  }
2479 
2480  // begin secondary glows
2481  if (Interp_secondary_thrust_glow_bitmap >= 0) {
2482  pnt = world_pnt;
2483  scale = magnitude * (MAX_SCALE - (MIN_SCALE / 2)) + (MIN_SCALE / 2);
2484  vm_vec_unrotate(&world_norm, &norm, orient);
2485  d = vm_vec_dot(&tempv, &world_norm);
2486  d += 0.75f;
2487  d *= 3.0f;
2488 
2489  if (d > 1.0f)
2490  d = 1.0f;
2491 
2492  if (d > 0.0f) {
2493  vm_vec_add(&norm2, &world_norm, &pnt);
2494  vm_vec_sub(&fvec, &norm2, &pnt);
2495  vm_vec_normalize(&fvec);
2496 
2497  float wVal = gpt->radius * scale * 2;
2498 
2499  vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * Interp_thrust_glow_len_factor);
2500 
2502  vm_vec_add(&npnt, &pnt, pos);
2503  d *= fog_int;
2504  }
2505 
2506  batch_add_beam(Interp_secondary_thrust_glow_bitmap,
2508  &pnt, &norm2, wVal*Interp_secondary_thrust_glow_rad_factor*0.5f, d
2509  );
2510  if (Scene_framebuffer_in_frame && Interp_draw_distortion) {
2511  vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * Interp_distortion_thrust_length_factor);
2512  int dist_bitmap;
2513  if (Interp_distortion_thrust_bitmap > 0) {
2514  dist_bitmap = Interp_distortion_thrust_bitmap;
2515  }
2516  else {
2517  dist_bitmap = Interp_secondary_thrust_glow_bitmap;
2518  }
2519  float mag = vm_vec_mag(&gpt->pnt);
2520  mag -= (float)((int)mag);//Valathil - Get a fairly random but constant number to offset the distortion texture
2521  distortion_add_beam(dist_bitmap,
2523  &pnt, &norm2, wVal*Interp_distortion_thrust_rad_factor*0.5f, 1.0f, mag
2524  );
2525  }
2526  }
2527  }
2528 
2529  // begin particles
2530  if (shipp) {
2531  ship_info *sip = &Ship_info[shipp->ship_info_index];
2532  particle_emitter pe;
2533  thruster_particles *tp;
2534  size_t num_particles = 0;
2535 
2536  if (Interp_afterburner)
2537  num_particles = sip->afterburner_thruster_particles.size();
2538  else
2539  num_particles = sip->normal_thruster_particles.size();
2540 
2541  for (k = 0; k < num_particles; k++) {
2542  if (Interp_afterburner)
2543  tp = &sip->afterburner_thruster_particles[k];
2544  else
2545  tp = &sip->normal_thruster_particles[k];
2546 
2548 
2549  vm_vec_unrotate(&npnt, &gpt->pnt, orient);
2550  vm_vec_add2(&npnt, pos);
2551 
2552  // Where the particles emit from
2553  pe.pos = npnt;
2554  // Initial velocity of all the particles
2555  pe.vel = Objects[shipp->objnum].phys_info.desired_vel;
2556  pe.min_vel = v * 0.75f;
2557  pe.max_vel = v * 1.25f;
2558  // What normal the particle emit around
2559  pe.normal = orient->vec.fvec;
2560  vm_vec_negate(&pe.normal);
2561 
2562  // Lowest number of particles to create
2563  pe.num_low = tp->n_low;
2564  // Highest number of particles to create
2565  pe.num_high = tp->n_high;
2566  pe.min_rad = gpt->radius * tp->min_rad;
2567  pe.max_rad = gpt->radius * tp->max_rad;
2568  // How close they stick to that normal 0=on normal, 1=180, 2=360 degree
2569  pe.normal_variance = tp->variance;
2570  pe.min_life = 0.0f;
2571  pe.max_life = 1.0f;
2572 
2574  }
2575  }
2576  }
2577  }
2578 
2579  // save current zbuffer, and set the correct mode for us
2580  int zbuff_save = gr_zbuffering_mode;
2582  gr_zbuffer_set(zbuff_save);
2583 }
2584 
2586 {
2587  int i, j;
2588 
2589  int cull = gr_set_cull(0);
2590 
2591  glow_point_bank_override *gpo = NULL;
2592  bool override_all = false;
2594  ship_info *sip = NULL;
2595 
2596  if(shipp)
2597  {
2598  sip = &Ship_info[shipp->ship_info_index];
2599  gpoi = sip->glowpoint_bank_override_map.find(-1);
2600 
2601  if(gpoi != sip->glowpoint_bank_override_map.end()) {
2602  override_all = true;
2604  }
2605  }
2606 
2607  for (i = 0; i < pm->n_glow_point_banks; i++ ) {
2608  glow_point_bank *bank = &pm->glow_point_banks[i];
2609 
2610  if(!override_all && sip) {
2611  gpoi = sip->glowpoint_bank_override_map.find(i);
2612  if(gpoi != sip->glowpoint_bank_override_map.end()) {
2614  } else {
2615  gpo = NULL;
2616  }
2617  }
2618 
2619  //Only continue if there actually is a glowpoint bitmap available
2620  if (bank->glow_bitmap == -1)
2621  continue;
2622 
2623  if (pm->submodel[bank->submodel_parent].blown_off)
2624  continue;
2625 
2626  if ((gpo && gpo->off_time_override && !gpo->off_time)?gpo->is_on:bank->is_on) {
2627  if ( (shipp != NULL) && !(shipp->glow_point_bank_active[i]) )
2628  continue;
2629 
2630  for (j = 0; j < bank->num_points; j++) {
2631  Assert( bank->points != NULL );
2632  int flick;
2633 
2634  if (pm->submodel[pm->detail[0]].num_arcs) {
2635  flick = static_rand( timestamp() % 20 ) % (pm->submodel[pm->detail[0]].num_arcs + j); //the more damage, the more arcs, the more likely the lights will fail
2636  } else {
2637  flick = 1;
2638  }
2639 
2640  if (flick == 1) {
2641  glow_point *gpt = &bank->points[j];
2642  vec3d loc_offset = gpt->pnt;
2643  vec3d loc_norm = gpt->norm;
2644  vec3d world_pnt;
2645  vec3d world_norm;
2646  vec3d tempv;
2647  vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
2648  bool submodel_rotation = false;
2649 
2650  if ( bank->submodel_parent > 0 && pm->submodel[bank->submodel_parent].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) && shipp != NULL ) {
2651  model_find_submodel_offset(&submodel_static_offset, Ship_info[shipp->ship_info_index].model_num, bank->submodel_parent);
2652 
2653  submodel_rotation = true;
2654  }
2655 
2656  if ( submodel_rotation ) {
2657  vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
2658 
2659  tempv = loc_offset;
2660  find_submodel_instance_point_normal(&loc_offset, &loc_norm, shipp->model_instance_num, bank->submodel_parent, &tempv, &loc_norm);
2661  }
2662 
2663  vm_vec_unrotate(&world_pnt, &loc_offset, orient);
2664  vm_vec_add2(&world_pnt, pos);
2665 
2666  vm_vec_unrotate(&world_norm, &loc_norm, orient);
2667 
2668  if ( shipp != NULL ) {
2669  if ( (shipp->flags & (SF_ARRIVING) ) && (shipp->warpin_effect) && Ship_info[shipp->ship_info_index].warpin_type != WT_HYPERSPACE) {
2670  vec3d warp_pnt, tmp;
2671  matrix warp_orient;
2672 
2673  shipp->warpin_effect->getWarpPosition(&warp_pnt);
2674  shipp->warpin_effect->getWarpOrientation(&warp_orient);
2675  vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
2676 
2677  if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) < 0.0f ) {
2678  continue;
2679  }
2680  }
2681 
2682  if ( (shipp->flags & (SF_DEPART_WARP) ) && (shipp->warpout_effect) && Ship_info[shipp->ship_info_index].warpout_type != WT_HYPERSPACE) {
2683  vec3d warp_pnt, tmp;
2684  matrix warp_orient;
2685 
2686  shipp->warpout_effect->getWarpPosition(&warp_pnt);
2687  shipp->warpout_effect->getWarpOrientation(&warp_orient);
2688  vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
2689 
2690  if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) > 0.0f ) {
2691  continue;
2692  }
2693  }
2694  }
2695 
2696  switch ((gpo && gpo->type_override)?gpo->type:bank->type)
2697  {
2698  case 0:
2699  {
2700  float d,pulse = 1.0f;
2701 
2702  if ( IS_VEC_NULL(&world_norm) ) {
2703  d = 1.0f; //if given a nul vector then always show it
2704  } else {
2705  vm_vec_sub(&tempv,&View_position,&world_pnt);
2706  vm_vec_normalize(&tempv);
2707 
2708  d = vm_vec_dot(&tempv,&world_norm);
2709  d -= 0.25;
2710  }
2711 
2712  float w = gpt->radius;
2713  if (d > 0.0f) {
2714  vertex p;
2715 
2716  d *= 3.0f;
2717 
2718  if (d > 1.0f)
2719  d = 1.0f;
2720 
2721 
2722  // fade them in the nebula as well
2724  //vec3d npnt;
2725  //vm_vec_add(&npnt, &loc_offset, pos);
2726 
2727  d *= (1.0f - neb2_get_fog_intensity(&world_pnt));
2728  w *= 1.5; //make it bigger in a nebula
2729  }
2730 
2731  // disable fogging
2732  if (Interp_tmap_flags & TMAP_FLAG_PIXEL_FOG)
2733  gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
2734 
2735  if (!Cmdline_nohtl) {
2736  g3_transfer_vertex(&p, &world_pnt);
2737  } else {
2738  g3_rotate_vertex(&p, &world_pnt);
2739  }
2740 
2741  p.r = p.g = p.b = p.a = (ubyte)(255.0f * MAX(d,0.0f));
2742 
2743  if((gpo && gpo->glow_bitmap_override)?(gpo->glow_bitmap > -1):(bank->glow_bitmap > -1)) {
2745  if (use_depth_buffer)
2746  gpflags |= TMAP_FLAG_SOFT_QUAD;
2747 
2749  (gpo && gpo->glow_bitmap_override)?gpo->glow_bitmap:bank->glow_bitmap,
2750  gpflags,
2751  &p,
2752  0,
2753  (w * 0.5f),
2754  d * pulse,
2755  w
2756  );
2757  }
2758  } //d>0.0f
2759  if(gpo && gpo->pulse_type) {
2760  int period;
2761  if(gpo->pulse_period_override) {
2762  period = gpo->pulse_period;
2763  } else {
2764  if(gpo->on_time_override) {
2765  period = 2 * gpo->on_time;
2766  } else {
2767  period = 2 * bank->on_time;
2768  }
2769  }
2770  int x = 0;
2771  if((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) {
2772  x = (timestamp() - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % ( ((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) ) - ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time);
2773  } else {
2774  x = (timestamp() - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % gpo->pulse_period;
2775  }
2776  switch(gpo->pulse_type) {
2777  case PULSE_SIN:
2778  pulse = gpo->pulse_bias + gpo->pulse_amplitude * pow(sin( PI2 / period * x),gpo->pulse_exponent);
2779  break;
2780  case PULSE_COS:
2781  pulse = gpo->pulse_bias + gpo->pulse_amplitude * pow(cos( PI2 / period * x),gpo->pulse_exponent);
2782  break;
2783  case PULSE_SHIFTTRI:
2784  x += period / 4;
2785  if((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) {
2786  x %= ( ((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) );
2787  } else {
2788  x %= gpo->pulse_period;
2789  }
2790  case PULSE_TRI:
2791  float inv;
2792  if( x > period / 2) {
2793  inv = -1;
2794  } else {
2795  inv = 1;
2796  }
2797  if( x > period / 4) {
2798  pulse = gpo->pulse_bias + gpo->pulse_amplitude * inv * pow( 1.0f - ((x - period / 4.0f) * 4 / period) ,gpo->pulse_exponent);
2799  } else {
2800  pulse = gpo->pulse_bias + gpo->pulse_amplitude * inv * pow( (x * 4.0f / period) ,gpo->pulse_exponent);
2801  }
2802  break;
2803  }
2804  }
2805  extern bool Deferred_lighting;
2806  if(Deferred_lighting && gpo && gpo->is_lightsource) {
2807  if(gpo->lightcone) {
2808  vec3d cone_dir_rot;
2809  vec3d cone_dir_model;
2810  vec3d cone_dir_world;
2811  vec3d cone_dir_screen;
2812  vec3d unused;
2813  if(gpo->rotating) {
2814  vm_rot_point_around_line(&cone_dir_rot, &gpo->cone_direction, PI * timestamp() * 0.000033333f * gpo->rotation_speed, &vmd_zero_vector, &gpo->rotation_axis);
2815  } else {
2816  cone_dir_rot = gpo->cone_direction;
2817  }
2818  find_submodel_instance_point_normal(&unused, &cone_dir_model, shipp->model_instance_num, bank->submodel_parent, &unused, &cone_dir_rot);
2819  vm_vec_unrotate(&cone_dir_world, &cone_dir_model, orient);
2820  vm_vec_rotate(&cone_dir_screen, &cone_dir_world, &Eye_matrix);
2821  cone_dir_screen.xyz.z = -cone_dir_screen.xyz.z;
2822  light_add_cone(&world_pnt, &cone_dir_screen, gpo->cone_angle, gpo->cone_inner_angle, gpo->dualcone, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
2823  } else {
2824  light_add_point(&world_pnt, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
2825  }
2826  }
2827  break;
2828  }
2829 
2830  case 1:
2831  {
2832  vertex verts[4];
2833  vec3d fvec, top1, bottom1, top2, bottom2, start, end;
2834 
2835  memset(verts, 0, sizeof(verts));
2836 
2837  vm_vec_add2(&loc_norm, &loc_offset);
2838 
2839  vm_vec_rotate(&start, &loc_offset, orient);
2840  vm_vec_rotate(&end, &loc_norm, orient);
2841  vm_vec_sub(&fvec, &end, &start);
2842 
2843  vm_vec_normalize(&fvec);
2844 
2845  moldel_calc_facing_pts(&top1, &bottom1, &fvec, &loc_offset, gpt->radius, 1.0f, &View_position);
2846  moldel_calc_facing_pts(&top2, &bottom2, &fvec, &loc_norm, gpt->radius, 1.0f, &View_position);
2847 
2848  int idx = 0;
2849 
2850  if (Cmdline_nohtl) {
2851  g3_rotate_vertex(&verts[0], &bottom1);
2852  g3_rotate_vertex(&verts[1], &bottom2);
2853  g3_rotate_vertex(&verts[2], &top2);
2854  g3_rotate_vertex(&verts[3], &top1);
2855 
2856  for (idx = 0; idx < 4; idx++) {
2857  g3_project_vertex(&verts[idx]);
2858  }
2859  } else {
2860  g3_transfer_vertex(&verts[0], &bottom1);
2861  g3_transfer_vertex(&verts[1], &bottom2);
2862  g3_transfer_vertex(&verts[2], &top2);
2863  g3_transfer_vertex(&verts[3], &top1);
2864  }
2865 
2866  verts[0].texture_position.u = 0.0f;
2867  verts[0].texture_position.v = 0.0f;
2868 
2869  verts[1].texture_position.u = 1.0f;
2870  verts[1].texture_position.v = 0.0f;
2871 
2872  verts[2].texture_position.u = 1.0f;
2873  verts[2].texture_position.v = 1.0f;
2874 
2875  verts[3].texture_position.u = 0.0f;
2876  verts[3].texture_position.v = 1.0f;
2877 
2878  vm_vec_sub(&tempv,&View_position,&loc_offset);
2879  vm_vec_normalize(&tempv);
2880 
2883  } else {
2885  }
2889  break;
2890  }
2891  } // switch(bank->type)
2892  } // flick
2893  } // for slot
2894  } // bank is on
2895  } // for bank
2896 
2897  gr_set_cull(cull);
2898 }
2899 
2900 void light_set_all_relevent();
2901 extern int Warp_model;
2902 extern bool Rendering_to_shadow_map;
2903 
2904 void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, int render, int objnum )
2905 {
2906  int i;
2907  int cull = 1;
2908  polymodel * pm;
2909 
2910  uint save_gr_zbuffering_mode;
2911  int zbuf_mode;
2912  ship *shipp = NULL;
2913  object *objp = NULL;
2914  bool set_autocen = false;
2915  bool draw_thrusters = false;
2916 
2917  // just to be on the safe side
2918  // If we're dealing with an attached model (i.e. an external weapon model) we need the object number
2919  // of the ship that the weapon is attached to, but nothing else
2920  Assert( (Interp_objnum == objnum) || (flags & MR_DEPRECATED_ATTACHED_MODEL) );
2921 
2922  if (objnum >= 0 && !(flags & MR_DEPRECATED_ATTACHED_MODEL)) {
2923  objp = &Objects[objnum];
2924 
2925  if (objp->type == OBJ_SHIP) {
2926  shipp = &Ships[objp->instance];
2927 
2928  if (shipp->flags2 & SF2_GLOWMAPS_DISABLED)
2929  flags |= MR_DEPRECATED_NO_GLOWMAPS;
2930  }
2931  }
2932 
2933  if (FULLCLOAK != 1)
2935 
2936 
2937  MONITOR_INC( NumModelsRend, 1 );
2938 
2939  Interp_orient = orient;
2940  Interp_pos = pos;
2941 
2942  int tmp_detail_level = Game_detail_level;
2943 
2944  // Turn off engine effect
2946 
2947  if (!Model_texturing)
2948  flags |= MR_DEPRECATED_NO_TEXTURING;
2949 
2950  if ( !Model_polys ) {
2951  flags |= MR_DEPRECATED_NO_POLYS;
2952  }
2953 
2954  Interp_flags = flags;
2955 
2956  pm = model_get(model_num);
2957 
2958  // Set the flags we will pass to the tmapper
2959  Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
2960 
2961  // if we're in nebula mode, fog everything except for the warp holes and other non-fogged models
2963  Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
2964  }
2965 
2966  if ( !(Interp_flags & MR_NO_TEXTURING) ) {
2967  Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
2968 
2969  if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
2970  Interp_tmap_flags |= TMAP_FLAG_TILED;
2971 
2972  if ( !(Interp_flags & MR_DEPRECATED_NO_CORRECT) ) {
2973  Interp_tmap_flags |= TMAP_FLAG_CORRECT;
2974  }
2975  }
2976 
2977  if ( Interp_flags & MR_DEPRECATED_ANIMATED_SHADER )
2978  Interp_tmap_flags |= TMAP_ANIMATED_SHADER;
2979 
2980  if ( Interp_desaturate ) {
2981  Interp_tmap_flags |= TMAP_FLAG_DESATURATE;
2982  }
2983 
2984  save_gr_zbuffering_mode = gr_zbuffering_mode;
2985  zbuf_mode = gr_zbuffering_mode;
2986 
2988  gr_set_color(0,128,0);
2989  g3_draw_sphere_ez( pos, pm->rad );
2990  return;
2991  }
2992 
2993  bool is_outlines_only = (flags & MR_DEPRECATED_NO_POLYS) && ((flags & MR_DEPRECATED_SHOW_OUTLINE_PRESET) || (flags & MR_DEPRECATED_SHOW_OUTLINE));
2994  bool is_outlines_only_htl = !Cmdline_nohtl && (flags & MR_DEPRECATED_NO_POLYS) && (flags & MR_DEPRECATED_SHOW_OUTLINE_HTL);
2995  bool use_api = !is_outlines_only_htl || (gr_screen.mode == GR_OPENGL);
2996 
2997  g3_start_instance_matrix(pos, orient, use_api);
2998 
2999  if ( Interp_flags & MR_DEPRECATED_SHOW_RADIUS ) {
3000  if ( !(Interp_flags & MR_DEPRECATED_SHOW_OUTLINE_PRESET) ) {
3001  gr_set_color(0,64,0);
3003  }
3004  }
3005 
3007 
3008  vec3d closest_pos;
3009  float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
3010 
3011 
3012  if ( !(Interp_flags & MR_DEPRECATED_LOCK_DETAIL) ) {
3013  #if MAX_DETAIL_LEVEL != 4
3014  #error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4
3015  #endif
3016 
3017  switch( Detail.detail_distance ) {
3018  case 0: // lowest
3020  break;
3021  case 1: // lower than normal
3023  break;
3024  case 2: // default
3026  break;
3027  case 3: // above normal
3029  break;
3030  case 4: // even more normal
3032  break;
3033  }
3034 
3035  // If we're rendering attached weapon models, check against the ships' tabled Weapon Model Draw Distance (which defaults to 200)
3036  if (Interp_flags & MR_DEPRECATED_ATTACHED_MODEL) {
3037  if (depth > Ship_info[Ships[Objects[objnum].instance].ship_info_index].weapon_model_draw_distance) {
3038  g3_done_instance(use_api);
3039  return;
3040  }
3041  }
3042  }
3043  if ( pm->n_detail_levels > 1 ) {
3044 
3045  if ( Interp_flags & MR_DEPRECATED_LOCK_DETAIL ) {
3046  i = Interp_detail_level_locked+1;
3047  } else {
3048  // nebula ?
3050  depth *= neb2_get_lod_scale(Interp_objnum);
3051  }
3052 
3053  for ( i=0; i<pm->n_detail_levels; i++ ) {
3054  if ( depth<=pm->detail_depth[i] ){
3055  break;
3056  }
3057  }
3058 
3059  // If no valid detail depths specified, use highest.
3060  if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f)) {
3061  i = 1;
3062  }
3063  }
3064 
3065  Interp_detail_level = i-1-tmp_detail_level;
3066 
3067  if ( Interp_detail_level < 0 )
3068  Interp_detail_level = 0;
3069  else if (Interp_detail_level >= pm->n_detail_levels )
3071 
3072  } else {
3073  Interp_detail_level = 0;
3074  }
3075 
3076 #ifndef NDEBUG
3077  if ( Interp_detail_level == 0 ) {
3078  MONITOR_INC( NumHiModelsRend, 1 );
3079  } else if ( Interp_detail_level == pm->n_detail_levels-1 ) {
3080  MONITOR_INC( NumLowModelsRend, 1 );
3081  } else {
3082  MONITOR_INC( NumMedModelsRend, 1 );
3083  }
3084 #endif
3085 
3086  // scale the render box settings based on the "Model Detail" slider
3087  switch (Detail.detail_distance)
3088  {
3089  // 1st dot is 20%
3090  case 0:
3091  Interp_box_scale = 0.2f;
3092  break;
3093 
3094  // 2nd dot is 50%
3095  case 1:
3096  Interp_box_scale = 0.5f;
3097  break;
3098 
3099  // 3rd dot is 80%
3100  case 2:
3101  Interp_box_scale = 0.8f;
3102  break;
3103 
3104  // 4th dot is 100% (this is the default setting for "High" and "Very High" settings)
3105  case 3:
3106  Interp_box_scale = 1.0f;
3107  break;
3108 
3109  // 5th dot (max) is 120%
3110  case 4:
3111  Interp_box_scale = 1.2f;
3112  break;
3113  }
3114 
3115  vec3d auto_back = ZERO_VECTOR;
3116  if (Interp_flags & MR_DEPRECATED_AUTOCENTER) {
3117  // standard autocenter using data in model
3118  if (pm->flags & PM_FLAG_AUTOCEN) {
3119  auto_back = pm->autocenter;
3120  vm_vec_scale(&auto_back, -1.0f);
3121  set_autocen = true;
3122  }
3123  // fake autocenter if we are a missile and don't already have autocen info
3124  else if (Interp_flags & MR_DEPRECATED_IS_MISSILE) {
3125  auto_back.xyz.x = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.x + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.x) / 2.0f );
3126  auto_back.xyz.y = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.y + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.y) / 2.0f );
3127  auto_back.xyz.z = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.z + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.z) / 2.0f );
3128  set_autocen = true;
3129  }
3130 
3131  if (set_autocen)
3132  g3_start_instance_matrix(&auto_back, NULL, true);
3133  }
3134 
3135  gr_zbias(1);
3136 
3137  if(Interp_tmap_flags & TMAP_FLAG_PIXEL_FOG)
3138  {
3139  float fog_near = 10.0f, fog_far = 1000.0f;
3140  neb2_get_adjusted_fog_values(&fog_near, &fog_far, objp);
3141  unsigned char r, g, b;
3142  neb2_get_fog_color(&r, &g, &b);
3143  gr_fog_set(GR_FOGMODE_FOG, r, g, b, fog_near, fog_far);
3144  }
3145 
3146  if (is_outlines_only_htl) {
3148  gr_set_color_fast( &Interp_outline_color );
3149  // lines shouldn't be rendered with textures or special RGB colors (assuming preset colors)
3150  Interp_flags |= MR_DEPRECATED_NO_TEXTURING;
3151  Interp_tmap_flags &= ~TMAP_FLAG_TEXTURED;
3152  Interp_tmap_flags &= ~TMAP_FLAG_RGB;
3153  // don't render with lighting either
3154  Interp_flags |= MR_DEPRECATED_NO_LIGHTING;
3155  } else {
3157  }
3158 
3159  if ( (Interp_flags & MR_DEPRECATED_NO_ZBUFFER) || (Interp_flags & MR_DEPRECATED_ALL_XPARENT) ) {
3160  zbuf_mode = GR_ZBUFF_NONE;
3161  } else {
3162  zbuf_mode = GR_ZBUFF_FULL;
3163  }
3164 
3165  gr_zbuffer_set(zbuf_mode);
3166 
3167  if (Interp_flags & MR_DEPRECATED_EDGE_ALPHA) {
3168  gr_center_alpha(-1);
3169  } else if (Interp_flags & MR_DEPRECATED_CENTER_ALPHA) {
3170  gr_center_alpha(1);
3171  } else {
3172  gr_center_alpha(0);
3173  }
3174 
3175  if ( (Interp_flags & MR_DEPRECATED_NO_CULL) || (Interp_flags & MR_DEPRECATED_ALL_XPARENT) || (Interp_warp_bitmap >= 0) ) {
3176  cull = gr_set_cull(0);
3177  } else {
3178  cull = gr_set_cull(1);
3179  }
3180 
3181  if ( !(Interp_flags & MR_DEPRECATED_NO_LIGHTING) ) {
3182  gr_set_lighting(true, true);
3183  }
3184 
3185  // rotate lights
3186  if ( !(Interp_flags & MR_DEPRECATED_NO_LIGHTING) ) {
3187  light_rotate_all();
3188 
3189  if ( !Cmdline_nohtl ) {
3191  }
3192  }
3193  if ( !(Interp_flags & MR_DEPRECATED_NO_LIGHTING) && (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) ) {
3194  opengl_change_active_lights(0); // Set up OpenGl lighting;
3195  }
3196 
3197  if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
3199  }
3200 
3201  // Draw the subobjects
3203  if(Rendering_to_shadow_map)
3204  gr_zbias(-1024);
3205  else
3206  gr_zbias(0);
3207  while( i >= 0 ) {
3208  if ( !pm->submodel[i].is_thruster ) {
3209  // When in htl mode render with htl method unless its a jump node
3210  if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
3212  } else {
3214  }
3215  } else {
3216  draw_thrusters = true;
3217  }
3218 
3219  i = pm->submodel[i].next_sibling;
3220  }
3221 
3222 
3223 
3225 
3226  //*************************** draw the hull of the ship *********************************************
3227 
3228  // When in htl mode render with htl method unless its a jump node
3229  if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
3231  } else {
3233  }
3234 
3235  // Draw the thruster subobjects
3236  if (draw_thrusters) {
3238 
3239  while( i >= 0 ) {
3240  if (pm->submodel[i].is_thruster) {
3241  // When in htl mode render with htl method unless its a jump node
3242  if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
3244  } else {
3246  }
3247  }
3248  i = pm->submodel[i].next_sibling;
3249  }
3250  }
3251 
3252  if ( !Interp_no_flush ) {
3253  gr_clear_states();
3254 
3255  if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
3256  gr_set_buffer(-1);
3257  }
3258  }
3259 
3260  if (is_outlines_only_htl) {
3262  }
3263 
3264  if ( !(Interp_flags & MR_DEPRECATED_NO_LIGHTING) ) {
3266  gr_set_lighting(false, false);
3267  }
3268 
3269  if (Interp_flags & MR_DEPRECATED_SHOW_PIVOTS ) {
3270  model_draw_debug_points( pm, NULL, Interp_flags );
3271  model_draw_debug_points( pm, &pm->submodel[pm->detail[Interp_detail_level]], Interp_flags );
3272 
3273  if(pm->flags & PM_FLAG_AUTOCEN){
3274  gr_set_color(255, 255, 255);
3275  g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
3276  }
3277  }
3278 
3279  model_radius = 0.0f;
3280 
3281  // render model insignias
3282  gr_zbias(1);
3283 
3284  if (!Cmdline_nohtl) gr_set_texture_panning(0.0, 0.0, false);
3285 
3287  if(!(Interp_flags & MR_DEPRECATED_NO_TEXTURING))
3288  model_render_insignias(pm, Interp_detail_level, Interp_insignia_bitmap);
3289 
3290  gr_zbias(0);
3291 
3292  if (FULLCLOAK != -1) model_finish_cloak(FULLCLOAK);
3293 
3294  if ( pm->submodel[pm->detail[0]].num_arcs ) {
3295  interp_render_lightning( pm, &pm->submodel[pm->detail[0]]);
3296  }
3297 
3298  if ( Interp_flags & MR_DEPRECATED_SHOW_SHIELDS ) {
3299  model_render_shields(pm, Interp_flags);
3300  }
3301 
3302  if ( Interp_flags & MR_DEPRECATED_SHOW_PATHS ){
3303  if (Cmdline_nohtl) model_draw_paths(model_num, Interp_flags);
3304  else model_draw_paths_htl(model_num, Interp_flags);
3305  }
3306 
3307  if (Interp_flags & MR_DEPRECATED_BAY_PATHS ){
3308  if (Cmdline_nohtl) model_draw_bay_paths(model_num);
3309  else model_draw_bay_paths_htl(model_num);
3310  }
3311 
3312  if ( (Interp_flags & MR_DEPRECATED_AUTOCENTER) && (set_autocen) ) {
3313  g3_done_instance(use_api);
3314  }
3315 
3316  g3_done_instance(use_api);
3317 
3318  // start rendering glow points -Bobboau
3319  if ( (pm->n_glow_point_banks) && !is_outlines_only && !is_outlines_only_htl && !Glowpoint_override ) {
3321  }
3322 
3323  // Draw the thruster glow
3324  if ( !is_outlines_only && !is_outlines_only_htl ) {
3325  if ( ( Interp_flags & MR_DEPRECATED_AUTOCENTER ) && set_autocen ) {
3326  vec3d autoback_rotated;
3327 
3328  vm_vec_unrotate(&autoback_rotated, &auto_back, orient);
3329  vm_vec_add2(&autoback_rotated, pos);
3330 
3331  model_render_thrusters( pm, objnum, shipp, orient, &autoback_rotated );
3332  } else {
3333  model_render_thrusters( pm, objnum, shipp, orient, pos );
3334  }
3335  }
3336 
3337  gr_zbuffer_set(save_gr_zbuffering_mode);
3338 
3339  gr_set_cull(cull);
3340 }
3341 
3342 
3343 void submodel_render_DEPRECATED(int model_num, int submodel_num, matrix *orient, vec3d * pos, uint flags, int objnum, int *replacement_textures, int render)
3344 {
3345  // replacement textures - Goober5000
3346  model_set_replacement_textures(replacement_textures);
3347 
3348  polymodel * pm;
3349 
3350  MONITOR_INC( NumModelsRend, 1 );
3351 
3352  if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return;
3353 
3354  // Turn off engine effect
3356 
3357  if (!Model_texturing)
3358  flags |= MR_NO_TEXTURING;
3359 
3360  Interp_flags = flags;
3361  Interp_pos=pos;
3362 
3363  pm = model_get(model_num);
3364 
3365  // Set the flags we will pass to the tmapper
3366  Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
3367 
3368  // if we're in nebula mode
3370  Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
3371  }
3372 
3373  if ( !(Interp_flags & MR_NO_TEXTURING) ) {
3374  Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
3375 
3376  if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling )
3377  Interp_tmap_flags |= TMAP_FLAG_TILED;
3378 
3379  if ( !(Interp_flags & MR_NO_CORRECT) ) {
3380  Interp_tmap_flags |= TMAP_FLAG_CORRECT;
3381  }
3382  }
3383 
3384 // if ( Interp_flags & MR_ANIMATED_SHADER )
3385 // Interp_tmap_flags |= TMAP_ANIMATED_SHADER;
3386 
3387  bool is_outlines_only_htl = !Cmdline_nohtl && (flags & MR_NO_POLYS) && (flags & MR_SHOW_OUTLINE_HTL);
3388 
3389  //set to true since D3d and OGL need the api matrices set
3390  g3_start_instance_matrix(pos, orient, true);
3391  bool set_autocen = false;
3392  vec3d auto_back = ZERO_VECTOR;
3393  if (Interp_flags & MR_AUTOCENTER) {
3394  // standard autocenter using data in model
3395  if (pm->flags & PM_FLAG_AUTOCEN) {
3396  auto_back = pm->autocenter;
3397  vm_vec_scale(&auto_back, -1.0f);
3398  set_autocen = true;
3399  }
3400  // fake autocenter if we are a missile and don't already have autocen info
3401  else if (Interp_flags & MR_IS_MISSILE) {
3402  auto_back.xyz.x = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.x + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.x) / 2.0f );
3403  auto_back.xyz.y = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.y + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.y) / 2.0f );
3404  auto_back.xyz.z = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.z + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.z) / 2.0f );
3405  set_autocen = true;
3406  }
3407 
3408  if (set_autocen)
3409  g3_start_instance_matrix(&auto_back, NULL, true);
3410  }
3411 
3412  if (is_outlines_only_htl) {
3414 
3415  // lines shouldn't be rendered with textures or special RGB colors (assuming preset colors)
3416  Interp_flags |= MR_NO_TEXTURING;
3417  Interp_tmap_flags &= ~TMAP_FLAG_TEXTURED;
3418  Interp_tmap_flags &= ~TMAP_FLAG_RGB;
3419  // don't render with lighting either
3420  Interp_flags |= MR_NO_LIGHTING;
3421  } else {
3423  }
3424 
3425  if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
3426  Interp_light = 1.0f;
3427 
3429 
3430  light_filter_push( -1, pos, pm->submodel[submodel_num].rad );
3431 
3432  light_rotate_all();
3433 
3434  if (!Cmdline_nohtl) {
3436  }
3437 
3438  gr_set_lighting(true, true);
3439  }
3440 
3441  Interp_base_frametime = 0;
3442 
3443  if (objnum >= 0) {
3444  object *objp = &Objects[objnum];
3445 
3446  if (objp->type == OBJ_SHIP) {
3447  Interp_base_frametime = Ships[objp->instance].base_texture_anim_frametime;
3448  }
3449  }
3450 
3451  // fixes disappearing HUD in OGL - taylor
3452  int cull = gr_set_cull(1);
3453 
3454  if (!Cmdline_nohtl) {
3455 
3456  // RT - Put this here to fog debris
3457  if(Interp_tmap_flags & TMAP_FLAG_PIXEL_FOG)
3458  {
3459  float fog_near, fog_far;
3460  object *obj = NULL;
3461 
3462  if (objnum >= 0)
3463  obj = &Objects[objnum];
3464 
3465  neb2_get_adjusted_fog_values(&fog_near, &fog_far, obj);
3466  unsigned char r, g, b;
3467  neb2_get_fog_color(&r, &g, &b);
3468  gr_fog_set(GR_FOGMODE_FOG, r, g, b, fog_near, fog_far);
3469  }
3470  if(Rendering_to_shadow_map)
3471  gr_zbias(-1024);
3472  else
3473  gr_zbias(0);
3475 
3476  model_render_buffers_DEPRECATED(pm, submodel_num, render);
3477 
3478  gr_set_buffer(-1);
3479  } else {
3480  model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 );
3481  }
3482 
3483  if ( !(Interp_flags & MR_NO_LIGHTING) ) {
3484  gr_set_lighting(false, false);
3486  }
3487 
3488  gr_set_cull(cull);
3489 
3491 
3492  if ( pm->submodel[submodel_num].num_arcs ) {
3493  interp_render_lightning( pm, &pm->submodel[submodel_num]);
3494  }
3495 
3496  if (Interp_flags & MR_DEPRECATED_SHOW_PIVOTS )
3497  model_draw_debug_points( pm, &pm->submodel[submodel_num], Interp_flags );
3498 
3499  if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
3500  light_filter_pop();
3501  }
3502  gr_zbias(0);
3503  if (set_autocen)
3504  g3_done_instance(true);
3505  g3_done_instance(true);
3506 
3507  // turn off fog after each model renders, RT This fixes HUD being fogged when debris is in target box
3509  gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
3510  }
3511 
3512  gr_clear_states();
3513 }
3514 
3515 // Fills in an array with points from a model.
3516 // Only gets up to max_num verts;
3517 // Returns number of verts found;
3518 static int submodel_get_points_internal(int model_num, int submodel_num)
3519 {
3520  polymodel * pm;
3521 
3522  pm = model_get(model_num);
3523 
3524  if ( submodel_num < 0 ) {
3525  submodel_num = pm->detail[0];
3526  }
3527 
3529  int chunk_type, chunk_size;
3530 
3531  chunk_type = w(p);
3532  chunk_size = w(p+4);
3533 
3534  while (chunk_type != OP_EOF) {
3535  switch (chunk_type) {
3536  case OP_DEFPOINTS: {
3537  int n;
3538  int nverts = w(p+8);
3539  int offset = w(p+16);
3540  int nnorms = 0;
3541 
3542  ubyte * normcount = p+20;
3543  vec3d *src = vp(p+offset);
3544 
3545  for (n = 0; n < nverts; n++) {
3546  nnorms += normcount[n];
3547  }
3548 
3549  model_allocate_interp_data(nverts, nnorms);
3550 
3551  // this must happen only after the interp_data allocation call (since the address changes)
3552  vec3d **verts = Interp_verts;
3553  vec3d **norms = Interp_norms;
3554 
3555  for (n=0; n<nverts; n++ ) {
3556  *verts++ = src;
3557  *norms++ = src + 1; // first normal associated with the point
3558 
3559  src += normcount[n]+1;
3560  }
3561  return nverts; // Read in 'n' points
3562  }
3563  break;
3564  case OP_FLATPOLY: break;
3565  case OP_TMAPPOLY: break;
3566  case OP_SORTNORM: break;
3567  case OP_BOUNDBOX: break;
3568  default:
3569  mprintf(( "Bad chunk type %d, len=%d in submodel_get_points\n", chunk_type, chunk_size ));
3570  Int3(); // Bad chunk type!
3571  return 0;
3572  }
3573  p += chunk_size;
3574  chunk_type = w(p);
3575  chunk_size = w(p+4);
3576  }
3577  return 0; // Couldn't find 'em
3578 }
3579 
3583 void submodel_get_two_random_points(int model_num, int submodel_num, vec3d *v1, vec3d *v2, vec3d *n1, vec3d *n2 )
3584 {
3585  int nv = submodel_get_points_internal(model_num, submodel_num);
3586 
3587  // this is not only because of the immediate div-0 error but also because of the less immediate expectation for at least one point (preferably two) to be found
3588  if (nv <= 0) {
3589  polymodel *pm = model_get(model_num);
3590  Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_points_internal!", model_num, (pm == NULL) ? "<null model?!?>" : pm->filename);
3591 
3592  // in case people ignore the error...
3593  vm_vec_zero(v1);
3594  vm_vec_zero(v2);
3595  if (n1 != NULL) {
3596  vm_vec_zero(n1);
3597  }
3598  if (n2 != NULL) {
3599  vm_vec_zero(n2);
3600  }
3601  return;
3602  }
3603 
3604 #ifndef NDEBUG
3605  if (RAND_MAX < nv)
3606  {
3607  static int submodel_get_two_random_points_warned = false;
3608  if (!submodel_get_two_random_points_warned)
3609  {
3610  polymodel *pm = model_get(model_num);
3611  Warning(LOCATION, "RAND_MAX is only %d, but submodel %d for model %s has %d vertices! Explosions will not propagate through the entire model!\n", RAND_MAX, submodel_num, pm->filename, nv);
3612  submodel_get_two_random_points_warned = true;
3613  }
3614  }
3615 #endif
3616 
3617  int vn1 = myrand() % nv;
3618  int vn2 = myrand() % nv;
3619 
3620  *v1 = *Interp_verts[vn1];
3621  *v2 = *Interp_verts[vn2];
3622 
3623  if(n1 != NULL){
3624  *n1 = *Interp_norms[vn1];
3625  }
3626  if(n2 != NULL){
3627  *n2 = *Interp_norms[vn2];
3628  }
3629 }
3630 
3631 void submodel_get_two_random_points_better(int model_num, int submodel_num, vec3d *v1, vec3d *v2)
3632 {
3633  polymodel *pm = model_get(model_num);
3634 
3635  if (pm != NULL) {
3636  if ( submodel_num < 0 ) {
3637  submodel_num = pm->detail[0];
3638  }
3639 
3641 
3642  int nv = tree->n_verts;
3643 
3644  // this is not only because of the immediate div-0 error but also because of the less immediate expectation for at least one point (preferably two) to be found
3645  if (nv <= 0) {
3646  Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_points_internal!", model_num, (pm == NULL) ? "<null model?!?>" : pm->filename);
3647 
3648  // in case people ignore the error...
3649  vm_vec_zero(v1);
3650  vm_vec_zero(v2);
3651 
3652  return;
3653  }
3654 
3655 #ifndef NDEBUG
3656  if (RAND_MAX < nv)
3657  {
3658  static int submodel_get_two_random_points_warned = false;
3659  if (!submodel_get_two_random_points_warned)
3660  {
3661  Warning(LOCATION, "RAND_MAX is only %d, but submodel %d for model %s has %d vertices! Explosions will not propagate through the entire model!\n", RAND_MAX, submodel_num, pm->filename, nv);
3662  submodel_get_two_random_points_warned = true;
3663  }
3664  }
3665 #endif
3666 
3667  int vn1 = myrand() % nv;
3668  int vn2 = myrand() % nv;
3669 
3670  *v1 = tree->point_list[vn1];
3671  *v2 = tree->point_list[vn2];
3672  }
3673 }
3674 
3675 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
3676 // This defaults to black.
3677 void model_set_outline_color(int r, int g, int b )
3678 {
3679  gr_init_color( &Interp_outline_color, r, g, b );
3680 
3681 }
3682 
3683 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
3684 // This defaults to black.
3685 void model_set_outline_color_fast(void *outline_color)
3686 {
3687  Interp_outline_color = *((color*)(outline_color));
3688 }
3689 
3690 // IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n'
3691 // This defaults to 0. (0=highest, larger=lower)
3693 {
3694  Interp_detail_level_locked = n;
3695 }
3696 
3700 int submodel_get_num_verts(int model_num, int submodel_num )
3701 {
3702  polymodel * pm;
3703 
3704  pm = model_get(model_num);
3705 
3706  ubyte *p = pm->submodel[submodel_num].bsp_data;
3707  int chunk_type, chunk_size;
3708 
3709  chunk_type = w(p);
3710  chunk_size = w(p+4);
3711 
3712  while (chunk_type != OP_EOF) {
3713  switch (chunk_type) {
3714  case OP_DEFPOINTS: {
3715  int n=w(p+8);
3716  return n; // Read in 'n' points
3717  }
3718  break;
3719  case OP_FLATPOLY: break;
3720  case OP_TMAPPOLY: break;
3721  case OP_SORTNORM: break;
3722  case OP_BOUNDBOX: break;
3723  default:
3724  mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size ));
3725  Int3(); // Bad chunk type!
3726  return 0;
3727  }
3728  p += chunk_size;
3729  chunk_type = w(p);
3730  chunk_size = w(p+4);
3731  }
3732  return 0; // Couldn't find 'em
3733 }
3734 
3739 {
3740  int chunk_type = w(p);
3741  int chunk_size = w(p+4);
3742  int n = 0;
3743 
3744  while (chunk_type != OP_EOF) {
3745  switch (chunk_type) {
3746  case OP_DEFPOINTS: break;
3747  case OP_FLATPOLY: n++; break;
3748  case OP_TMAPPOLY: n++; break;
3749  case OP_SORTNORM: {
3750  int frontlist = w(p+36);
3751  int backlist = w(p+40);
3752  int prelist = w(p+44);
3753  int postlist = w(p+48);
3754  int onlist = w(p+52);
3755  n += submodel_get_num_polys_sub(p+frontlist);
3756  n += submodel_get_num_polys_sub(p+backlist);
3757  n += submodel_get_num_polys_sub(p+prelist);
3758  n += submodel_get_num_polys_sub(p+postlist );
3759  n += submodel_get_num_polys_sub(p+onlist );
3760  }
3761  break;
3762  case OP_BOUNDBOX: break;
3763  default:
3764  mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size ));
3765  Int3(); // Bad chunk type!
3766  return 0;
3767  }
3768  p += chunk_size;
3769  chunk_type = w(p);
3770  chunk_size = w(p+4);
3771  }
3772  return n;
3773 }
3774 
3778 int submodel_get_num_polys(int model_num, int submodel_num )
3779 {
3780  polymodel * pm;
3781 
3782  pm = model_get(model_num);
3783 
3784  return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data );
3785 }
3786 
3787 // Sets the submodel instance data in a submodel
3788 // If show_damaged is true it shows only damaged submodels.
3789 // If it is false it shows only undamaged submodels.
3790 void model_show_damaged(int model_num, int show_damaged )
3791 {
3792  polymodel * pm;
3793  int i;
3794 
3795  pm = model_get(model_num);
3796 
3797  for (i=0; i<pm->n_models; i++ ) {
3798  bsp_info *sm = &pm->submodel[i];
3799 
3800  // Set the "blown out" flags
3801  sm->blown_off = 0;
3802  }
3803 
3804  for (i=0; i<pm->n_models; i++ ) {
3805  bsp_info *sm = &pm->submodel[i];
3806 
3807  // Set the "blown out" flags
3808  if ( show_damaged ) {
3809  if ( sm->my_replacement > -1 ) {
3810  pm->submodel[sm->my_replacement].blown_off = 0;
3811  sm->blown_off = 1;
3812  }
3813  } else {
3814  if ( sm->my_replacement > -1 ) {
3815  pm->submodel[sm->my_replacement].blown_off = 1;
3816  sm->blown_off = 0;
3817  }
3818  }
3819  }
3820 }
3821 
3826 {
3827  Interp_insignia_bitmap = bmap;
3828 }
3829 
3830 void model_set_replacement_textures(int *replacement_textures)
3831 {
3832  Interp_new_replacement_textures = replacement_textures;
3833 }
3834 
3839 {
3840  Interp_forced_bitmap = bmap;
3841 }
3842 
3847 {
3848  Interp_xparent_alpha = alpha;
3849 }
3850 
3854 int model_find_texture(int model_num, int bitmap)
3855 {
3856  polymodel * pm;
3857  int idx;
3858 
3859  // get a handle to the model
3860  pm = model_get(model_num);
3861  if(pm == NULL){
3862  return -1;
3863  }
3864 
3865  // find the texture
3866  for(idx=0; idx<pm->n_textures; idx++)
3867  {
3868  if(pm->maps[idx].FindTexture(bitmap) > -1)
3869  {
3870  return 1;
3871  }
3872  }
3873 
3874  // no texture
3875  return 0;
3876 }
3877 
3878 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
3879 // returns closest distance to extended box
3880 // positive return value means start_point is outside extended box
3881 // displaces closest point an optional amount delta to the outside of the box
3882 // closest_box_point can be NULL.
3883 float get_model_closest_box_point_with_delta(vec3d *closest_box_point, vec3d *start_point, int modelnum, int *is_inside, float delta)
3884 {
3885  int i, idx;
3886  vec3d box_point, ray_direction, *extremes;
3887  float dist, best_dist;
3888  polymodel *pm;
3889  int inside = 0;
3890  int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
3891  int mask_inside = 0x3f;
3892 
3893  best_dist = FLT_MAX;
3894  pm = model_get(modelnum);
3895 
3896  for (i=0; i<6; i++) {
3897  idx = i / 2; // which row vector of Identity matrix
3898 
3899  memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vec3d));
3900 
3901  // do negative, then positive plane for each axis
3902  if (2 * idx == i) {
3903  extremes = &pm->mins;
3904  vm_vec_negate(&ray_direction);
3905  } else {
3906  extremes = &pm->maxs;
3907  }
3908 
3909  // a negative distance means started outside the box
3910  dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f);
3911  if (dist > 0) {
3912  inside |= masks[i];
3913  }
3914  if (fabs(dist) < fabs(best_dist)) {
3915  best_dist = dist;
3916  if (closest_box_point) {
3917  vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta);
3918  }
3919  }
3920  }
3921 
3922  // is start_point inside the box
3923  if (is_inside) {
3924  *is_inside = (inside == mask_inside);
3925  }
3926 
3927  return -best_dist;
3928 }
3929 
3930 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
3931 // returns closest distance to extended box
3932 // positive return value means start_point is outside extended box
3933 // displaces closest point an optional amount delta to the outside of the box
3934 // closest_box_point can be NULL.
3935 float get_world_closest_box_point_with_delta(vec3d *closest_box_point, object *box_obj, vec3d *start_point, int *is_inside, float delta)
3936 {
3937  vec3d temp, box_start;
3938  float dist;
3939  int modelnum;
3940 
3941  // get modelnum
3942  modelnum = Ship_info[Ships[box_obj->instance].ship_info_index].model_num;
3943 
3944  // rotate start_point to box_obj RF
3945  vm_vec_sub(&temp, start_point, &box_obj->pos);
3946  vm_vec_rotate(&box_start, &temp, &box_obj->orient);
3947 
3948  dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta);
3949 
3950  // rotate closest_box_point to world RF
3951  if (closest_box_point) {
3952  vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient);
3953  vm_vec_add(closest_box_point, &temp, &box_obj->pos);
3954  }
3955 
3956  return dist;
3957 }
3958 
3959 void model_set_fog_level(float l)
3960 {
3961  Interp_fog_level = l;
3962 }
3963 
3967 void model_page_in_textures(int modelnum, int ship_info_index)
3968 {
3969  int i, idx;
3970  polymodel *pm = model_get(modelnum);
3971 
3972  // bogus
3973  if (pm == NULL)
3974  return;
3975 
3976  for (idx = 0; idx < pm->n_textures; idx++) {
3977  pm->maps[idx].PageIn();
3978  }
3979 
3980  for (i = 0; i < pm->n_glow_point_banks; i++) {
3981  glow_point_bank *bank = &pm->glow_point_banks[i];
3982 
3985  }
3986 
3987  if (ship_info_index >= 0)
3988  ship_page_in_textures(ship_info_index);
3989 }
3990 
3991 // unload all textures for a given model
3992 // "release" should only be set if called from model_unload()!!!
3993 void model_page_out_textures(int model_num, bool release)
3994 {
3995  int i, j;
3996 
3997  if (model_num < 0)
3998  return;
3999 
4000  polymodel *pm = model_get(model_num);
4001 
4002  if (pm == NULL)
4003  return;
4004 
4005  if (release && (pm->used_this_mission > 0))
4006  return;
4007 
4008 
4009  for (i = 0; i < pm->n_textures; i++) {
4010  pm->maps[i].PageOut(release);
4011  }
4012 
4013  // NOTE: "release" doesn't work here for some, as of yet unknown, reason - taylor
4014  for (j = 0; j < pm->n_glow_point_banks; j++) {
4015  glow_point_bank *bank = &pm->glow_point_banks[j];
4016 
4017  if (bank->glow_bitmap >= 0) {
4018  // if (release) {
4019  // bm_release(bank->glow_bitmap);
4020  // bank->glow_bitmap = -1;
4021  // } else {
4022  bm_unload(bank->glow_bitmap);
4023  // }
4024  }
4025 
4026  if (bank->glow_neb_bitmap >= 0) {
4027  // if (release) {
4028  // bm_release(bank->glow_neb_bitmap);
4029  // bank->glow_neb_bitmap = -1;
4030  // } else {
4031  bm_unload(bank->glow_neb_bitmap);
4032  // }
4033  }
4034  }
4035 }
4036 
4037 
4038 //**********vertex buffer stuff**********//
4041 
4042 void parse_defpoint(int off, ubyte *bsp_data)
4043 {
4044  int i, n;
4045  int nverts = w(off+bsp_data+8);
4046  int offset = w(off+bsp_data+16);
4047  int next_norm = 0;
4048 
4049  ubyte *normcount = off+bsp_data+20;
4050  vec3d *src = vp(off+bsp_data+offset);
4051 
4052  // Get pointer to lights
4053  Interp_lights = off+bsp_data+20+nverts;
4054 
4055 #ifndef NDEBUG
4056  modelstats_num_verts += nverts;
4057 #endif
4058 
4059  for (n = 0; n < nverts; n++) {
4060  Interp_verts[n] = src;
4061  src++; // move to normal
4062 
4063  for (i = 0; i < normcount[n]; i++) {
4064  Interp_norms[next_norm] = src;
4065 
4066  next_norm++;
4067  src++;
4068  }
4069  }
4070 }
4071 
4073 {
4074  // Values equal to -1.#IND0
4075  if(!is_valid_vec(N))
4076  {
4077  N->xyz.x = 1.0f;
4078  N->xyz.y = 0.0f;
4079  N->xyz.z = 0.0f;
4080  return 1;
4081  }
4082 
4083  return 0;
4084 }
4085 
4087 
4088 void parse_tmap(int offset, ubyte *bsp_data)
4089 {
4090  int pof_tex = w(bsp_data+offset+40);
4091  int n_vert = w(bsp_data+offset+36);
4092 
4093  ubyte *p = &bsp_data[offset+8];
4094  model_tmap_vert *tverts;
4095 
4096  tverts = (model_tmap_vert *)&bsp_data[offset+44];
4097 
4098  vertex *V;
4099  vec3d *v;
4100  vec3d *N;
4101 
4102  int problem_count = 0;
4103 
4104  for (int i = 1; i < (n_vert-1); i++) {
4105  V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)];
4106  N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)];
4107  v = Interp_verts[(int)tverts[0].vertnum];
4108  V->world.xyz.x = v->xyz.x;
4109  V->world.xyz.y = v->xyz.y;
4110  V->world.xyz.z = v->xyz.z;
4111  V->texture_position.u = tverts[0].u;
4112  V->texture_position.v = tverts[0].v;
4113 
4114  *N = *Interp_norms[(int)tverts[0].normnum];
4115 
4116  if ( IS_VEC_NULL(N) )
4117  *N = *vp(p);
4118 
4119  problem_count += check_values(N);
4121 
4122  V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)+1];
4123  N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)+1];
4124  v = Interp_verts[(int)tverts[i].vertnum];
4125  V->world.xyz.x = v->xyz.x;
4126  V->world.xyz.y = v->xyz.y;
4127  V->world.xyz.z = v->xyz.z;
4128  V->texture_position.u = tverts[i].u;
4129  V->texture_position.v = tverts[i].v;
4130 
4131  *N = *Interp_norms[(int)tverts[i].normnum];
4132 
4133  if ( IS_VEC_NULL(N) )
4134  *N = *vp(p);
4135 
4136  problem_count += check_values(N);
4138 
4139  V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)+2];
4140  N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)+2];
4141  v = Interp_verts[(int)tverts[i+1].vertnum];
4142  V->world.xyz.x = v->xyz.x;
4143  V->world.xyz.y = v->xyz.y;
4144  V->world.xyz.z = v->xyz.z;
4145  V->texture_position.u = tverts[i+1].u;
4146  V->texture_position.v = tverts[i+1].v;
4147 
4148  *N = *Interp_norms[(int)tverts[i+1].normnum];
4149 
4150  if ( IS_VEC_NULL(N) )
4151  *N = *vp(p);
4152 
4153  problem_count += check_values(N);
4155 
4156  polygon_list[pof_tex].n_verts += 3;
4157  }
4158 
4159  Parse_normal_problem_count += problem_count;
4160 }
4161 
4162 void parse_sortnorm(int offset, ubyte *bsp_data);
4163 
4164 void parse_bsp(int offset, ubyte *bsp_data)
4165 {
4166  int id = w(bsp_data+offset);
4167  int size = w(bsp_data+offset+4);
4168 
4169  while (id != 0) {
4170  switch (id)
4171  {
4172  case OP_DEFPOINTS:
4173  parse_defpoint(offset, bsp_data);
4174  break;
4175 
4176  case OP_SORTNORM:
4177  parse_sortnorm(offset, bsp_data);
4178  break;
4179 
4180  case OP_FLATPOLY:
4181  break;
4182 
4183  case OP_TMAPPOLY:
4184  parse_tmap(offset, bsp_data);
4185  break;
4186 
4187  case OP_BOUNDBOX:
4188  break;
4189 
4190  default:
4191  return;
4192  }
4193 
4194  offset += size;
4195  id = w(bsp_data+offset);
4196  size = w(bsp_data+offset+4);
4197 
4198  if (size < 1)
4199  id = OP_EOF;
4200  }
4201 }
4202 
4203 void parse_sortnorm(int offset, ubyte *bsp_data)
4204 {
4205  int frontlist, backlist, prelist, postlist, onlist;
4206 
4207  frontlist = w(bsp_data+offset+36);
4208  backlist = w(bsp_data+offset+40);
4209  prelist = w(bsp_data+offset+44);
4210  postlist = w(bsp_data+offset+48);
4211  onlist = w(bsp_data+offset+52);
4212 
4213  if (prelist) parse_bsp(offset+prelist,bsp_data);
4214  if (backlist) parse_bsp(offset+backlist, bsp_data);
4215  if (onlist) parse_bsp(offset+onlist, bsp_data);
4216  if (frontlist) parse_bsp(offset+frontlist, bsp_data);
4217  if (postlist) parse_bsp(offset+postlist, bsp_data);
4218 }
4219 
4220 void find_tmap(int offset, ubyte *bsp_data)
4221 {
4222  int pof_tex = w(bsp_data+offset+40);
4223  int n_vert = w(bsp_data+offset+36);
4224 
4225  tri_count[pof_tex] += n_vert-2;
4226 }
4227 
4228 void find_defpoint(int off, ubyte *bsp_data)
4229 {
4230  int n;
4231  int nverts = w(off+bsp_data+8);
4232 
4233  ubyte * normcount = off+bsp_data+20;
4234 
4235  // Get pointer to lights
4236  Interp_lights = off+bsp_data+20+nverts;
4237 
4238 #ifndef NDEBUG
4239  modelstats_num_verts += nverts;
4240 #endif
4241 
4242  int norm_num = 0;
4243 
4244  for (n = 0; n < nverts; n++) {
4245  norm_num += normcount[n];
4246  }
4247 
4248  Interp_num_verts = nverts;
4249  Interp_num_norms = norm_num;
4250 }
4251 
4252 void find_sortnorm(int offset, ubyte *bsp_data);
4253 
4254 // tri_count
4255 void find_tri_counts(int offset, ubyte *bsp_data)
4256 {
4257  int id = w(bsp_data+offset);
4258  int size = w(bsp_data+offset+4);
4259 
4260  while (id != 0) {
4261  switch (id)
4262  {
4263  case OP_DEFPOINTS:
4264  find_defpoint(offset, bsp_data);
4265  break;
4266 
4267  case OP_SORTNORM:
4268  find_sortnorm(offset, bsp_data);
4269  break;
4270 
4271  case OP_FLATPOLY:
4272  break;
4273 
4274  case OP_TMAPPOLY:
4275  find_tmap(offset, bsp_data);
4276  break;
4277 
4278  case OP_BOUNDBOX:
4279  break;
4280 
4281  default:
4282  return;
4283  }
4284 
4285  offset += size;
4286  id = w(bsp_data+offset);
4287  size = w(bsp_data+offset+4);
4288 
4289  if (size < 1)
4290  id = OP_EOF;
4291  }
4292 }
4293 
4294 void find_sortnorm(int offset, ubyte *bsp_data)
4295 {
4296  int frontlist, backlist, prelist, postlist, onlist;
4297 
4298  frontlist = w(bsp_data+offset+36);
4299  backlist = w(bsp_data+offset+40);
4300  prelist = w(bsp_data+offset+44);
4301  postlist = w(bsp_data+offset+48);
4302  onlist = w(bsp_data+offset+52);
4303 
4304  if (prelist) find_tri_counts(offset+prelist,bsp_data);
4305  if (backlist) find_tri_counts(offset+backlist, bsp_data);
4306  if (onlist) find_tri_counts(offset+onlist, bsp_data);
4307  if (frontlist) find_tri_counts(offset+frontlist, bsp_data);
4308  if (postlist) find_tri_counts(offset+postlist, bsp_data);
4309 }
4310 
4312 {
4313  Assert( pm->vertex_buffer_id >= 0 );
4314  Assert( (mn >= 0) && (mn < pm->n_models) );
4315 
4316  bsp_info *model = &pm->submodel[mn];
4317 
4318  if ( !model->buffer.model_list ) {
4319  return;
4320  }
4321 
4322  bool rval = gr_pack_buffer(pm->vertex_buffer_id, &model->buffer);
4323 
4324  if ( model->trans_buffer.flags & VB_FLAG_TRANS && model->trans_buffer.tex_buf.size() > 0 ) {
4326  }
4327 
4328  if ( !rval ) {
4329  Error( LOCATION, "Unable to pack vertex buffer for '%s'\n", pm->filename );
4330  }
4331 }
4332 
4334 {
4335  int i, j, first_index;
4336  uint total_verts = 0;
4337  SCP_vector<int> vertex_list;
4338 
4339  Assert( (mn >= 0) && (mn < pm->n_models) );
4340 
4341  bsp_info *model = &pm->submodel[mn];
4342 
4343  for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
4344  polygon_list[i].n_verts = 0;
4345  tri_count[i] = 0;
4346  }
4347 
4348  bsp_polygon_data *bsp_polies = new bsp_polygon_data(model->bsp_data);
4349 
4350  for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
4351  int vert_count = bsp_polies->get_num_triangles(i) * 3;
4352  tri_count[i] = vert_count / 3;
4353  total_verts += vert_count;
4354 
4355  polygon_list[i].allocate(vert_count);
4356 
4357  bsp_polies->generate_triangles(i, polygon_list[i].vert, polygon_list[i].norm);
4358  polygon_list[i].n_verts = vert_count;
4359 
4360  // set submodel ID
4361  if ( GLSL_version >= 130 ) {
4362  for ( j = 0; j < polygon_list[i].n_verts; ++j ) {
4363  polygon_list[i].submodels[j] = mn;
4364  }
4365  }
4366 
4367  // for the moment we can only support INT_MAX worth of verts per index buffer
4368  if (total_verts > INT_MAX) {
4369  Error( LOCATION, "Unable to generate vertex buffer data because model '%s' with %i verts is over the maximum of %i verts!\n", pm->filename, total_verts, INT_MAX);
4370  }
4371  }
4372 
4373  // figure out if we have an outline
4374  int outline_n_lines = bsp_polies->get_num_lines(-1);
4375 
4376  if ( outline_n_lines > 0 ) {
4377  model->n_verts_outline = outline_n_lines * 2;
4378  model->outline_buffer = (vertex*)vm_malloc(sizeof(vertex) * model->n_verts_outline);
4379 
4380  bsp_polies->generate_lines(-1, model->outline_buffer);
4381  }
4382 
4383  // done with the bsp now that we have the vertex data
4384  delete bsp_polies;
4385 
4386  if (total_verts < 1) {
4387  return;
4388  }
4389 
4390  total_verts = 0;
4391 
4392  for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
4393  total_verts += polygon_list[i].n_verts;
4394  }
4395 
4396  poly_list *model_list = new(std::nothrow) poly_list;
4397 
4398  if ( !model_list ) {
4399  Error( LOCATION, "Unable to allocate memory for poly_list!\n" );
4400  }
4401 
4402  model->buffer.model_list = model_list;
4403 
4404  model_list->allocate( (int)total_verts );
4405 
4406  for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
4407  if ( !polygon_list[i].n_verts )
4408  continue;
4409 
4410  memcpy( (model_list->vert) + model_list->n_verts, polygon_list[i].vert, sizeof(vertex) * polygon_list[i].n_verts );
4411  memcpy( (model_list->norm) + model_list->n_verts, polygon_list[i].norm, sizeof(vec3d) * polygon_list[i].n_verts );
4412 
4413  if (Cmdline_normal) {
4414  memcpy( (model_list->tsb) + model_list->n_verts, polygon_list[i].tsb, sizeof(tsb_t) * polygon_list[i].n_verts );
4415  }
4416 
4417  if ( GLSL_version >= 130 ) {
4418  memcpy( (model_list->submodels) + model_list->n_verts, polygon_list[i].submodels, sizeof(int) * polygon_list[i].n_verts );
4419  }
4420 
4421  model_list->n_verts += polygon_list[i].n_verts;
4422  }
4423 
4424  // no read file so we'll have to generate
4425  model_list->make_index_buffer(vertex_list);
4426 
4427  vertex_list.clear(); // done
4428 
4429  int vertex_flags = (VB_FLAG_POSITION | VB_FLAG_NORMAL | VB_FLAG_UV1);
4430 
4431  if (model_list->tsb != NULL) {
4433  vertex_flags |= VB_FLAG_TANGENT;
4434  }
4435 
4436  if ( model_list->submodels != NULL ) {
4437  Assert( GLSL_version >= 130 );
4438  vertex_flags |= VB_FLAG_MODEL_ID;
4439  }
4440 
4441  model->buffer.flags = vertex_flags;
4442 
4443  for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
4444  if ( !polygon_list[i].n_verts )
4445  continue;
4446 
4447  buffer_data new_buffer(polygon_list[i].n_verts);
4448 
4449  Verify( new_buffer.get_index() != NULL );
4450 
4451  for (j = 0; j < polygon_list[i].n_verts; j++) {
4452  first_index = model_list->find_index_fast(&polygon_list[i], j);
4453  Assert(first_index != -1);
4454 
4455  new_buffer.assign(j, first_index);
4456  }
4457 
4458  new_buffer.texture = i;
4459 
4460  new_buffer.flags = 0;
4461 
4462  if (polygon_list[i].n_verts >= USHRT_MAX) {
4463  new_buffer.flags |= VB_FLAG_LARGE_INDEX;
4464  }
4465 
4466  model->buffer.tex_buf.push_back( new_buffer );
4467  }
4468 
4469  bool rval = gr_config_buffer(pm->vertex_buffer_id, &model->buffer, false);
4470 
4471  if ( !rval ) {
4472  Error( LOCATION, "Unable to configure vertex buffer for '%s'\n", pm->filename );
4473  }
4474 }
4475 
4477 {
4478  size_t i, j, k;
4479  size_t src_buff_size;
4480  buffer_data *src_buffer;
4481  buffer_data *dest_buffer;
4482  uint vert_offset = src->vertex_offset / src->stride; // assuming all submodels crunched into this index buffer have the same stride
4483  //int vert_offset = 0;
4484 
4485  for ( i = 0; i < dest->tex_buf.size(); ++i ) {
4486  dest_buffer = &dest->tex_buf[i];
4487 
4488  for ( j = 0; j < src->tex_buf.size(); ++j ) {
4489  if ( dest_buffer->texture != src->tex_buf[j].texture ) {
4490  continue;
4491  }
4492 
4493  src_buffer = &src->tex_buf[j];
4494  src_buff_size = (size_t)src_buffer->n_verts;
4495 
4496  for ( k = 0; k < src_buff_size; ++k ) {
4497  dest_buffer->assign(dest_buffer->n_verts, src_buffer->get_index()[k] + vert_offset); // take into account the vertex offset.
4498  dest_buffer->n_verts++;
4499 
4500  Assert(dest_buffer->n_verts <= index_counts[dest_buffer->texture]);
4501  }
4502  }
4503  }
4504 }
4505 
4507 {
4508  int index_counts[MAX_MODEL_TEXTURES];
4509  int i, j;
4510  int model_num;
4511 
4512  for ( i = 0; i < MAX_MODEL_TEXTURES; ++i ) {
4513  index_counts[i] = 0;
4514  }
4515 
4516  buffer->vertex_offset = 0;
4517  buffer->model_list = new(std::nothrow) poly_list;
4518 
4519  int num_buffers;
4520  int tex_num;
4521 
4522  // need to first count how many indexes there are in this entire detail model hierarchy
4523  for ( i = 0; i < (int)submodel_list.size(); ++i ) {
4524  model_num = submodel_list[i];
4525 
4526  if ( pm->submodel[model_num].is_thruster ) {
4527  continue;
4528  }
4529 
4530  num_buffers = (int)pm->submodel[model_num].buffer.tex_buf.size();
4531 
4532  buffer->flags |= pm->submodel[model_num].buffer.flags;
4533 
4534  for ( j = 0; j < num_buffers; ++j ) {
4535  tex_num = pm->submodel[model_num].buffer.tex_buf[j].texture;
4536 
4537  index_counts[tex_num] += pm->submodel[model_num].buffer.tex_buf[j].n_verts;
4538  }
4539  }
4540 
4541  // allocate the respective texture buffers with indexes for our detail buffer
4542  for ( i = 0; i < MAX_MODEL_TEXTURES; ++i ) {
4543  if ( index_counts[i] <= 0 ) {
4544  continue;
4545  }
4546 
4547  buffer->tex_buf.push_back(buffer_data(index_counts[i]));
4548 
4549  buffer_data &new_buffer = buffer->tex_buf.back();
4550  //new_buffer.n_verts = 0;
4551  new_buffer.texture = i;
4552  }
4553 
4554  for ( i = 0; i < (int)buffer->tex_buf.size(); ++i ) {
4555  buffer->tex_buf[i].n_verts = 0;
4556  }
4557 
4558  // finally copy over the indexes
4559  for ( i = 0; i < (int)submodel_list.size(); ++i ) {
4560  model_num = submodel_list[i];
4561 
4562  if (pm->submodel[model_num].is_thruster) {
4563  continue;
4564  }
4565 
4566  interp_copy_index_buffer(&pm->submodel[model_num].buffer, buffer, index_counts);
4567  }
4568 
4569  // check which buffers need to have the > USHORT flag
4570  for ( i = 0; i < (int)buffer->tex_buf.size(); ++i ) {
4571  if ( buffer->tex_buf[i].i_last >= USHRT_MAX ) {
4572  buffer->tex_buf[i].flags |= VB_FLAG_LARGE_INDEX;
4573  }
4574  }
4575 }
4576 
4578 {
4579  SCP_vector<int> submodel_list;
4580 
4581  submodel_list.clear();
4582 
4583  model_get_submodel_tree_list(submodel_list, pm, pm->detail[detail_num]);
4584 
4585  if ( submodel_list.size() < 1 ) {
4586  return;
4587  }
4588 
4589  interp_fill_detail_index_buffer(submodel_list, pm, &pm->detail_buffers[detail_num]);
4590  //interp_fill_detail_index_buffer(submodel_list, pm, &pm->trans_buff[detail_num]);
4591 
4592  // check if anything was even put into this buffer
4593  if ( pm->detail_buffers[detail_num].tex_buf.size() < 1 ) {
4594  return;
4595  }
4596 
4597  gr_config_buffer(pm->vertex_buffer_id, &pm->detail_buffers[detail_num], true);
4598  //gr_config_buffer(pm->vertex_buffer_id, &pm->trans_buff[detail_num], true);
4599 }
4600 
4602 {
4603  const int NUM_VERTS_PER_TRI = 3;
4604 
4605  bsp_info *sub_model = &pm->submodel[mn];
4606 
4607  vertex_buffer *trans_buffer = &sub_model->trans_buffer;
4608 
4609  trans_buffer->model_list = new(std::nothrow) poly_list;
4610  trans_buffer->vertex_offset = pm->submodel[mn].buffer.vertex_offset;
4611  trans_buffer->stride = pm->submodel[mn].buffer.stride;
4612  trans_buffer->flags = pm->submodel[mn].buffer.flags;
4613 
4614  poly_list *model_list = pm->submodel[mn].buffer.model_list;
4615 
4616  // bail out if this buffer is empty
4617  if ( model_list == NULL || model_list->n_verts < 1 ) {
4618  return;
4619  }
4620 
4621  SCP_vector<buffer_data> &tex_buffers = pm->submodel[mn].buffer.tex_buf;
4622  uint current_tri[NUM_VERTS_PER_TRI];
4623  bool transparent_tri = false;
4624  int num_tris = 0;
4625 
4626  for ( int i = 0; i < (int)tex_buffers.size(); ++i ) {
4627  buffer_data *tex_buf = &tex_buffers[i];
4628 
4629  if ( tex_buf->n_verts < 1 ) {
4630  continue;
4631  }
4632 
4633  const uint *indices = tex_buf->get_index();
4634 
4635  texture_map *tmap = &pm->maps[tex_buf->texture];
4636 
4637  // skip if this is already designated to be a transparent pass by the modeller
4638 // if ( tmap->is_transparent ) {
4639 // continue;
4640 // }
4641 
4642  int bitmap_handle = tmap->textures[TM_BASE_TYPE].GetTexture();
4643 
4644  if ( bitmap_handle < 0 || !bm_has_alpha_channel(bitmap_handle) ) {
4645  continue;
4646  }
4647 
4648  bitmap_lookup texture_lookup(bitmap_handle);
4649 
4650  if ( !texture_lookup.valid() ) {
4651  continue;
4652  }
4653 
4654  SCP_vector<int> transparent_indices;
4655 
4656  transparent_tri = false;
4657  num_tris = 0;
4658 
4659  for ( int j = 0; j < tex_buf->n_verts; ++j ) {
4660  uint index = indices[j];
4661 
4662  // need the uv coords of the vert at this index
4663  float u = model_list->vert[index].texture_position.u;
4664  float v = model_list->vert[index].texture_position.v;
4665 
4666  if ( texture_lookup.get_channel_alpha(u, v) < 0.95f) {
4667  transparent_tri = true;
4668  }
4669 
4670  current_tri[num_tris] = index;
4671  num_tris++;
4672 
4673  if ( num_tris == NUM_VERTS_PER_TRI ) {
4674  if ( transparent_tri ) {
4675  // we have a triangle and it's transparent.
4676  // shove index into the transparency buffer
4677  transparent_indices.push_back(current_tri[0]);
4678  transparent_indices.push_back(current_tri[1]);
4679  transparent_indices.push_back(current_tri[2]);
4680  }
4681 
4682  transparent_tri = false;
4683  num_tris = 0;
4684  }
4685  }
4686 
4687  if ( transparent_indices.empty() ) {
4688  continue;
4689  }
4690 
4691  pm->flags |= PM_FLAG_TRANS_BUFFER;
4692  trans_buffer->flags |= VB_FLAG_TRANS;
4693 
4694  trans_buffer->tex_buf.push_back ( buffer_data ( transparent_indices.size() ) );
4695 
4696  buffer_data &new_buff = trans_buffer->tex_buf.back();
4697  new_buff.texture = tex_buf->texture;
4698 
4699  for ( int j = 0; j < (int)transparent_indices.size(); ++j ) {
4700  new_buff.assign(j, transparent_indices[j]);
4701  }
4702  }
4703 
4704  if ( trans_buffer->flags & VB_FLAG_TRANS ) {
4705  gr_config_buffer(pm->vertex_buffer_id, trans_buffer, true);
4706  }
4707 }
4708 
4709 void model_render_children_buffers_DEPRECATED(polymodel *pm, int mn, int detail_level, int render)
4710 {
4711  int i;
4712 
4713  if ( (mn < 0) || (mn >= pm->n_models) ) {
4714  Int3();
4715  return;
4716  }
4717 
4718  bsp_info *model = &pm->submodel[mn];
4719 
4720  if (model->blown_off)
4721  return;
4722 
4723  uint fl = Interp_flags;
4724 
4725  if (model->is_thruster) {
4726  if ( !(Interp_flags & MR_SHOW_THRUSTERS) )
4727  return;
4728 
4729  Interp_flags |= MR_NO_LIGHTING;
4731  gr_set_lighting(false, false);
4732  } else {
4734  }
4735 
4736  // if using detail boxes or spheres, check that we are valid for the range
4737  if ( !(Interp_flags & MR_FULL_DETAIL) && model->use_render_box ) {
4738  vec3d box_min, box_max, offset;
4739 
4740  if (model->use_render_box_offset) {
4741  model_find_submodel_offset(&offset, pm->id, mn);
4742  vm_vec_sub(&offset, &vmd_zero_vector, &offset);
4743  vm_vec_add2(&offset, &model->render_box_offset);
4744  } else {
4745  offset = vmd_zero_vector;
4746  }
4747 
4748  vm_vec_copy_scale(&box_min, &model->render_box_min, Interp_box_scale);
4749  vm_vec_copy_scale(&box_max, &model->render_box_max, Interp_box_scale);
4750 
4751  if ( (-model->use_render_box + in_box(&box_min, &box_max, &offset, &View_position)) )
4752  return;
4753  }
4754  if ( !(Interp_flags & MR_FULL_DETAIL) && model->use_render_sphere ) {
4755  float radius = model->render_sphere_radius * Interp_box_scale;
4756 
4757  // TODO: doesn't consider submodel rotations yet -zookeeper
4758  vec3d offset;
4759  if (model->use_render_sphere_offset) {
4760  model_find_submodel_offset(&offset, pm->id, mn);
4761  vm_vec_sub(&offset, &vmd_zero_vector, &offset);
4762  vm_vec_add2(&offset, &model->render_sphere_offset);
4763  } else {
4764  offset = vmd_zero_vector;
4765  }
4766 
4767  if ( (-model->use_render_sphere + in_sphere(&offset, radius, &View_position)) )
4768  return;
4769  }
4770 
4771  // Get submodel rotation data and use submodel orientation matrix
4772  // to put together a matrix describing the final orientation of
4773  // the submodel relative to its parent
4774  angles ang = model->angs;
4775 
4776  // Add barrel rotation if needed
4777  if (model->gun_rotation) {
4778  if ( pm->gun_submodel_rotation > PI2 ) {
4779  pm->gun_submodel_rotation -= PI2;
4780  } else if ( pm->gun_submodel_rotation < 0.0f ) {
4781  pm->gun_submodel_rotation += PI2;
4782  }
4783 
4784  ang.b += pm->gun_submodel_rotation;
4785  }
4786 
4787  // Compute final submodel orientation by using the orientation matrix
4788  // and the rotation angles.
4789  // By using this kind of computation, the rotational angles can always
4790  // be computed relative to the submodel itself, instead of relative
4791  // to the parent
4792  matrix rotation_matrix = model->orientation;
4793  vm_rotate_matrix_by_angles(&rotation_matrix, &ang);
4794 
4795  matrix inv_orientation;
4796  vm_copy_transpose(&inv_orientation, &model->orientation);
4797 
4798  matrix submodel_matrix;
4799  vm_matrix_x_matrix(&submodel_matrix, &rotation_matrix, &inv_orientation);
4800 
4801  g3_start_instance_matrix(&model->offset, &submodel_matrix, true);
4802 
4803  model_render_buffers_DEPRECATED(pm, mn, render, true);
4804 
4805  if (Interp_flags & MR_DEPRECATED_SHOW_PIVOTS)
4806  model_draw_debug_points( pm, &pm->submodel[mn], Interp_flags );
4807 
4808  if (model->num_arcs)
4809  interp_render_lightning( pm, &pm->submodel[mn]);
4810 
4811  i = model->first_child;
4812 
4813  while (i >= 0) {
4814  if ( !pm->submodel[i].is_thruster ) {
4815  model_render_children_buffers_DEPRECATED( pm, i, detail_level, render );
4816  }
4817 
4818  i = pm->submodel[i].next_sibling;
4819  }
4820 
4821  Interp_flags = fl;
4822 
4825 
4826  if ( !(Interp_flags & MR_NO_LIGHTING) ) {
4827  gr_set_lighting(true, true);
4828  }
4829  }
4830 
4831  g3_done_instance(true);
4832 }
4833 
4834 void model_render_buffers_DEPRECATED(polymodel *pm, int mn, int render, bool is_child)
4835 {
4836  if (pm->vertex_buffer_id < 0)
4837  return;
4838 
4839  if ( (mn < 0) || (mn >= pm->n_models) ) {
4840  Int3();
4841  return;
4842  }
4843 
4844  bsp_info *model = &pm->submodel[mn];
4845 
4846  // if using detail boxes or spheres, check that we are valid for the range
4847  if ( !is_child && !(Interp_flags & MR_FULL_DETAIL) && model->use_render_box ) {
4848  vec3d box_min, box_max, offset;
4849 
4850  if (model->use_render_box_offset) {
4851  model_find_submodel_offset(&offset, pm->id, mn);
4852  vm_vec_sub(&offset, &vmd_zero_vector, &offset);
4853  vm_vec_add2(&offset, &model->render_box_offset);
4854  } else {
4855  offset = vmd_zero_vector;
4856  }
4857 
4858  vm_vec_copy_scale(&box_min, &model->render_box_min, Interp_box_scale);
4859  vm_vec_copy_scale(&box_max, &model->render_box_max, Interp_box_scale);
4860 
4861  if ( (-model->use_render_box + in_box(&box_min, &box_max, &offset, &View_position)) )
4862  return;
4863  }
4864  if ( !is_child && !(Interp_flags & MR_FULL_DETAIL) && model->use_render_sphere ) {
4865  float radius = model->render_sphere_radius * Interp_box_scale;
4866 
4867  // TODO: doesn't consider submodel rotations yet -zookeeper
4868  vec3d offset;
4869  if (model->use_render_sphere_offset) {
4870  model_find_submodel_offset(&offset, pm->id, mn);
4871  vm_vec_sub(&offset, &vmd_zero_vector, &offset);
4872  vm_vec_add2(&offset, &model->render_sphere_offset);
4873  } else {
4874  offset = vmd_zero_vector;
4875  }
4876 
4877  if ( (-model->use_render_sphere + in_sphere(&offset, radius, &View_position)) )
4878  return;
4879  }
4880 
4881  vec3d scale;
4882 
4884  scale.xyz.x = 1.0f;
4885  scale.xyz.y = 1.0f;
4886  scale.xyz.z = Interp_thrust_scale;
4887  } else {
4888  scale.xyz.x = Interp_warp_scale_x;
4889  scale.xyz.y = Interp_warp_scale_y;
4890  scale.xyz.z = Interp_warp_scale_z;
4891  }
4892 
4894 
4895  texture_info tex_replace[TM_NUM_TYPES];
4896 
4897  int no_texturing = (Interp_flags & MR_NO_TEXTURING);
4898 
4899  int forced_texture = -2;
4900  float forced_alpha = 1.0f;
4901  int forced_blend_filter = GR_ALPHABLEND_NONE;
4902 
4903  if ( Interp_forced_bitmap >= 0 ) {
4904  forced_texture = Interp_forced_bitmap;
4905  } else if (Interp_warp_bitmap >= 0) {
4906  forced_texture = Interp_warp_bitmap;
4907  forced_alpha = Interp_warp_alpha;
4908  forced_blend_filter = GR_ALPHABLEND_FILTER;
4909  } else if (Interp_thrust_scale_subobj) {
4910  if ( (Interp_thrust_bitmap >= 0) && (Interp_thrust_scale > 0.0f) ) {
4911  forced_texture = Interp_thrust_bitmap;
4912  } else {
4913  forced_texture = -1;
4914  }
4915 
4916  forced_alpha = 1.2f;
4917  forced_blend_filter = GR_ALPHABLEND_FILTER;
4918  } else if (Interp_flags & MR_ALL_XPARENT) {
4919  forced_alpha = Interp_xparent_alpha;
4920  forced_blend_filter = GR_ALPHABLEND_FILTER;
4921  }
4922 
4924  gr_push_scale_matrix(&scale);
4925  }
4926 
4927  size_t buffer_size = model->buffer.tex_buf.size();
4928 
4929  for (size_t i = 0; i < buffer_size; i++) {
4930  int texture = -1;
4931  int tmap_num = model->buffer.tex_buf[i].texture;
4932  texture_map *tmap = &pm->maps[tmap_num];
4933  int rt_begin_index = tmap_num*TM_NUM_TYPES;
4934  float alpha = 1.0f;
4935  int blend_filter = GR_ALPHABLEND_NONE;
4936 
4937  if (forced_texture != -2) {
4938  texture = forced_texture;
4939  alpha = forced_alpha;
4940  }
4941  else if ( !no_texturing ) {
4942  // pick the texture, animating it if necessary
4943  if ( (Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE] == REPLACE_WITH_INVISIBLE) ) {
4944  // invisible textures aren't rendered, but we still have to skip assigning the underlying model texture
4945  texture = -1;
4946  } else if ( (Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0) ) {
4947  // an underlying texture is replaced with a real new texture
4948  tex_replace[TM_BASE_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE]);
4949  texture = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], Interp_base_frametime);
4950  } else {
4951  // we just use the underlying texture
4952  texture = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], Interp_base_frametime);
4953  }
4954 
4955  if (texture < 0) {
4956  continue;
4957  }
4958 
4959  // doing glow maps?
4960  if ( !(Interp_flags & MR_NO_GLOWMAPS) ) {
4961  texture_info *tglow = &tmap->textures[TM_GLOW_TYPE];
4962 
4963  if ( (Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_GLOW_TYPE] >= 0) ) {
4964  tex_replace[TM_GLOW_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_GLOW_TYPE]);
4965  GLOWMAP = model_interp_get_texture(&tex_replace[TM_GLOW_TYPE], Interp_base_frametime);
4966  } else if (tglow->GetTexture() >= 0) {
4967  // shockwaves are special, their current frame has to come out of the shockwave code to get the timing correct
4968  if ( (Interp_objnum >= 0) && (Objects[Interp_objnum].type == OBJ_SHOCKWAVE) && (tglow->GetNumFrames() > 1) ) {
4969  GLOWMAP = tglow->GetTexture() + shockwave_get_framenum(Objects[Interp_objnum].instance, tglow->GetNumFrames());
4970  } else {
4971  GLOWMAP = model_interp_get_texture(tglow, Interp_base_frametime);
4972  }
4973  }
4974  }
4975 
4976  if ( (Detail.lighting > 2) && (Interp_detail_level < 2) ) {
4977  // likewise, etc.
4978  texture_info *spec_map = &tmap->textures[TM_SPECULAR_TYPE];
4979  texture_info *norm_map = &tmap->textures[TM_NORMAL_TYPE];
4980  texture_info *height_map = &tmap->textures[TM_HEIGHT_TYPE];
4981 
4982  if (Interp_new_replacement_textures != NULL) {
4983  if (Interp_new_replacement_textures[rt_begin_index + TM_SPECULAR_TYPE] >= 0) {
4984  tex_replace[TM_SPECULAR_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_SPECULAR_TYPE]);
4985  spec_map = &tex_replace[TM_SPECULAR_TYPE];
4986  }
4987 
4988  if (Interp_new_replacement_textures[rt_begin_index + TM_NORMAL_TYPE] >= 0) {
4989  tex_replace[TM_NORMAL_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_NORMAL_TYPE]);
4990  norm_map = &tex_replace[TM_NORMAL_TYPE];
4991  }
4992 
4993  if (Interp_new_replacement_textures[rt_begin_index + TM_HEIGHT_TYPE] >= 0) {
4994  tex_replace[TM_HEIGHT_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_HEIGHT_TYPE]);
4995  height_map = &tex_replace[TM_HEIGHT_TYPE];
4996  }
4997  }
4998 
4999  SPECMAP = model_interp_get_texture(spec_map, Interp_base_frametime);
5000  NORMMAP = model_interp_get_texture(norm_map, Interp_base_frametime);
5001  HEIGHTMAP = model_interp_get_texture(height_map, Interp_base_frametime);
5002  }
5003 
5004  texture_info *misc_map = &tmap->textures[TM_MISC_TYPE];
5005 
5006  if (Interp_new_replacement_textures != NULL) {
5007  if (Interp_new_replacement_textures[rt_begin_index + TM_MISC_TYPE] >= 0) {
5008  tex_replace[TM_MISC_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_MISC_TYPE]);
5009  misc_map = &tex_replace[TM_MISC_TYPE];
5010  }
5011  }
5012 
5013  MISCMAP = model_interp_get_texture(misc_map, Interp_base_frametime);
5014  } else {
5015  alpha = forced_alpha;
5016 
5017  //Check for invisible or transparent textures so they dont show up in the shadow maps - Valathil
5018  if ( (Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0) ) {
5019  tex_replace[TM_BASE_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE]);
5020  texture = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], Interp_base_frametime);
5021  } else {
5022  texture = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], Interp_base_frametime);
5023  }
5024 
5025  if (texture <= 0) {
5026  continue;
5027  }
5028 
5029  //if(bm_has_alpha_channel(texture))
5030  //continue;
5031  }
5032 
5033  if ( (texture == -1) && !no_texturing ) {
5034  continue;
5035  }
5036 
5037  // trying to get transperent textures-Bobboau
5038  if (tmap->is_transparent) {
5039  // for special shockwave/warpmap usage
5040  alpha = (Interp_warp_alpha != -1.0f) ? Interp_warp_alpha : 0.8f;
5041  blend_filter = GR_ALPHABLEND_FILTER;
5042 
5043  }
5044 
5045  if (forced_blend_filter != GR_ALPHABLEND_NONE) {
5046  blend_filter = forced_blend_filter;
5047  }
5048 
5049  extern bool object_had_transparency;
5050  if (blend_filter != GR_ALPHABLEND_NONE) {
5051  if(render & MODEL_RENDER_TRANS)
5052  {
5054  gr_set_bitmap(texture, blend_filter, GR_BITBLT_MODE_NORMAL, alpha);
5055  gr_render_buffer(0, &model->buffer, i, Interp_tmap_flags);
5057  }
5058  else
5059  {
5060  object_had_transparency = true;
5061  }
5062  }
5063  else
5064  {
5065  if(render & MODEL_RENDER_OPAQUE)
5066  {
5067  gr_set_bitmap(texture, blend_filter, GR_BITBLT_MODE_NORMAL, alpha);
5068  gr_render_buffer(0, &model->buffer, i, Interp_tmap_flags);
5069  }
5070  }
5071 
5072  GLOWMAP = -1;
5073  SPECMAP = -1;
5074  NORMMAP = -1;
5075  HEIGHTMAP = -1;
5076  MISCMAP = -1;
5077  }
5078 
5080 }
5081 
5082 // returns 1 if the thruster should be drawn
5083 // 0 if it shouldn't
5084 int model_should_render_engine_glow(int objnum, int bank_obj)
5085 {
5086  if ((bank_obj <= -1) || (objnum <= -1))
5087  return 1;
5088 
5089  object *obj = &Objects[objnum];
5090 
5091  if (obj->type == OBJ_SHIP) {
5092  ship_subsys *ssp;
5093  ship *shipp = &Ships[obj->instance];
5094  ship_info *sip = &Ship_info[shipp->ship_info_index];
5095 
5096  Assert( bank_obj < sip->n_subsystems );
5097 
5098  char subname[MAX_NAME_LEN];
5099  // shipp->subsystems isn't always valid here so don't use it
5100  strcpy_s(subname, sip->subsystems[bank_obj].subobj_name);
5101 
5102  ssp = GET_FIRST(&shipp->subsys_list);
5103  while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
5104  if ( !strcmp(subname, ssp->system_info->subobj_name) ) {
5105  // this subsystem has 0 or less hits, ie. it's destroyed
5106  if ( ssp->current_hits <= 0 )
5107  return 0;
5108 
5109  // see if the subsystem is disrupted, in which case it should be inoperable
5110  if ( ship_subsys_disrupted(ssp) )
5111  return 0;
5112 
5113  return 1;
5114  }
5115  ssp = GET_NEXT( ssp );
5116  }
5117 
5118  } else if (obj->type == OBJ_WEAPON) {
5119  // for weapons, if needed in the future
5120  }
5121 
5122  // default to render glow
5123  return 1;
5124 }
5125 
5126 // Goober5000
5127 // uses same algorithms as in ship_do_thruster_frame
5128 int model_interp_get_texture(texture_info *tinfo, fix base_frametime)
5129 {
5130  int texture, frame, num_frames;
5131  float cur_time, total_time;
5132 
5133  // get texture
5134  num_frames = tinfo->GetNumFrames();
5135  texture = tinfo->GetTexture();
5136  total_time = tinfo->GetTotalTime();
5137 
5138  // maybe animate it
5139  if (texture >= 0 && num_frames > 1)
5140  {
5141  // sanity check total_time first thing
5142  Assert(total_time > 0.0f);
5143 
5144  cur_time = f2fl((game_get_overall_frametime() - base_frametime) % fl2f(total_time));
5145 
5146  // get animation frame
5147  frame = fl2i((cur_time * num_frames) / total_time);
5148  CLAMP(frame, 0, num_frames - 1);
5149 
5150  // advance to the correct frame
5151  texture += frame;
5152  }
5153 
5154  // done
5155  return texture;
5156 }
5157 
5158 void model_set_warp_globals(float scale_x, float scale_y, float scale_z, int bitmap_id, float alpha)
5159 {
5160  Interp_warp_scale_x = scale_x;
5161  Interp_warp_scale_y = scale_y;
5162  Interp_warp_scale_z = scale_z;
5163 
5164  Interp_warp_bitmap = bitmap_id;
5165  Interp_warp_alpha = alpha;
5166 }
5167 
5169 {
5170  dest->base.r = a->base.r * (1.0f - mix_factor) + b->base.r * mix_factor;
5171  dest->base.g = a->base.g * (1.0f - mix_factor) + b->base.g * mix_factor;
5172  dest->base.b = a->base.b * (1.0f - mix_factor) + b->base.b * mix_factor;
5173 
5174  dest->stripe.r = a->stripe.r * (1.0f - mix_factor) + b->stripe.r * mix_factor;
5175  dest->stripe.g = a->stripe.g * (1.0f - mix_factor) + b->stripe.g * mix_factor;
5176  dest->stripe.b = a->stripe.b * (1.0f - mix_factor) + b->stripe.b * mix_factor;
5177 }
5178 
5179 bool model_get_team_color( team_color *clr, const SCP_string &team, const SCP_string &secondaryteam, fix timestamp, int fadetime )
5180 {
5181  Assert(clr != NULL);
5182 
5183  if ( !stricmp(secondaryteam.c_str(), "none") ) {
5184  if (Team_Colors.find(team) != Team_Colors.end()) {
5185  *clr =