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"
17 #include "gamesequence/gamesequence.h"
18 #include "gamesnd/gamesnd.h"
19 #include "globalincs/alphacolors.h"
20 #include "globalincs/linklist.h"
21 #include "graphics/2d.h"
22 #include "graphics/util/GPUMemoryHeap.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 "tracing/Monitor.h"
37 #include "tracing/tracing.h"
38 #include "utils/Random.h"
39 #include "weapon/shockwave.h"
40
41 #include <climits>
42
43
44 float model_radius = 0;
45
46 // Some debug variables used externally for displaying stats
47 #ifndef NDEBUG
48 int modelstats_num_polys = 0;
49 int modelstats_num_polys_drawn = 0;
50 int modelstats_num_verts = 0;
51 int modelstats_num_sortnorms = 0;
52 int modelstats_num_boxes = 0;
53 #endif
54
55 extern fix game_get_overall_frametime(); // for texture animation
56
57 typedef struct model_light {
58 ubyte r, g, b;
59 ubyte spec_r, spec_g, spec_b;
60 } model_light;
61
62 // a lighting object
63 typedef struct model_light_object {
64 model_light *lights;
65
66 int objnum;
67 int skip;
68 int skip_max;
69 } model_light_object;
70
71 struct bsp_vertex
72 {
73 vec3d position;
74 vec3d normal;
75 uv_pair tex_coord;
76 ubyte r, g, b, a;
77 };
78
79 struct bsp_polygon
80 {
81 uint Start_index;
82 uint Num_verts;
83
84 int texture;
85 };
86
87 class bsp_polygon_data
88 {
89 SCP_vector<vec3d> Vertex_list;
90 SCP_vector<vec3d> Normal_list;
91
92 SCP_vector<bsp_vertex> Polygon_vertices;
93 SCP_vector<bsp_polygon> Polygons;
94
95 ubyte* Lights;
96
97 int Num_polies[MAX_MODEL_TEXTURES];
98 int Num_verts[MAX_MODEL_TEXTURES];
99
100 int Num_flat_polies;
101 int Num_flat_verts;
102
103 void process_bsp(int offset, ubyte* bsp_data);
104 void process_defpoints(int off, ubyte* bsp_data);
105 void process_sortnorm(int offset, ubyte* bsp_data);
106 void process_tmap(int offset, ubyte* bsp_data);
107 void process_flat(int offset, ubyte* bsp_data);
108 public:
109 bsp_polygon_data(ubyte* bsp_data);
110
111 int get_num_triangles(int texture);
112 int get_num_lines(int texture);
113
114 void generate_triangles(int texture, vertex *vert_ptr, vec3d* norm_ptr);
115 void generate_lines(int texture, vertex *vert_ptr);
116 };
117
118 /**
119 * @brief Vertex structure for passing data to the GPU
120 */
121 struct interp_vertex {
122 uv_pair uv;
123 vec3d normal;
124 vec4 tangent;
125 float modelId;
126 vec3d pos;
127 };
128
129 // -----------------------
130 // Local variables
131 //
132
133 static int Num_interp_verts_allocated = 0;
134 vec3d **Interp_verts = NULL;
135 static vertex *Interp_points = NULL;
136 static vertex *Interp_splode_points = NULL;
137 vec3d *Interp_splode_verts = NULL;
138 static int Interp_num_verts = 0;
139
140 static float Interp_box_scale = 1.0f; // this is used to scale both detail boxes and spheres
141
142 // -------------------------------------------------------------------
143 // lighting save stuff
144 //
145
146 model_light_object Interp_lighting_temp;
147 model_light_object *Interp_lighting = &Interp_lighting_temp;
148 int Interp_use_saved_lighting = 0;
149 int Interp_saved_lighting_full = 0;
150 //
151 // lighting save stuff
152 // -------------------------------------------------------------------
153
154
155 static int Num_interp_norms_allocated = 0;
156 static vec3d **Interp_norms = NULL;
157 static ubyte *Interp_light_applied = NULL;
158 static int Interp_num_norms = 0;
159 static ubyte *Interp_lights;
160
161 // Stuff to control rendering parameters
162 static color Interp_outline_color;
163 static int Interp_detail_level_locked = -1;
164 static uint Interp_flags = 0;
165
166 // If non-zero, then the subobject gets scaled by Interp_thrust_scale.
167 int Interp_thrust_scale_subobj = 0;
168 float Interp_thrust_scale = 0.1f;
169 static float Interp_thrust_scale_x = 0.0f;//added -bobboau
170 static float Interp_thrust_scale_y = 0.0f;//added -bobboau
171
172 static int Interp_thrust_bitmap = -1;
173 static int Interp_thrust_glow_bitmap = -1;
174 static float Interp_thrust_glow_noise = 1.0f;
175 static bool Interp_afterburner = false;
176
177 // Bobboau's thruster stuff
178 static int Interp_secondary_thrust_glow_bitmap = -1;
179 static int Interp_tertiary_thrust_glow_bitmap = -1;
180 static int Interp_distortion_thrust_bitmap = -1;
181 static float Interp_thrust_glow_rad_factor = 1.0f;
182 static float Interp_secondary_thrust_glow_rad_factor = 1.0f;
183 static float Interp_tertiary_thrust_glow_rad_factor = 1.0f;
184 static float Interp_distortion_thrust_rad_factor = 1.0f;
185 static float Interp_distortion_thrust_length_factor = 1.0f;
186 static float Interp_thrust_glow_len_factor = 1.0f;
187 static vec3d Interp_thrust_rotvel = ZERO_VECTOR;
188 static bool Interp_draw_distortion = true;
189
190 // Bobboau's warp stuff
191 static float Interp_warp_scale_x = 1.0f;
192 static float Interp_warp_scale_y = 1.0f;
193 static float Interp_warp_scale_z = 1.0f;
194
195 // if != -1, use this bitmap when rendering ship insignias
196 static int Interp_insignia_bitmap = -1;
197
198 // if != -1, use this bitmap when rendering with a forced texture
199 static int Interp_forced_bitmap = -1;
200
201 // our current level of detail (LOD)
202 int Interp_detail_level = 0;
203
204 // forward references
205 int model_should_render_engine_glow(int objnum, int bank_obj);
206 int model_interp_get_texture(texture_info *tinfo, fix base_frametime);
207
model_deallocate_interp_data()208 void model_deallocate_interp_data()
209 {
210 if (Interp_verts != NULL)
211 vm_free(Interp_verts);
212
213 if (Interp_points != NULL)
214 vm_free(Interp_points);
215
216 if (Interp_splode_points != NULL)
217 vm_free(Interp_splode_points);
218
219 if (Interp_splode_verts != NULL)
220 vm_free(Interp_splode_verts);
221
222 if (Interp_norms != NULL)
223 vm_free(Interp_norms);
224
225 if (Interp_light_applied != NULL)
226 vm_free(Interp_light_applied);
227
228 if (Interp_lighting_temp.lights != NULL)
229 vm_free(Interp_lighting_temp.lights);
230
231 Num_interp_verts_allocated = 0;
232 Num_interp_norms_allocated = 0;
233 }
234
235 extern void model_collide_allocate_point_list(int n_points);
236 extern void model_collide_free_point_list();
237
model_allocate_interp_data(int n_verts,int n_norms)238 void model_allocate_interp_data(int n_verts, int n_norms)
239 {
240 static ubyte dealloc = 0;
241
242 if (!dealloc) {
243 atexit(model_deallocate_interp_data);
244 atexit(model_collide_free_point_list);
245 dealloc = 1;
246 }
247
248 Assert( (n_verts >= 0) && (n_norms >= 0) );
249 Assert( (n_verts || Num_interp_verts_allocated) && (n_norms || Num_interp_norms_allocated) );
250
251 if (n_verts > Num_interp_verts_allocated) {
252 if (Interp_verts != NULL) {
253 vm_free(Interp_verts);
254 Interp_verts = NULL;
255 }
256 // Interp_verts can't be reliably realloc'd so free and malloc it on each resize (no data needs to be carried over)
257 Interp_verts = (vec3d**) vm_malloc( n_verts * sizeof(vec3d *) );
258
259 Interp_points = (vertex*) vm_realloc( Interp_points, n_verts * sizeof(vertex) );
260 Interp_splode_points = (vertex*) vm_realloc( Interp_splode_points, n_verts * sizeof(vertex) );
261 Interp_splode_verts = (vec3d*) vm_realloc( Interp_splode_verts, n_verts * sizeof(vec3d) );
262
263 Num_interp_verts_allocated = n_verts;
264
265 // model collide needs a similar size to resize it based on this new value
266 model_collide_allocate_point_list( n_verts );
267 }
268
269 if (n_norms > Num_interp_norms_allocated) {
270 if (Interp_norms != NULL) {
271 vm_free(Interp_norms);
272 Interp_norms = NULL;
273 }
274 // Interp_norms can't be reliably realloc'd so free and malloc it on each resize (no data needs to be carried over)
275 Interp_norms = (vec3d**) vm_malloc( n_norms * sizeof(vec3d *) );
276
277 // 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
278 if (Interp_light_applied != NULL) {
279 vm_free(Interp_light_applied);
280 Interp_light_applied = NULL;
281 }
282
283 if (Interp_lighting_temp.lights != NULL) {
284 vm_free(Interp_lighting_temp.lights);
285 Interp_lighting_temp.lights = NULL;
286 }
287
288 Interp_light_applied = (ubyte*) vm_malloc( n_norms * sizeof(ubyte) );
289 Interp_lighting_temp.lights = (model_light*) vm_malloc( n_norms * sizeof(model_light) );
290
291 memset( Interp_light_applied, 0, n_norms * sizeof(ubyte) );
292 memset( Interp_lighting_temp.lights, 0, n_norms * sizeof(model_light) );
293
294 Num_interp_norms_allocated = n_norms;
295 }
296
297 Interp_num_verts = n_verts;
298 Interp_num_norms = n_norms;
299
300 // check that everything is still usable (works in release and debug builds)
301 Verify( Interp_points != NULL );
302 Verify( Interp_splode_points != NULL );
303 Verify( Interp_verts != NULL );
304 Verify( Interp_splode_verts != NULL );
305 Verify( Interp_norms != NULL );
306 Verify( Interp_light_applied != NULL );
307 }
308
interp_clear_instance()309 void interp_clear_instance()
310 {
311 Interp_thrust_scale = 0.1f;
312 Interp_thrust_scale_x = 0.0f;//added-Bobboau
313 Interp_thrust_scale_y = 0.0f;//added-Bobboau
314 Interp_thrust_bitmap = -1;
315 Interp_thrust_glow_bitmap = -1;
316 Interp_thrust_glow_noise = 1.0f;
317 Interp_insignia_bitmap = -1;
318 Interp_afterburner = false;
319
320 // Bobboau's thruster stuff
321 {
322 Interp_thrust_glow_rad_factor = 1.0f;
323
324 Interp_secondary_thrust_glow_bitmap = -1;
325 Interp_secondary_thrust_glow_rad_factor = 1.0f;
326
327 Interp_tertiary_thrust_glow_bitmap = -1;
328 Interp_tertiary_thrust_glow_rad_factor = 1.0f;
329
330 Interp_thrust_glow_len_factor = 1.0f;
331
332 vm_vec_zero(&Interp_thrust_rotvel);
333 }
334
335 Interp_box_scale = 1.0f;
336
337 Interp_detail_level_locked = -1;
338
339 Interp_forced_bitmap = -1;
340 }
341
342 /**
343 * Scales the engines thrusters by this much
344 */
model_set_thrust(int,mst_info * mst)345 void model_set_thrust(int /*model_num*/, mst_info *mst)
346 {
347 if (mst == NULL) {
348 Int3();
349 return;
350 }
351
352 Interp_thrust_scale = mst->length.xyz.z;
353 Interp_thrust_scale_x = mst->length.xyz.x;
354 Interp_thrust_scale_y = mst->length.xyz.y;
355
356 CLAMP(Interp_thrust_scale, 0.1f, 1.0f);
357
358 Interp_thrust_bitmap = mst->primary_bitmap;
359 Interp_thrust_glow_bitmap = mst->primary_glow_bitmap;
360 Interp_secondary_thrust_glow_bitmap = mst->secondary_glow_bitmap;
361 Interp_tertiary_thrust_glow_bitmap = mst->tertiary_glow_bitmap;
362 Interp_distortion_thrust_bitmap = mst->distortion_bitmap;
363
364 Interp_thrust_glow_noise = mst->glow_noise;
365 Interp_afterburner = mst->use_ab;
366
367 Interp_thrust_rotvel = mst->rotvel;
368
369 Interp_thrust_glow_rad_factor = mst->glow_rad_factor;
370 Interp_secondary_thrust_glow_rad_factor = mst->secondary_glow_rad_factor;
371 Interp_tertiary_thrust_glow_rad_factor = mst->tertiary_glow_rad_factor;
372 Interp_thrust_glow_len_factor = mst->glow_length_factor;
373 Interp_distortion_thrust_rad_factor = mst->distortion_rad_factor;
374 Interp_distortion_thrust_length_factor = mst->distortion_length_factor;
375
376 Interp_draw_distortion = mst->draw_distortion;
377 }
378
379 bool splodeing = false;
380 int splodeingtexture = -1;
381 float splode_level = 0.0f;
382
383 float GEOMETRY_NOISE = 0.0f;
384
385 // Point list
386 // +0 int id
387 // +4 int size
388 // +8 int n_verts
389 // +12 int n_norms
390 // +16 int offset from start of chunk to vertex data
391 // +20 n_verts*char norm_counts
392 // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
model_interp_splode_defpoints(ubyte * p,polymodel *,bsp_info *,float dist)393 void model_interp_splode_defpoints(ubyte * p, polymodel * /*pm*/, bsp_info * /*sm*/, float dist)
394 {
395 if(dist==0.0f)return;
396
397 if(dist<0.0f)dist*=-1.0f;
398
399 int n;
400 int nverts = w(p+8);
401 int offset = w(p+16);
402 int nnorms = 0;
403
404 ubyte * normcount = p+20;
405 vertex *dest = Interp_splode_points;
406 vec3d *src = vp(p+offset);
407
408 for (n = 0; n < nverts; n++) {
409 nnorms += normcount[n];
410 }
411
412 model_allocate_interp_data(nverts, nnorms);
413
414 vec3d dir;
415
416 for (n=0; n<nverts; n++ ) {
417 Interp_splode_verts[n] = *src;
418
419 src++;
420
421 vm_vec_avg_n(&dir, normcount[n], src);
422 vm_vec_normalize(&dir);
423
424 for(int i=0; i<normcount[n]; i++)src++;
425
426 vm_vec_scale_add2(&Interp_splode_verts[n], &dir, dist);
427
428 g3_rotate_vertex(dest, &Interp_splode_verts[n]);
429
430 dest++;
431
432 }
433
434 }
435
436 // Point list
437 // +0 int id
438 // +4 int size
439 // +8 int n_verts
440 // +12 int n_norms
441 // +16 int offset from start of chunk to vertex data
442 // +20 n_verts*char norm_counts
443 // +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
model_interp_defpoints(ubyte * p,polymodel * pm,bsp_info * sm)444 void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm)
445 {
446 if(splodeing)model_interp_splode_defpoints(p, pm, sm, splode_level*model_radius);
447
448 int i, n;
449 int nverts = w(p+8);
450 int offset = w(p+16);
451 int next_norm = 0;
452 int nnorms = 0;
453
454 ubyte * normcount = p+20;
455 vertex *dest = NULL;
456 vec3d *src = vp(p+offset);
457
458 // Get pointer to lights
459 Interp_lights = p+20+nverts;
460
461 for (i = 0; i < nverts; i++) {
462 nnorms += normcount[i];
463 }
464
465 // allocate new Interp data if size is greater than we already have ready to use
466 model_allocate_interp_data(nverts, nnorms);
467
468 dest = Interp_points;
469
470 Assert( dest != NULL );
471
472 #ifndef NDEBUG
473 modelstats_num_verts += nverts;
474 #endif
475
476
477 if (Interp_thrust_scale_subobj) {
478
479 // Only scale vertices that aren't on the "base" of
480 // the effect. Base is something Adam decided to be
481 // anything under 1.5 meters, hence the 1.5f.
482 float min_thruster_dist = -1.5f;
483
484 if ( Interp_flags & MR_IS_MISSILE ) {
485 min_thruster_dist = 0.5f;
486 }
487
488 for (n=0; n<nverts; n++ ) {
489 vec3d tmp;
490
491 Interp_verts[n] = src;
492
493 // Only scale vertices that aren't on the "base" of
494 // the effect. Base is something Adam decided to be
495 // anything under 1.5 meters, hence the 1.5f.
496 if ( src->xyz.z < min_thruster_dist ) {
497 tmp.xyz.x = src->xyz.x * 1.0f;
498 tmp.xyz.y = src->xyz.y * 1.0f;
499 tmp.xyz.z = src->xyz.z * Interp_thrust_scale;
500 } else {
501 tmp = *src;
502 }
503
504 g3_rotate_vertex(dest,&tmp);
505
506 src++; // move to normal
507
508 for (i=0; i<normcount[n]; i++ ) {
509 Interp_light_applied[next_norm] = 0;
510 Interp_norms[next_norm] = src;
511
512 next_norm++;
513 src++;
514 }
515 dest++;
516 }
517 } else if ( (Interp_warp_scale_x != 1.0f) || (Interp_warp_scale_y != 1.0f) || (Interp_warp_scale_z != 1.0f)) {
518 for (n=0; n<nverts; n++ ) {
519 vec3d tmp;
520
521 Interp_verts[n] = src;
522
523 tmp.xyz.x = (src->xyz.x) * Interp_warp_scale_x;
524 tmp.xyz.y = (src->xyz.y) * Interp_warp_scale_y;
525 tmp.xyz.z = (src->xyz.z) * Interp_warp_scale_z;
526
527 g3_rotate_vertex(dest,&tmp);
528
529 src++; // move to normal
530
531 for (i=0; i<normcount[n]; i++ ) {
532 Interp_light_applied[next_norm] = 0;
533 Interp_norms[next_norm] = src;
534
535 next_norm++;
536 src++;
537 }
538 dest++;
539 }
540 } else {
541 vec3d point;
542
543 for (n=0; n<nverts; n++ ) {
544
545 if(GEOMETRY_NOISE!=0.0f){
546 GEOMETRY_NOISE = model_radius / 50;
547
548 Interp_verts[n] = src;
549 point.xyz.x = src->xyz.x + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
550 point.xyz.y = src->xyz.y + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
551 point.xyz.z = src->xyz.z + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
552
553 g3_rotate_vertex(dest, &point);
554 }else{
555 Interp_verts[n] = src;
556 g3_rotate_vertex(dest, src);
557 }
558
559 src++; // move to normal
560
561 for (i=0; i<normcount[n]; i++ ) {
562 Interp_light_applied[next_norm] = 0;
563 Interp_norms[next_norm] = src;
564
565 next_norm++;
566 src++;
567 }
568 dest++;
569 }
570 }
571
572 Interp_num_norms = next_norm;
573 }
574
model_interp_edge_alpha(ubyte * param_r,ubyte * param_g,ubyte * param_b,vec3d * pnt,vec3d * norm,float alpha,bool invert=false)575 void model_interp_edge_alpha( ubyte *param_r, ubyte *param_g, ubyte *param_b, vec3d *pnt, vec3d *norm, float alpha, bool invert = false)
576 {
577 vec3d r;
578
579 vm_vec_sub(&r, &View_position, pnt);
580 vm_vec_normalize(&r);
581
582 float d = vm_vec_dot(&r, norm);
583
584 if (d < 0.0f)
585 d = -d;
586
587 if (invert)
588 *param_r = *param_g = *param_b = ubyte( fl2i((1.0f - d) * 254.0f * alpha));
589 else
590 *param_r = *param_g = *param_b = ubyte( fl2i(d * 254.0f * alpha) );
591 }
592
593 int Interp_subspace = 0;
594 float Interp_subspace_offset_u = 0.0f;
595 float Interp_subspace_offset_v = 0.0f;
596 ubyte Interp_subspace_r = 255;
597 ubyte Interp_subspace_g = 255;
598 ubyte Interp_subspace_b = 255;
599
model_draw_debug_points(polymodel * pm,bsp_info * submodel,uint flags)600 void model_draw_debug_points( polymodel *pm, bsp_info * submodel, uint flags )
601 {
602 if ( flags & MR_SHOW_OUTLINE_PRESET ) {
603 return;
604 }
605
606 // Draw a red pivot point
607 gr_set_color(128,0,0);
608 g3_draw_sphere_ez(&vmd_zero_vector, 2.0f );
609
610 // Draw a green center of mass when drawing the hull
611 if ( submodel && (submodel->parent==-1) ) {
612 gr_set_color(0,128,0);
613 g3_draw_sphere_ez( &pm->center_of_mass, 1.0f );
614 }
615
616 if ( submodel ) {
617 // Draw a blue center point
618 gr_set_color(0,0,128);
619 g3_draw_sphere_ez( &submodel->geometric_center, 0.9f );
620 }
621
622 // Draw the bounding box
623 int i;
624 vertex pts[8];
625
626 if ( submodel ) {
627 for (i=0; i<8; i++ ) {
628 g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] );
629 }
630 gr_set_color(128,128,128);
631 g3_draw_line( &pts[0], &pts[1] );
632 g3_draw_line( &pts[1], &pts[2] );
633 g3_draw_line( &pts[2], &pts[3] );
634 g3_draw_line( &pts[3], &pts[0] );
635
636 g3_draw_line( &pts[4], &pts[5] );
637 g3_draw_line( &pts[5], &pts[6] );
638 g3_draw_line( &pts[6], &pts[7] );
639 g3_draw_line( &pts[7], &pts[4] );
640
641 g3_draw_line( &pts[0], &pts[4] );
642 g3_draw_line( &pts[1], &pts[5] );
643 g3_draw_line( &pts[2], &pts[6] );
644 g3_draw_line( &pts[3], &pts[7] );
645 } else {
646 gr_set_color(0,255,0);
647
648 int j;
649 for (j=0; j<8; j++ ) {
650
651 vec3d bounding_box[8]; // caclulated fron min/max
652 model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max);
653
654 for (i=0; i<8; i++ ) {
655 g3_rotate_vertex( &pts[i], &bounding_box[i] );
656 }
657 gr_set_color(128,0,0);
658 g3_draw_line( &pts[0], &pts[1] );
659 g3_draw_line( &pts[1], &pts[2] );
660 g3_draw_line( &pts[2], &pts[3] );
661 g3_draw_line( &pts[3], &pts[0] );
662
663 g3_draw_line( &pts[4], &pts[5] );
664 g3_draw_line( &pts[5], &pts[6] );
665 g3_draw_line( &pts[6], &pts[7] );
666 g3_draw_line( &pts[7], &pts[4] );
667
668 g3_draw_line( &pts[0], &pts[4] );
669 g3_draw_line( &pts[1], &pts[5] );
670 g3_draw_line( &pts[2], &pts[6] );
671 g3_draw_line( &pts[3], &pts[7] );
672 }
673 }
674 }
675
676 /**
677 * Debug code to show all the paths of a model
678 */
model_draw_paths_htl(int model_num,uint flags)679 void model_draw_paths_htl( int model_num, uint flags )
680 {
681 int i,j;
682 vec3d pnt;
683 vec3d prev_pnt;
684 polymodel * pm;
685
686 if ( flags & MR_SHOW_OUTLINE_PRESET ) {
687 return;
688 }
689
690 pm = model_get(model_num);
691
692 if (pm->n_paths<1){
693 return;
694 }
695
696 int cull = gr_set_cull(0);
697 for (i=0; i<pm->n_paths; i++ ) {
698 for (j=0; j<pm->paths[i].nverts; j++ )
699 {
700 // Rotate point into world coordinates
701 pnt = pm->paths[i].verts[j].pos;
702
703 // Pnt is now the x,y,z world coordinates of this vert.
704 // For this example, I am just drawing a sphere at that
705 // point.
706
707 vertex tmp;
708 g3_rotate_vertex(&tmp,&pnt);
709
710 if ( pm->paths[i].verts[j].nturrets > 0 ){
711 gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
712 } else {
713 gr_set_color( 255, 0, 0 );
714 }
715
716 g3_render_sphere(&pnt, 0.5f);
717
718 if (j)
719 {
720 //g3_draw_htl_line(&prev_pnt, &pnt);
721 g3_render_line_3d(true, &prev_pnt, &pnt);
722 }
723
724 prev_pnt = pnt;
725 }
726 }
727
728 gr_set_cull(cull);
729 }
730
731 /**
732 * Docking bay and fighter bay paths
733 */
model_draw_bay_paths_htl(int model_num)734 void model_draw_bay_paths_htl(int model_num)
735 {
736 int idx, s_idx;
737 vec3d v1, v2;
738
739 polymodel *pm = model_get(model_num);
740 if(pm == NULL){
741 return;
742 }
743
744 int cull = gr_set_cull(0);
745 // render docking bay normals
746 gr_set_color(0, 255, 0);
747 for(idx=0; idx<pm->n_docks; idx++){
748 for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
749 v1 = pm->docking_bays[idx].pnt[s_idx];
750 vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
751
752 // draw the point and normal
753 g3_render_sphere(&v1, 2.0);
754 //g3_draw_htl_line(&v1, &v2);
755 g3_render_line_3d(true, &v1, &v2);
756 }
757 }
758
759 // render figher bay paths
760 gr_set_color(0, 255, 255);
761
762 // iterate through the paths that exist in the polymodel, searching for $bayN pathnames
763 for (idx = 0; idx<pm->n_paths; idx++) {
764 if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
765 for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
766 v1 = pm->paths[idx].verts[s_idx].pos;
767 v2 = pm->paths[idx].verts[s_idx+1].pos;
768
769 //g3_draw_htl_line(&v1, &v2);
770 g3_render_line_3d(true, &v1, &v2);
771 }
772 }
773 }
774
775 gr_set_cull(cull);
776 }
777
778 static const int MAX_ARC_SEGMENT_POINTS = 50;
779 int Num_arc_segment_points = 0;
780 vec3d Arc_segment_points[MAX_ARC_SEGMENT_POINTS];
781
interp_render_arc_segment(vec3d * v1,vec3d * v2,int depth)782 void interp_render_arc_segment( vec3d *v1, vec3d *v2, int depth )
783 {
784 float d = vm_vec_dist_quick( v1, v2 );
785 const float scaler = 0.30f;
786
787 if ( (d < scaler) || (depth > 4) ) {
788 // the real limit appears to be 33, so we should never hit this unless the code changes
789 Assert( Num_arc_segment_points < MAX_ARC_SEGMENT_POINTS );
790
791 memcpy( &Arc_segment_points[Num_arc_segment_points++], v2, sizeof(vec3d) );
792 } else {
793 // divide in half
794 vec3d tmp;
795 vm_vec_avg( &tmp, v1, v2 );
796
797 tmp.xyz.x += (frand() - 0.5f) * d * scaler;
798 tmp.xyz.y += (frand() - 0.5f) * d * scaler;
799 tmp.xyz.z += (frand() - 0.5f) * d * scaler;
800
801 // add additional point
802 interp_render_arc_segment( v1, &tmp, depth+1 );
803 interp_render_arc_segment( &tmp, v2, depth+1 );
804 }
805 }
806
807 int Interp_lightning = 1;
DCF_BOOL(Arcs,Interp_lightning)808 DCF_BOOL( Arcs, Interp_lightning )
809
810 // Returns one of the following
811 #define IBOX_ALL_OFF 0
812 #define IBOX_ALL_ON 1
813 #define IBOX_SOME_ON_SOME_OFF 2
814
815 int interp_box_offscreen( vec3d *min, vec3d *max )
816 {
817 if ( keyd_pressed[KEY_LSHIFT] ) {
818 return IBOX_ALL_ON;
819 }
820
821 vec3d v[8];
822 v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z;
823 v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z;
824 v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z;
825 v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z;
826
827 v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z;
828 v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z;
829 v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z;
830 v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z;
831
832 ubyte and_codes = 0xff;
833 ubyte or_codes = 0xff;
834 int i;
835
836 for (i=0; i<8; i++ ) {
837 vertex tmp;
838 ubyte codes=g3_rotate_vertex( &tmp, &v[i] );
839
840 or_codes |= codes;
841 and_codes &= codes;
842 }
843
844 // If and_codes is set this means that all points lie off to the
845 // same side of the screen.
846 if (and_codes) {
847 return IBOX_ALL_OFF; //all points off screen
848 }
849
850 // If this is set it means at least one of the points is offscreen,
851 // but they aren't all off to the same side.
852 if (or_codes) {
853 return IBOX_SOME_ON_SOME_OFF;
854 }
855
856 // They are all onscreen.
857 return IBOX_ALL_ON;
858 }
859
model_render_shields(polymodel * pm,uint flags)860 void model_render_shields( polymodel * pm, uint flags )
861 {
862 int i, j;
863 shield_tri *tri;
864 vertex pnt0, prev_pnt, tmp = vertex();
865
866 if ( flags & MR_SHOW_OUTLINE_PRESET ) {
867 return;
868 }
869
870 gr_set_color(0, 0, 200 );
871
872 // Scan all the triangles in the mesh.
873 for (i=0; i<pm->shield.ntris; i++ ) {
874
875 tri = &pm->shield.tris[i];
876
877 if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) {
878
879 // Process the vertices.
880 // Note this rotates each vertex each time it's needed, very dumb.
881 for (j=0; j<3; j++ ) {
882
883 g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos );
884
885 if (j)
886 g3_draw_line(&prev_pnt, &tmp);
887 else
888 pnt0 = tmp;
889 prev_pnt = tmp;
890 }
891
892 g3_draw_line(&pnt0, &prev_pnt);
893 }
894 }
895 }
896
897 int Model_texturing = 1;
898 int Model_polys = 1;
899
DCF_BOOL(model_texturing,Model_texturing)900 DCF_BOOL( model_texturing, Model_texturing )
901 DCF_BOOL( model_polys, Model_polys )
902
903 MONITOR( NumModelsRend )
904 MONITOR( NumHiModelsRend )
905 MONITOR( NumMedModelsRend )
906 MONITOR( NumLowModelsRend )
907
908 /**
909 * Draws a bitmap with the specified 3d width & height
910 * @return 1 if off screen, 0 if not
911 */
912 int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v)
913 {
914 float sa, ca;
915 int i;
916
917 Assert( G3_count == 1 );
918
919 sa = sinf(angle);
920 ca = cosf(angle);
921
922 float width, height;
923
924 width = height = rad;
925
926 v[0].world.xyz.x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
927 v[0].world.xyz.y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
928 v[0].world.xyz.z = pnt->world.xyz.z;
929 v[0].screen.xyw.w = 0.0f;
930 v[0].texture_position.u = 0.0f;
931 v[0].texture_position.v = 0.0f;
932
933 v[1].world.xyz.x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
934 v[1].world.xyz.y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
935 v[1].world.xyz.z = pnt->world.xyz.z;
936 v[1].screen.xyw.w = 0.0f;
937 v[1].texture_position.u = 1.0f;
938 v[1].texture_position.v = 0.0f;
939
940 v[2].world.xyz.x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
941 v[2].world.xyz.y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
942 v[2].world.xyz.z = pnt->world.xyz.z;
943 v[2].screen.xyw.w = 0.0f;
944 v[2].texture_position.u = 1.0f;
945 v[2].texture_position.v = 1.0f;
946
947 v[3].world.xyz.x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
948 v[3].world.xyz.y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
949 v[3].world.xyz.z = pnt->world.xyz.z;
950 v[3].screen.xyw.w = 0.0f;
951 v[3].texture_position.u = 0.0f;
952 v[3].texture_position.v = 1.0f;
953
954 ubyte codes_and=0xff;
955
956 float sw,z;
957 z = pnt->world.xyz.z - rad / 4.0f;
958 if ( z < 0.0f ) z = 0.0f;
959 sw = 1.0f / z;
960
961 for (i=0; i<4; i++ ) {
962 //now code the four points
963 codes_and &= g3_code_vertex(&v[i]);
964 v[i].flags = 0; // mark as not yet projected
965 g3_project_vertex(&v[i]);
966 v[i].screen.xyw.w = sw;
967 }
968
969 if (codes_and)
970 return 1; //1 means off screen
971
972 return 0;
973 }
974
975 float Interp_depth_scale = 1500.0f;
976
977 DCF(model_darkening,"Makes models darker with distance")
978 {
979 if (dc_optional_string_either("help", "--help")) {
980 dc_printf( "Usage: model_darkening <float>\n" );
981 dc_printf("Sets the distance at which to start blacking out models (namely asteroids).\n");
982 return;
983 }
984
985 if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
986 dc_printf( "model_darkening = %.1f\n", Interp_depth_scale );
987 return;
988 }
989
990 dc_stuff_float(&Interp_depth_scale);
991
992 dc_printf("model_darkening set to %.1f\n", Interp_depth_scale);
993 }
994
995 // tmp_detail_level
996 // 0 - Max
997 // 1
998 // 2
999 // 3
1000 // 4 - None
1001
1002 #if MAX_DETAIL_LEVEL != 4
1003 #error MAX_DETAIL_LEVEL is assumed to be 4 in ModelInterp.cpp
1004 #endif
1005
1006
1007 /**
1008 * Find the distance-squared from p0 to the closest point on a box. Fills in closest_pt.
1009 * The box's dimensions are from 'min' to 'max'.
1010 */
interp_closest_dist_sq_to_box(vec3d * closest_pt,const vec3d * p0,const vec3d * min,const vec3d * max)1011 float interp_closest_dist_sq_to_box( vec3d *closest_pt, const vec3d *p0, const vec3d *min, const vec3d *max )
1012 {
1013 auto origin = p0->a1d;
1014 auto minB = min->a1d;
1015 auto maxB = max->a1d;
1016 auto coord = closest_pt->a1d;
1017 bool inside = true;
1018 int i;
1019
1020 for (i=0; i<3; i++ ) {
1021 if ( origin[i] < minB[i] ) {
1022 coord[i] = minB[i];
1023 inside = false;
1024 } else if (origin[i] > maxB[i] ) {
1025 coord[i] = maxB[i];
1026 inside = false;
1027 } else {
1028 coord[i] = origin[i];
1029 }
1030 }
1031
1032 if ( inside ) {
1033 return 0.0f;
1034 }
1035
1036 return vm_vec_dist_squared(closest_pt, p0);
1037 }
1038
1039
1040 // Finds the closest point on a model to a point in space. Actually only finds a point
1041 // on the bounding box of the model.
1042 // Given:
1043 // model_num Which model
1044 // orient Orientation of the model
1045 // pos Position of the model
1046 // eye_pos Point that you want to find the closest point to
1047 // Returns:
1048 // distance from eye_pos to closest_point. 0 means eye_pos is
1049 // on or inside the bounding box.
1050 // Also fills in outpnt with the actual closest point (in local coordinates).
model_find_closest_point(vec3d * outpnt,int model_num,int submodel_num,const matrix * orient,const vec3d * pos,const vec3d * eye_pos)1051 float model_find_closest_point( vec3d *outpnt, int model_num, int submodel_num, const matrix *orient, const vec3d *pos, const vec3d *eye_pos )
1052 {
1053 vec3d tempv, eye_rel_pos;
1054
1055 polymodel *pm = model_get(model_num);
1056
1057 if ( submodel_num < 0 ) {
1058 submodel_num = pm->detail[0];
1059 }
1060
1061 // Rotate eye pos into object coordinates
1062 vm_vec_sub(&tempv, pos, eye_pos);
1063 vm_vec_rotate(&eye_rel_pos, &tempv, orient);
1064
1065 return fl_sqrt( interp_closest_dist_sq_to_box( outpnt, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max ) );
1066 }
1067
1068 // Like the above, but finds the closest two points to each other.
model_find_closest_points(vec3d * outpnt1,int model_num1,int submodel_num1,const matrix * orient1,const vec3d * pos1,vec3d * outpnt2,int model_num2,int submodel_num2,const matrix * orient2,const vec3d * pos2)1069 float model_find_closest_points(vec3d *outpnt1, int model_num1, int submodel_num1, const matrix *orient1, const vec3d *pos1, vec3d *outpnt2, int model_num2, int submodel_num2, const matrix *orient2, const vec3d *pos2)
1070 {
1071 polymodel *pm1 = model_get(model_num1);
1072 if (submodel_num1 < 0)
1073 submodel_num1 = pm1->detail[0];
1074
1075 polymodel *pm2 = model_get(model_num2);
1076 if (submodel_num2 < 0)
1077 submodel_num2 = pm2->detail[0];
1078
1079 // determine obj2's bounding box
1080 vec3d bounding_box[8];
1081 model_calc_bound_box(bounding_box, &pm2->submodel[submodel_num2].min, &pm2->submodel[submodel_num2].max);
1082
1083 float closest_dist_sq = -1.0f;
1084
1085 // check each point on it
1086 for (const auto &pt : bounding_box)
1087 {
1088 vec3d temp, rel_pt;
1089
1090 // find world coordinates of this point
1091 vm_vec_unrotate(&temp, &pt, orient2);
1092 vm_vec_add(&rel_pt, &temp, pos2);
1093
1094 // now find coordinates relative to obj1
1095 vm_vec_sub(&temp, pos1, &rel_pt);
1096 vm_vec_rotate(&rel_pt, &temp, orient1);
1097
1098 // test this point
1099 float dist_sq = interp_closest_dist_sq_to_box(&temp, &rel_pt, &pm1->submodel[submodel_num1].min, &pm1->submodel[submodel_num1].max);
1100 if (closest_dist_sq < 0.0f || dist_sq < closest_dist_sq)
1101 {
1102 closest_dist_sq = dist_sq;
1103
1104 // Note: As in the other function, both of these points are
1105 // in local coordinates relative to each of their models.
1106 *outpnt1 = temp;
1107 *outpnt2 = pt;
1108 }
1109 }
1110
1111 // we have now found the closest point
1112 return fl_sqrt(closest_dist_sq);
1113 }
1114
1115 int tiling = 1;
1116 DCF(tiling, "Toggles rendering of tiled textures (default is on)")
1117 {
1118 if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1119 dc_printf("Tiled textures are %s", tiling ? "ON" : "OFF");
1120 return;
1121 }
1122
1123 tiling = !tiling;
1124 if(tiling){
1125 dc_printf("Tiled textures\n");
1126 } else {
1127 dc_printf("Non-tiled textures\n");
1128 }
1129 }
1130
moldel_calc_facing_pts(vec3d * top,vec3d * bot,vec3d * fvec,vec3d * pos,float w,float,vec3d * Eyeposition)1131 void moldel_calc_facing_pts( vec3d *top, vec3d *bot, vec3d *fvec, vec3d *pos, float w, float /*z_add*/, vec3d *Eyeposition )
1132 {
1133 vec3d uvec, rvec;
1134 vec3d temp;
1135
1136 temp = *pos;
1137
1138 vm_vec_sub( &rvec, Eyeposition, &temp );
1139 vm_vec_normalize( &rvec );
1140
1141 vm_vec_cross(&uvec,fvec,&rvec);
1142 vm_vec_normalize(&uvec);
1143
1144 vm_vec_scale_add( top, &temp, &uvec, w/2.0f );
1145 vm_vec_scale_add( bot, &temp, &uvec, -w/2.0f );
1146 }
1147
1148 // Fills in an array with points from a model.
1149 // Only gets up to max_num verts;
1150 // Returns number of verts found;
submodel_get_points_internal(int model_num,int submodel_num)1151 static int submodel_get_points_internal(int model_num, int submodel_num)
1152 {
1153 polymodel * pm;
1154
1155 pm = model_get(model_num);
1156
1157 if ( submodel_num < 0 ) {
1158 submodel_num = pm->detail[0];
1159 }
1160
1161 ubyte *p = pm->submodel[submodel_num].bsp_data;
1162 int chunk_type, chunk_size;
1163
1164 chunk_type = w(p);
1165 chunk_size = w(p+4);
1166
1167 while (chunk_type != OP_EOF) {
1168 switch (chunk_type) {
1169 case OP_DEFPOINTS: {
1170 int n;
1171 int nverts = w(p+8);
1172 int offset = w(p+16);
1173 int nnorms = 0;
1174
1175 ubyte * normcount = p+20;
1176 vec3d *src = vp(p+offset);
1177
1178 for (n = 0; n < nverts; n++) {
1179 nnorms += normcount[n];
1180 }
1181
1182 model_allocate_interp_data(nverts, nnorms);
1183
1184 // this must happen only after the interp_data allocation call (since the address changes)
1185 vec3d **verts = Interp_verts;
1186 vec3d **norms = Interp_norms;
1187
1188 for (n=0; n<nverts; n++ ) {
1189 *verts++ = src;
1190 *norms++ = src + 1; // first normal associated with the point
1191
1192 src += normcount[n]+1;
1193 }
1194 return nverts; // Read in 'n' points
1195 }
1196 break;
1197 case OP_FLATPOLY: break;
1198 case OP_TMAPPOLY: break;
1199 case OP_SORTNORM: break;
1200 case OP_BOUNDBOX: break;
1201 default:
1202 mprintf(( "Bad chunk type %d, len=%d in submodel_get_points\n", chunk_type, chunk_size ));
1203 Int3(); // Bad chunk type!
1204 return 0;
1205 }
1206 p += chunk_size;
1207 chunk_type = w(p);
1208 chunk_size = w(p+4);
1209 }
1210 return 0; // Couldn't find 'em
1211 }
1212
1213 /**
1214 * Gets two random points on a model
1215 */
submodel_get_two_random_points(int model_num,int submodel_num,vec3d * v1,vec3d * v2,vec3d * n1,vec3d * n2)1216 void submodel_get_two_random_points(int model_num, int submodel_num, vec3d *v1, vec3d *v2, vec3d *n1, vec3d *n2 )
1217 {
1218 int nv = submodel_get_points_internal(model_num, submodel_num);
1219
1220 // 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
1221 if (nv <= 0) {
1222 polymodel *pm = model_get(model_num);
1223 Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_points_internal!", model_num, (pm == NULL) ? "<null model?!?>" : pm->filename);
1224
1225 // in case people ignore the error...
1226 vm_vec_zero(v1);
1227 vm_vec_zero(v2);
1228 if (n1 != NULL) {
1229 vm_vec_zero(n1);
1230 }
1231 if (n2 != NULL) {
1232 vm_vec_zero(n2);
1233 }
1234 return;
1235 }
1236
1237 int vn1 = Random::next(nv);
1238 int vn2 = Random::next(nv);
1239
1240 *v1 = *Interp_verts[vn1];
1241 *v2 = *Interp_verts[vn2];
1242
1243 if(n1 != NULL){
1244 *n1 = *Interp_norms[vn1];
1245 }
1246 if(n2 != NULL){
1247 *n2 = *Interp_norms[vn2];
1248 }
1249 }
1250
submodel_get_two_random_points_better(int model_num,int submodel_num,vec3d * v1,vec3d * v2,int seed)1251 void submodel_get_two_random_points_better(int model_num, int submodel_num, vec3d *v1, vec3d *v2, int seed)
1252 {
1253 polymodel *pm = model_get(model_num);
1254
1255 if (pm != NULL) {
1256 if ( submodel_num < 0 ) {
1257 submodel_num = pm->detail[0];
1258 }
1259
1260 // the Shivan Comm Node does not have a collision tree, for one
1261 if (pm->submodel[submodel_num].collision_tree_index < 0) {
1262 nprintf(("Model", "In submodel_get_two_random_points_better(), model %s does not have a collision tree! Falling back to submodel_get_two_random_points().\n", pm->filename));
1263
1264 submodel_get_two_random_points(model_num, submodel_num, v1, v2);
1265 return;
1266 }
1267
1268 bsp_collision_tree *tree = model_get_bsp_collision_tree(pm->submodel[submodel_num].collision_tree_index);
1269
1270 int nv = tree->n_verts;
1271
1272 // 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
1273 if (nv <= 0) {
1274 Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_points_internal!", model_num, (pm == NULL) ? "<null model?!?>" : pm->filename);
1275
1276 // in case people ignore the error...
1277 vm_vec_zero(v1);
1278 vm_vec_zero(v2);
1279
1280 return;
1281 }
1282
1283 int seed_num = seed == -1 ? Random::next() : seed;
1284 int vn1 = static_rand(seed_num) % nv;
1285 int vn2 = static_rand(seed_num) % nv;
1286
1287 *v1 = tree->point_list[vn1];
1288 *v2 = tree->point_list[vn2];
1289 }
1290 }
1291
submodel_get_cross_sectional_avg_pos(int model_num,int submodel_num,float z_slice_pos,vec3d * pos)1292 void submodel_get_cross_sectional_avg_pos(int model_num, int submodel_num, float z_slice_pos, vec3d* pos)
1293 {
1294 polymodel* pm = model_get(model_num);
1295
1296 if (pm != nullptr) {
1297 if (submodel_num < 0) {
1298 submodel_num = pm->detail[0];
1299 }
1300
1301 // the Shivan Comm Node does not have a collision tree, for one
1302 if (pm->submodel[submodel_num].collision_tree_index < 0) {
1303 nprintf(("Model", "In submodel_get_cross_sectional_avg_pos(), model %s does not have a collision tree!\n", pm->filename));
1304 return;
1305 }
1306
1307 bsp_collision_tree* tree = model_get_bsp_collision_tree(pm->submodel[submodel_num].collision_tree_index);
1308
1309 int nv = tree->n_verts;
1310
1311 // 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
1312 if (nv <= 0) {
1313 Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_cross_sectional_avg_pos!", model_num, (pm == nullptr) ? "<null model?!?>" : pm->filename);
1314
1315 // in case people ignore the error...
1316 vm_vec_zero(pos);
1317 return;
1318 }
1319
1320 vm_vec_zero(pos);
1321 // we take a regular average, add them all up, divide by the total number, but weighted by how close they are to the z slice
1322 float accum_scale_factor = 0.0f;
1323 for (int i = 0; i < tree->n_verts; i++) {
1324 // this goes from 1 directly at our z pos, and quickly goes to 0 the further it gets
1325 float scale_factor = 1 / ((fabs(tree->point_list[i].xyz.z - z_slice_pos) / (pm->rad / 10)) + 1);
1326 vm_vec_scale_add(pos, pos, &tree->point_list[i], scale_factor);
1327 // keep track of the scale factor we use because we need to divide by its total at the end
1328 accum_scale_factor += scale_factor;
1329 }
1330
1331 *pos /= accum_scale_factor;
1332 }
1333 }
1334
submodel_get_cross_sectional_random_pos(int model_num,int submodel_num,float z_slice_pos,vec3d * pos)1335 void submodel_get_cross_sectional_random_pos(int model_num, int submodel_num, float z_slice_pos, vec3d* pos)
1336 {
1337 polymodel* pm = model_get(model_num);
1338
1339 if (pm != nullptr) {
1340 if (submodel_num < 0) {
1341 submodel_num = pm->detail[0];
1342 }
1343
1344 // the Shivan Comm Node does not have a collision tree, for one
1345 if (pm->submodel[submodel_num].collision_tree_index < 0) {
1346 nprintf(("Model", "In submodel_get_cross_sectional_random_pos(), model %s does not have a collision tree!\n", pm->filename));
1347 return;
1348 }
1349
1350 bsp_collision_tree* tree = model_get_bsp_collision_tree(pm->submodel[submodel_num].collision_tree_index);
1351
1352 int nv = tree->n_verts;
1353
1354 // 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
1355 if (nv <= 0) {
1356 Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_cross_sectional_random_pos!", model_num, (pm == nullptr) ? "<null model?!?>" : pm->filename);
1357
1358 // in case people ignore the error...
1359 vm_vec_zero(pos);
1360 return;
1361 }
1362
1363 vm_vec_zero(pos);
1364 vec3d best1, best2;
1365 // make random guesses a bunch of times, and average our two best guesses closest to the z pos, ez
1366 // there are more accurate ways, but this is reasonably good and super cheap
1367 vm_vec_make(&best1, 0, 0, 999999);
1368 vm_vec_make(&best2, 0, 0, 999999);
1369 for (int i = 0; i < 15; i++) {
1370 vec3d rand_point = tree->point_list[Random::next(nv)];
1371 if (fabs(rand_point.xyz.z - z_slice_pos) < fabs(best1.xyz.z - z_slice_pos))
1372 best1 = rand_point;
1373 else if (fabs(rand_point.xyz.z - z_slice_pos) < fabs(best2.xyz.z - z_slice_pos))
1374 best2 = rand_point;
1375 }
1376
1377 vm_vec_avg(pos, &best1, &best2);
1378 }
1379 }
1380
1381 // If MR_FLAG_OUTLINE bit set this color will be used for outlines.
1382 // This defaults to black.
model_set_outline_color(int r,int g,int b)1383 void model_set_outline_color(int r, int g, int b )
1384 {
1385 gr_init_color( &Interp_outline_color, r, g, b );
1386
1387 }
1388
1389 // IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n'
1390 // This defaults to 0. (0=highest, larger=lower)
model_set_detail_level(int n)1391 void model_set_detail_level(int n)
1392 {
1393 Interp_detail_level_locked = n;
1394 }
1395
1396 /**
1397 * Returns number of tmaps & flat polys in a submodel;
1398 */
submodel_get_num_polys_sub(ubyte * p)1399 int submodel_get_num_polys_sub( ubyte *p )
1400 {
1401 int chunk_type = w(p);
1402 int chunk_size = w(p+4);
1403 int n = 0;
1404
1405 while (chunk_type != OP_EOF) {
1406 switch (chunk_type) {
1407 case OP_DEFPOINTS: break;
1408 case OP_FLATPOLY: n++; break;
1409 case OP_TMAPPOLY: n++; break;
1410 case OP_SORTNORM: {
1411 int frontlist = w(p+36);
1412 int backlist = w(p+40);
1413 int prelist = w(p+44);
1414 int postlist = w(p+48);
1415 int onlist = w(p+52);
1416 n += submodel_get_num_polys_sub(p+frontlist);
1417 n += submodel_get_num_polys_sub(p+backlist);
1418 n += submodel_get_num_polys_sub(p+prelist);
1419 n += submodel_get_num_polys_sub(p+postlist );
1420 n += submodel_get_num_polys_sub(p+onlist );
1421 }
1422 break;
1423 case OP_BOUNDBOX: break;
1424 default:
1425 mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size ));
1426 Int3(); // Bad chunk type!
1427 return 0;
1428 }
1429 p += chunk_size;
1430 chunk_type = w(p);
1431 chunk_size = w(p+4);
1432 }
1433 return n;
1434 }
1435
1436 /**
1437 * Returns number of tmaps & flat polys in a submodel
1438 */
submodel_get_num_polys(int model_num,int submodel_num)1439 int submodel_get_num_polys(int model_num, int submodel_num )
1440 {
1441 polymodel * pm;
1442
1443 pm = model_get(model_num);
1444
1445 return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data );
1446 }
1447
1448 /**
1449 * See if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error
1450 */
model_find_texture(int model_num,int bitmap)1451 int model_find_texture(int model_num, int bitmap)
1452 {
1453 polymodel * pm;
1454 int idx;
1455
1456 // get a handle to the model
1457 pm = model_get(model_num);
1458 if(pm == NULL){
1459 return -1;
1460 }
1461
1462 // find the texture
1463 for(idx=0; idx<pm->n_textures; idx++)
1464 {
1465 if(pm->maps[idx].FindTexture(bitmap) > -1)
1466 {
1467 return 1;
1468 }
1469 }
1470
1471 // no texture
1472 return 0;
1473 }
1474
1475 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
1476 // returns closest distance to extended box
1477 // positive return value means start_point is outside extended box
1478 // displaces closest point an optional amount delta to the outside of the box
1479 // closest_box_point can be NULL.
get_model_closest_box_point_with_delta(vec3d * closest_box_point,vec3d * start_point,int modelnum,int * is_inside,float delta)1480 float get_model_closest_box_point_with_delta(vec3d *closest_box_point, vec3d *start_point, int modelnum, int *is_inside, float delta)
1481 {
1482 int i, idx;
1483 vec3d box_point, ray_direction, *extremes;
1484 float dist, best_dist;
1485 polymodel *pm;
1486 int inside = 0;
1487 int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
1488 int mask_inside = 0x3f;
1489
1490 best_dist = FLT_MAX;
1491 pm = model_get(modelnum);
1492
1493 for (i=0; i<6; i++) {
1494 idx = i / 2; // which row vector of Identity matrix
1495
1496 memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vec3d));
1497
1498 // do negative, then positive plane for each axis
1499 if (2 * idx == i) {
1500 extremes = &pm->mins;
1501 vm_vec_negate(&ray_direction);
1502 } else {
1503 extremes = &pm->maxs;
1504 }
1505
1506 // a negative distance means started outside the box
1507 dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f);
1508 if (dist > 0) {
1509 inside |= masks[i];
1510 }
1511 if (fabs(dist) < fabs(best_dist)) {
1512 best_dist = dist;
1513 if (closest_box_point) {
1514 vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta);
1515 }
1516 }
1517 }
1518
1519 // is start_point inside the box
1520 if (is_inside) {
1521 *is_inside = (inside == mask_inside);
1522 }
1523
1524 return -best_dist;
1525 }
1526
1527 // find closest point on extended bounding box (the bounding box plus all the planes that make it up)
1528 // returns closest distance to extended box
1529 // positive return value means start_point is outside extended box
1530 // displaces closest point an optional amount delta to the outside of the box
1531 // closest_box_point can be NULL.
get_world_closest_box_point_with_delta(vec3d * closest_box_point,object * box_obj,vec3d * start_point,int * is_inside,float delta)1532 float get_world_closest_box_point_with_delta(vec3d *closest_box_point, object *box_obj, vec3d *start_point, int *is_inside, float delta)
1533 {
1534 vec3d temp, box_start;
1535 float dist;
1536 int modelnum;
1537
1538 // get modelnum
1539 modelnum = Ship_info[Ships[box_obj->instance].ship_info_index].model_num;
1540
1541 // rotate start_point to box_obj RF
1542 vm_vec_sub(&temp, start_point, &box_obj->pos);
1543 vm_vec_rotate(&box_start, &temp, &box_obj->orient);
1544
1545 dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta);
1546
1547 // rotate closest_box_point to world RF
1548 if (closest_box_point) {
1549 vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient);
1550 vm_vec_add(closest_box_point, &temp, &box_obj->pos);
1551 }
1552
1553 return dist;
1554 }
1555
1556 /**
1557 * Given a newly loaded model, page in all textures
1558 */
model_page_in_textures(int modelnum,int ship_info_index)1559 void model_page_in_textures(int modelnum, int ship_info_index)
1560 {
1561 int i, idx;
1562 polymodel *pm = model_get(modelnum);
1563
1564 // bogus
1565 if (pm == NULL)
1566 return;
1567
1568 for (idx = 0; idx < pm->n_textures; idx++) {
1569 pm->maps[idx].PageIn();
1570 }
1571
1572 for (i = 0; i < pm->n_glow_point_banks; i++) {
1573 glow_point_bank *bank = &pm->glow_point_banks[i];
1574
1575 bm_page_in_texture(bank->glow_bitmap);
1576 bm_page_in_texture(bank->glow_neb_bitmap);
1577 }
1578
1579 if (ship_info_index >= 0)
1580 ship_page_in_textures(ship_info_index);
1581 }
1582
1583 // unload all textures for a given model
1584 // "release" should only be set if called from model_unload()!!!
model_page_out_textures(int model_num,bool release)1585 void model_page_out_textures(int model_num, bool release)
1586 {
1587 int i, j;
1588
1589 if (model_num < 0)
1590 return;
1591
1592 polymodel *pm = model_get(model_num);
1593
1594 if (pm == NULL)
1595 return;
1596
1597 if (release && (pm->used_this_mission > 0))
1598 return;
1599
1600
1601 for (i = 0; i < pm->n_textures; i++) {
1602 pm->maps[i].PageOut(release);
1603 }
1604
1605 // NOTE: "release" doesn't work here for some, as of yet unknown, reason - taylor
1606 for (j = 0; j < pm->n_glow_point_banks; j++) {
1607 glow_point_bank *bank = &pm->glow_point_banks[j];
1608
1609 if (bank->glow_bitmap >= 0) {
1610 // if (release) {
1611 // bm_release(bank->glow_bitmap);
1612 // bank->glow_bitmap = -1;
1613 // } else {
1614 bm_unload(bank->glow_bitmap);
1615 // }
1616 }
1617
1618 if (bank->glow_neb_bitmap >= 0) {
1619 // if (release) {
1620 // bm_release(bank->glow_neb_bitmap);
1621 // bank->glow_neb_bitmap = -1;
1622 // } else {
1623 bm_unload(bank->glow_neb_bitmap);
1624 // }
1625 }
1626 }
1627 }
1628
1629
1630 //**********vertex buffer stuff**********//
1631 int tri_count[MAX_MODEL_TEXTURES];
1632 poly_list polygon_list[MAX_MODEL_TEXTURES];
1633
parse_defpoint(int off,ubyte * bsp_data)1634 void parse_defpoint(int off, ubyte *bsp_data)
1635 {
1636 int i, n;
1637 int nverts = w(off+bsp_data+8);
1638 int offset = w(off+bsp_data+16);
1639 int next_norm = 0;
1640
1641 ubyte *normcount = off+bsp_data+20;
1642 vec3d *src = vp(off+bsp_data+offset);
1643
1644 // Get pointer to lights
1645 Interp_lights = off+bsp_data+20+nverts;
1646
1647 #ifndef NDEBUG
1648 modelstats_num_verts += nverts;
1649 #endif
1650
1651 for (n = 0; n < nverts; n++) {
1652 Interp_verts[n] = src;
1653 src++; // move to normal
1654
1655 for (i = 0; i < normcount[n]; i++) {
1656 Interp_norms[next_norm] = src;
1657
1658 next_norm++;
1659 src++;
1660 }
1661 }
1662 }
1663
check_values(vec3d * N)1664 int check_values(vec3d *N)
1665 {
1666 // Values equal to -1.#IND0
1667 if(!is_valid_vec(N))
1668 {
1669 N->xyz.x = 1.0f;
1670 N->xyz.y = 0.0f;
1671 N->xyz.z = 0.0f;
1672 return 1;
1673 }
1674
1675 return 0;
1676 }
1677
1678 int Parse_normal_problem_count = 0;
1679
parse_tmap(int offset,ubyte * bsp_data)1680 void parse_tmap(int offset, ubyte *bsp_data)
1681 {
1682 int pof_tex = w(bsp_data+offset+40);
1683 int n_vert = w(bsp_data+offset+36);
1684
1685 ubyte *p = &bsp_data[offset+8];
1686 model_tmap_vert *tverts;
1687
1688 tverts = (model_tmap_vert *)&bsp_data[offset+44];
1689
1690 vertex *V;
1691 vec3d *v;
1692 vec3d *N;
1693
1694 int problem_count = 0;
1695
1696 for (int i = 1; i < (n_vert-1); i++) {
1697 V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)];
1698 N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)];
1699 v = Interp_verts[(int)tverts[0].vertnum];
1700 V->world.xyz.x = v->xyz.x;
1701 V->world.xyz.y = v->xyz.y;
1702 V->world.xyz.z = v->xyz.z;
1703 V->texture_position.u = tverts[0].u;
1704 V->texture_position.v = tverts[0].v;
1705
1706 *N = *Interp_norms[(int)tverts[0].normnum];
1707
1708 if ( IS_VEC_NULL(N) )
1709 *N = *vp(p);
1710
1711 problem_count += check_values(N);
1712 vm_vec_normalize_safe(N);
1713
1714 V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)+1];
1715 N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)+1];
1716 v = Interp_verts[(int)tverts[i].vertnum];
1717 V->world.xyz.x = v->xyz.x;
1718 V->world.xyz.y = v->xyz.y;
1719 V->world.xyz.z = v->xyz.z;
1720 V->texture_position.u = tverts[i].u;
1721 V->texture_position.v = tverts[i].v;
1722
1723 *N = *Interp_norms[(int)tverts[i].normnum];
1724
1725 if ( IS_VEC_NULL(N) )
1726 *N = *vp(p);
1727
1728 problem_count += check_values(N);
1729 vm_vec_normalize_safe(N);
1730
1731 V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)+2];
1732 N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)+2];
1733 v = Interp_verts[(int)tverts[i+1].vertnum];
1734 V->world.xyz.x = v->xyz.x;
1735 V->world.xyz.y = v->xyz.y;
1736 V->world.xyz.z = v->xyz.z;
1737 V->texture_position.u = tverts[i+1].u;
1738 V->texture_position.v = tverts[i+1].v;
1739
1740 *N = *Interp_norms[(int)tverts[i+1].normnum];
1741
1742 if ( IS_VEC_NULL(N) )
1743 *N = *vp(p);
1744
1745 problem_count += check_values(N);
1746 vm_vec_normalize_safe(N);
1747
1748 polygon_list[pof_tex].n_verts += 3;
1749 }
1750
1751 Parse_normal_problem_count += problem_count;
1752 }
1753
1754 void parse_sortnorm(int offset, ubyte *bsp_data);
1755
parse_bsp(int offset,ubyte * bsp_data)1756 void parse_bsp(int offset, ubyte *bsp_data)
1757 {
1758 int id = w(bsp_data+offset);
1759 int size = w(bsp_data+offset+4);
1760
1761 while (id != 0) {
1762 switch (id)
1763 {
1764 case OP_DEFPOINTS:
1765 parse_defpoint(offset, bsp_data);
1766 break;
1767
1768 case OP_SORTNORM:
1769 parse_sortnorm(offset, bsp_data);
1770 break;
1771
1772 case OP_FLATPOLY:
1773 break;
1774
1775 case OP_TMAPPOLY:
1776 parse_tmap(offset, bsp_data);
1777 break;
1778
1779 case OP_BOUNDBOX:
1780 break;
1781
1782 default:
1783 return;
1784 }
1785
1786 offset += size;
1787 id = w(bsp_data+offset);
1788 size = w(bsp_data+offset+4);
1789
1790 if (size < 1)
1791 id = OP_EOF;
1792 }
1793 }
1794
parse_sortnorm(int offset,ubyte * bsp_data)1795 void parse_sortnorm(int offset, ubyte *bsp_data)
1796 {
1797 int frontlist, backlist, prelist, postlist, onlist;
1798
1799 frontlist = w(bsp_data+offset+36);
1800 backlist = w(bsp_data+offset+40);
1801 prelist = w(bsp_data+offset+44);
1802 postlist = w(bsp_data+offset+48);
1803 onlist = w(bsp_data+offset+52);
1804
1805 if (prelist) parse_bsp(offset+prelist,bsp_data);
1806 if (backlist) parse_bsp(offset+backlist, bsp_data);
1807 if (onlist) parse_bsp(offset+onlist, bsp_data);
1808 if (frontlist) parse_bsp(offset+frontlist, bsp_data);
1809 if (postlist) parse_bsp(offset+postlist, bsp_data);
1810 }
1811
find_tmap(int offset,ubyte * bsp_data)1812 void find_tmap(int offset, ubyte *bsp_data)
1813 {
1814 int pof_tex = w(bsp_data+offset+40);
1815 int n_vert = w(bsp_data+offset+36);
1816
1817 tri_count[pof_tex] += n_vert-2;
1818 }
1819
find_defpoint(int off,ubyte * bsp_data)1820 void find_defpoint(int off, ubyte *bsp_data)
1821 {
1822 int n;
1823 int nverts = w(off+bsp_data+8);
1824
1825 ubyte * normcount = off+bsp_data+20;
1826
1827 // Get pointer to lights
1828 Interp_lights = off+bsp_data+20+nverts;
1829
1830 #ifndef NDEBUG
1831 modelstats_num_verts += nverts;
1832 #endif
1833
1834 int norm_num = 0;
1835
1836 for (n = 0; n < nverts; n++) {
1837 norm_num += normcount[n];
1838 }
1839
1840 Interp_num_verts = nverts;
1841 Interp_num_norms = norm_num;
1842 }
1843
1844 void find_sortnorm(int offset, ubyte *bsp_data);
1845
1846 // tri_count
find_tri_counts(int offset,ubyte * bsp_data)1847 void find_tri_counts(int offset, ubyte *bsp_data)
1848 {
1849 int id = w(bsp_data+offset);
1850 int size = w(bsp_data+offset+4);
1851
1852 while (id != 0) {
1853 switch (id)
1854 {
1855 case OP_DEFPOINTS:
1856 find_defpoint(offset, bsp_data);
1857 break;
1858
1859 case OP_SORTNORM:
1860 find_sortnorm(offset, bsp_data);
1861 break;
1862
1863 case OP_FLATPOLY:
1864 break;
1865
1866 case OP_TMAPPOLY:
1867 find_tmap(offset, bsp_data);
1868 break;
1869
1870 case OP_BOUNDBOX:
1871 break;
1872
1873 default:
1874 return;
1875 }
1876
1877 offset += size;
1878 id = w(bsp_data+offset);
1879 size = w(bsp_data+offset+4);
1880
1881 if (size < 1)
1882 id = OP_EOF;
1883 }
1884 }
1885
find_sortnorm(int offset,ubyte * bsp_data)1886 void find_sortnorm(int offset, ubyte *bsp_data)
1887 {
1888 int frontlist, backlist, prelist, postlist, onlist;
1889
1890 frontlist = w(bsp_data+offset+36);
1891 backlist = w(bsp_data+offset+40);
1892 prelist = w(bsp_data+offset+44);
1893 postlist = w(bsp_data+offset+48);
1894 onlist = w(bsp_data+offset+52);
1895
1896 if (prelist) find_tri_counts(offset+prelist,bsp_data);
1897 if (backlist) find_tri_counts(offset+backlist, bsp_data);
1898 if (onlist) find_tri_counts(offset+onlist, bsp_data);
1899 if (frontlist) find_tri_counts(offset+frontlist, bsp_data);
1900 if (postlist) find_tri_counts(offset+postlist, bsp_data);
1901 }
1902
model_interp_submit_buffers(indexed_vertex_source * vert_src,size_t vertex_stride)1903 void model_interp_submit_buffers(indexed_vertex_source *vert_src, size_t vertex_stride)
1904 {
1905 Assert(vert_src != NULL);
1906
1907 if ( !(vert_src->Vertex_list_size > 0 && vert_src->Index_list_size > 0 ) ) {
1908 return;
1909 }
1910
1911 if ( vert_src->Vertex_list != NULL ) {
1912 size_t offset;
1913 gr_heap_allocate(GpuHeap::ModelVertex, vert_src->Vertex_list_size, vert_src->Vertex_list, offset, vert_src->Vbuffer_handle);
1914
1915 // If this happens then someone must have allocated something from the heap with a different stride than what we
1916 // are using.
1917 Assertion(offset % vertex_stride == 0, "Offset returned by GPU heap allocation does not match stride value!");
1918 vert_src->Base_vertex_offset = offset / vertex_stride;
1919 vert_src->Vertex_offset = offset;
1920
1921 vm_free(vert_src->Vertex_list);
1922 vert_src->Vertex_list = NULL;
1923 }
1924
1925 if ( vert_src->Index_list != NULL ) {
1926 gr_heap_allocate(GpuHeap::ModelIndex, vert_src->Index_list_size, vert_src->Index_list, vert_src->Index_offset, vert_src->Ibuffer_handle);
1927
1928 vm_free(vert_src->Index_list);
1929 vert_src->Index_list = NULL;
1930 }
1931 }
1932
model_interp_pack_buffer(indexed_vertex_source * vert_src,vertex_buffer * vb)1933 bool model_interp_pack_buffer(indexed_vertex_source *vert_src, vertex_buffer *vb)
1934 {
1935 if ( vert_src == NULL ) {
1936 return false;
1937 }
1938
1939 Assertion(vb != nullptr, "Invalid vertex buffer specified!");
1940
1941 int i, n_verts = 0;
1942 size_t j;
1943 if ( vert_src->Vertex_list == NULL ) {
1944 vert_src->Vertex_list = vm_malloc(vert_src->Vertex_list_size);
1945
1946 // return invalid if we don't have the memory
1947 if ( vert_src->Vertex_list == NULL ) {
1948 return false;
1949 }
1950
1951 memset(vert_src->Vertex_list, 0, vert_src->Vertex_list_size);
1952 }
1953
1954 if ( vert_src->Index_list == NULL ) {
1955 vert_src->Index_list = vm_malloc(vert_src->Index_list_size);
1956
1957 // return invalid if we don't have the memory
1958 if ( vert_src->Index_list == NULL ) {
1959 return false;
1960 }
1961
1962 memset(vert_src->Index_list, 0, vert_src->Index_list_size);
1963 }
1964
1965 // bump to our index in the array
1966 auto array = reinterpret_cast<interp_vertex*>(static_cast<uint8_t*>(vert_src->Vertex_list) + (vb->vertex_offset));
1967
1968 // generate the vertex array
1969 n_verts = vb->model_list->n_verts;
1970 for ( i = 0; i < n_verts; i++ ) {
1971 vertex *vl = &vb->model_list->vert[i];
1972 auto outVert = &array[i];
1973
1974 // don't try to generate more data than what's available
1975 Assert(((i * sizeof(interp_vertex)) + sizeof(interp_vertex)) <= (vert_src->Vertex_list_size - vb->vertex_offset));
1976
1977 // NOTE: UV->NORM->TSB->MODEL_ID->VERT, This array order *must* be preserved!!
1978
1979 // tex coords
1980 if ( vb->flags & VB_FLAG_UV1 ) {
1981 outVert->uv = vl->texture_position;
1982 } else {
1983 outVert->uv.u = 1.0f;
1984 outVert->uv.v = 1.0f;
1985 }
1986
1987 // normals
1988 if ( vb->flags & VB_FLAG_NORMAL ) {
1989 Assert(vb->model_list->norm != NULL);
1990 outVert->normal = vb->model_list->norm[i];
1991 } else {
1992 outVert->normal.xyz.x = 0.0f;
1993 outVert->normal.xyz.y = 0.0f;
1994 outVert->normal.xyz.z = 1.0f;
1995 }
1996
1997 // tangent space data
1998 if ( vb->flags & VB_FLAG_TANGENT ) {
1999 Assert(vb->model_list->tsb != NULL);
2000 tsb_t *tsb = &vb->model_list->tsb[i];
2001
2002 outVert->tangent.xyzw.x = tsb->tangent.xyz.x;
2003 outVert->tangent.xyzw.y = tsb->tangent.xyz.y;
2004 outVert->tangent.xyzw.z = tsb->tangent.xyz.z;
2005 outVert->tangent.xyzw.w = tsb->scaler;
2006 } else {
2007 outVert->tangent.xyzw.x = 1.0f;
2008 outVert->tangent.xyzw.y = 0.0f;
2009 outVert->tangent.xyzw.z = 0.0f;
2010 outVert->tangent.xyzw.w = 0.0f;
2011 }
2012
2013 if ( vb->flags & VB_FLAG_MODEL_ID ) {
2014 Assert(vb->model_list->submodels != NULL);
2015 outVert->modelId = (float)vb->model_list->submodels[i];
2016 } else {
2017 outVert->modelId = 0.0f;
2018 }
2019
2020 // verts
2021 outVert->pos = vl->world;
2022 }
2023
2024 // generate the index array
2025 for ( j = 0; j < vb->tex_buf.size(); j++ ) {
2026 buffer_data* tex_buf = &vb->tex_buf[j];
2027 n_verts = (int)tex_buf->n_verts;
2028 auto offset = tex_buf->index_offset;
2029 const uint *index = tex_buf->get_index();
2030
2031 // bump to our spot in the buffer
2032 auto ibuf = static_cast<uint8_t*>(vert_src->Index_list) + offset;
2033
2034 if ( vb->tex_buf[j].flags & VB_FLAG_LARGE_INDEX ) {
2035 memcpy(ibuf, index, n_verts * sizeof(uint));
2036 } else {
2037 ushort *mybuf = (ushort*)ibuf;
2038
2039 for ( i = 0; i < n_verts; i++ ) {
2040 mybuf[i] = (ushort)index[i];
2041 }
2042 }
2043 }
2044
2045 return true;
2046 }
2047
interp_pack_vertex_buffers(polymodel * pm,int mn)2048 void interp_pack_vertex_buffers(polymodel *pm, int mn)
2049 {
2050 Assert( (mn >= 0) && (mn < pm->n_models) );
2051
2052 bsp_info *model = &pm->submodel[mn];
2053
2054 if ( !model->buffer.model_list ) {
2055 return;
2056 }
2057
2058 bool rval = model_interp_pack_buffer(&pm->vert_source, &model->buffer);
2059
2060 if ( model->trans_buffer.flags & VB_FLAG_TRANS && !model->trans_buffer.tex_buf.empty() ) {
2061 model_interp_pack_buffer(&pm->vert_source, &model->trans_buffer);
2062 }
2063
2064 if ( !rval ) {
2065 Error( LOCATION, "Unable to pack vertex buffer for '%s'\n", pm->filename );
2066 }
2067 }
2068
model_interp_set_buffer_layout(vertex_layout * layout)2069 void model_interp_set_buffer_layout(vertex_layout *layout)
2070 {
2071 Assert(layout != NULL);
2072
2073 // Similarly to model_interp_config_buffer, we add all vectex components even if they aren't used
2074 // This reduces the amount of vertex format respecification and since the data contains valid data there is no risk
2075 // of reading garbage data on the GPU
2076
2077 layout->add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(interp_vertex), offsetof(interp_vertex, uv));
2078
2079 layout->add_vertex_component(vertex_format_data::NORMAL, sizeof(interp_vertex), offsetof(interp_vertex, normal));
2080
2081 layout->add_vertex_component(vertex_format_data::TANGENT, sizeof(interp_vertex), offsetof(interp_vertex, tangent));
2082
2083 layout->add_vertex_component(vertex_format_data::MODEL_ID, sizeof(interp_vertex), offsetof(interp_vertex, modelId));
2084
2085 layout->add_vertex_component(vertex_format_data::POSITION3, sizeof(interp_vertex), offsetof(interp_vertex, pos));
2086 }
2087
model_interp_config_buffer(indexed_vertex_source * vert_src,vertex_buffer * vb,bool update_ibuffer_only)2088 bool model_interp_config_buffer(indexed_vertex_source *vert_src, vertex_buffer *vb, bool update_ibuffer_only)
2089 {
2090 if ( vb == NULL ) {
2091 return false;
2092 }
2093
2094 if ( !(vb->flags & VB_FLAG_POSITION) ) {
2095 Int3();
2096 return false;
2097 }
2098
2099 // pad out the vertex buffer even if it doesn't use certain attributes
2100 // we require consistent stride across vertex buffers so we can use base vertex offsetting for performance reasons
2101 vb->stride = sizeof(interp_vertex);
2102
2103 model_interp_set_buffer_layout(&vb->layout);
2104
2105 // offsets for this chunk
2106 if ( !update_ibuffer_only ) {
2107 vb->vertex_offset = vert_src->Vertex_list_size;
2108 vb->vertex_num_offset = vb->vertex_offset / vb->stride;
2109 vert_src->Vertex_list_size += (uint)(vb->stride * vb->model_list->n_verts);
2110 }
2111
2112 for ( size_t idx = 0; idx < vb->tex_buf.size(); idx++ ) {
2113 buffer_data *bd = &vb->tex_buf[idx];
2114
2115 bd->index_offset = vert_src->Index_list_size;
2116 vert_src->Index_list_size += (uint)(bd->n_verts * ((bd->flags & VB_FLAG_LARGE_INDEX) ? sizeof(uint) : sizeof(ushort)));
2117
2118 // even out index buffer so we are always word aligned
2119 vert_src->Index_list_size += (uint)(vert_src->Index_list_size % sizeof(uint));
2120 }
2121
2122 return true;
2123 }
2124
interp_configure_vertex_buffers(polymodel * pm,int mn)2125 void interp_configure_vertex_buffers(polymodel *pm, int mn)
2126 {
2127 TRACE_SCOPE(tracing::ModelConfigureVertexBuffers);
2128
2129 int i, j, first_index;
2130 uint total_verts = 0;
2131 SCP_vector<int> vertex_list;
2132
2133 Assert( (mn >= 0) && (mn < pm->n_models) );
2134
2135 bsp_info *model = &pm->submodel[mn];
2136
2137 for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
2138 polygon_list[i].n_verts = 0;
2139 tri_count[i] = 0;
2140 }
2141
2142 int milliseconds = timer_get_milliseconds();
2143
2144 bsp_polygon_data *bsp_polies = new bsp_polygon_data(model->bsp_data);
2145
2146 for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
2147 int vert_count = bsp_polies->get_num_triangles(i) * 3;
2148 tri_count[i] = vert_count / 3;
2149 total_verts += vert_count;
2150
2151 polygon_list[i].allocate(vert_count);
2152
2153 bsp_polies->generate_triangles(i, polygon_list[i].vert, polygon_list[i].norm);
2154 polygon_list[i].n_verts = vert_count;
2155
2156 // set submodel ID
2157 for ( j = 0; j < polygon_list[i].n_verts; ++j ) {
2158 polygon_list[i].submodels[j] = mn;
2159 }
2160
2161 // for the moment we can only support INT_MAX worth of verts per index buffer
2162 if (total_verts > INT_MAX) {
2163 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);
2164 }
2165 }
2166
2167 // figure out if we have an outline
2168 int outline_n_lines = bsp_polies->get_num_lines(-1);
2169
2170 if ( outline_n_lines > 0 ) {
2171 model->n_verts_outline = outline_n_lines * 2;
2172 model->outline_buffer = (vertex*)vm_malloc(sizeof(vertex) * model->n_verts_outline);
2173
2174 bsp_polies->generate_lines(-1, model->outline_buffer);
2175 }
2176
2177 // done with the bsp now that we have the vertex data
2178 delete bsp_polies;
2179
2180 int time_elapsed = timer_get_milliseconds() - milliseconds;
2181
2182 nprintf(("Model", "BSP Parse took %d milliseconds.\n", time_elapsed));
2183
2184 if (total_verts < 1) {
2185 return;
2186 }
2187
2188 total_verts = 0;
2189
2190 for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
2191 total_verts += polygon_list[i].n_verts;
2192 }
2193
2194 poly_list *model_list = new(std::nothrow) poly_list;
2195
2196 if ( !model_list ) {
2197 Error( LOCATION, "Unable to allocate memory for poly_list!\n" );
2198 }
2199
2200 model->buffer.model_list = model_list;
2201
2202 model_list->allocate( (int)total_verts );
2203
2204 for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
2205 if ( !polygon_list[i].n_verts )
2206 continue;
2207
2208 memcpy( (model_list->vert) + model_list->n_verts, polygon_list[i].vert, sizeof(vertex) * polygon_list[i].n_verts );
2209 memcpy( (model_list->norm) + model_list->n_verts, polygon_list[i].norm, sizeof(vec3d) * polygon_list[i].n_verts );
2210
2211 if (Cmdline_normal) {
2212 memcpy( (model_list->tsb) + model_list->n_verts, polygon_list[i].tsb, sizeof(tsb_t) * polygon_list[i].n_verts );
2213 }
2214
2215 memcpy( (model_list->submodels) + model_list->n_verts, polygon_list[i].submodels, sizeof(int) * polygon_list[i].n_verts );
2216
2217 model_list->n_verts += polygon_list[i].n_verts;
2218 }
2219
2220 // no read file so we'll have to generate
2221 model_list->make_index_buffer(vertex_list);
2222
2223 vertex_list.clear(); // done
2224
2225 int vertex_flags = (VB_FLAG_POSITION | VB_FLAG_NORMAL | VB_FLAG_UV1);
2226
2227 if (model_list->tsb != NULL) {
2228 Assert( Cmdline_normal );
2229 vertex_flags |= VB_FLAG_TANGENT;
2230 }
2231
2232 if ( model_list->submodels != NULL ) {
2233 vertex_flags |= VB_FLAG_MODEL_ID;
2234 }
2235
2236 model->buffer.flags = vertex_flags;
2237
2238 for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
2239 if ( !polygon_list[i].n_verts )
2240 continue;
2241
2242 buffer_data new_buffer(polygon_list[i].n_verts);
2243
2244 Verify( new_buffer.get_index() != NULL );
2245
2246 for (j = 0; j < polygon_list[i].n_verts; j++) {
2247 first_index = model_list->find_index_fast(&polygon_list[i], j);
2248 Assert(first_index != -1);
2249
2250 new_buffer.assign(j, first_index);
2251 }
2252
2253 new_buffer.texture = i;
2254
2255 new_buffer.flags = 0;
2256
2257 if (polygon_list[i].n_verts >= USHRT_MAX) {
2258 new_buffer.flags |= VB_FLAG_LARGE_INDEX;
2259 }
2260
2261 model->buffer.tex_buf.push_back( new_buffer );
2262 }
2263
2264 bool rval = model_interp_config_buffer(&pm->vert_source, &model->buffer, false);
2265
2266 if ( !rval ) {
2267 Error( LOCATION, "Unable to configure vertex buffer for '%s'\n", pm->filename );
2268 }
2269 }
2270
interp_copy_index_buffer(vertex_buffer * src,vertex_buffer * dest,size_t * index_counts)2271 void interp_copy_index_buffer(vertex_buffer *src, vertex_buffer *dest, size_t *index_counts)
2272 {
2273 size_t i, j, k;
2274 size_t src_buff_size;
2275 buffer_data *src_buffer;
2276 buffer_data *dest_buffer;
2277 size_t vert_offset = src->vertex_num_offset; // assuming all submodels crunched into this index buffer have the same stride
2278 //int vert_offset = 0;
2279
2280 for ( i = 0; i < dest->tex_buf.size(); ++i ) {
2281 dest_buffer = &dest->tex_buf[i];
2282
2283 for ( j = 0; j < src->tex_buf.size(); ++j ) {
2284 if ( dest_buffer->texture != src->tex_buf[j].texture ) {
2285 continue;
2286 }
2287
2288 src_buffer = &src->tex_buf[j];
2289 src_buff_size = (size_t)src_buffer->n_verts;
2290
2291 for ( k = 0; k < src_buff_size; ++k ) {
2292 dest_buffer->assign(dest_buffer->n_verts, (uint32_t)(src_buffer->get_index()[k] + vert_offset)); // take into account the vertex offset.
2293 dest_buffer->n_verts++;
2294
2295 Assert(dest_buffer->n_verts <= index_counts[dest_buffer->texture]);
2296 }
2297 }
2298 }
2299 }
2300
interp_fill_detail_index_buffer(SCP_vector<int> & submodel_list,polymodel * pm,vertex_buffer * buffer)2301 void interp_fill_detail_index_buffer(SCP_vector<int> &submodel_list, polymodel *pm, vertex_buffer *buffer)
2302 {
2303 size_t index_counts[MAX_MODEL_TEXTURES];
2304 int i, j;
2305 int model_num;
2306
2307 for ( i = 0; i < MAX_MODEL_TEXTURES; ++i ) {
2308 index_counts[i] = 0;
2309 }
2310
2311 buffer->vertex_offset = 0;
2312 buffer->vertex_num_offset = 0;
2313 buffer->model_list = new(std::nothrow) poly_list;
2314
2315 int num_buffers;
2316 int tex_num;
2317
2318 // need to first count how many indexes there are in this entire detail model hierarchy
2319 for ( i = 0; i < (int)submodel_list.size(); ++i ) {
2320 model_num = submodel_list[i];
2321
2322 if ( pm->submodel[model_num].is_thruster ) {
2323 continue;
2324 }
2325
2326 num_buffers = (int)pm->submodel[model_num].buffer.tex_buf.size();
2327
2328 buffer->flags |= pm->submodel[model_num].buffer.flags;
2329
2330 for ( j = 0; j < num_buffers; ++j ) {
2331 tex_num = pm->submodel[model_num].buffer.tex_buf[j].texture;
2332
2333 index_counts[tex_num] += pm->submodel[model_num].buffer.tex_buf[j].n_verts;
2334 }
2335 }
2336
2337 // allocate the respective texture buffers with indexes for our detail buffer
2338 for ( i = 0; i < MAX_MODEL_TEXTURES; ++i ) {
2339 if ( index_counts[i] == 0 ) {
2340 continue;
2341 }
2342
2343 buffer->tex_buf.push_back(buffer_data(index_counts[i]));
2344
2345 buffer_data &new_buffer = buffer->tex_buf.back();
2346 //new_buffer.n_verts = 0;
2347 new_buffer.texture = i;
2348 }
2349
2350 for ( i = 0; i < (int)buffer->tex_buf.size(); ++i ) {
2351 buffer->tex_buf[i].n_verts = 0;
2352 }
2353
2354 // finally copy over the indexes
2355 for ( i = 0; i < (int)submodel_list.size(); ++i ) {
2356 model_num = submodel_list[i];
2357
2358 if (pm->submodel[model_num].is_thruster) {
2359 continue;
2360 }
2361
2362 interp_copy_index_buffer(&pm->submodel[model_num].buffer, buffer, index_counts);
2363 }
2364
2365 // check which buffers need to have the > USHORT flag
2366 for ( i = 0; i < (int)buffer->tex_buf.size(); ++i ) {
2367 if ( buffer->tex_buf[i].i_last >= USHRT_MAX ) {
2368 buffer->tex_buf[i].flags |= VB_FLAG_LARGE_INDEX;
2369 }
2370 }
2371 }
2372
interp_create_detail_index_buffer(polymodel * pm,int detail_num)2373 void interp_create_detail_index_buffer(polymodel *pm, int detail_num)
2374 {
2375 TRACE_SCOPE(tracing::ModelCreateDetailIndexBuffers);
2376
2377 SCP_vector<int> submodel_list;
2378
2379 submodel_list.clear();
2380
2381 model_get_submodel_tree_list(submodel_list, pm, pm->detail[detail_num]);
2382
2383 if ( submodel_list.empty() ) {
2384 return;
2385 }
2386
2387 interp_fill_detail_index_buffer(submodel_list, pm, &pm->detail_buffers[detail_num]);
2388
2389 // check if anything was even put into this buffer
2390 if ( pm->detail_buffers[detail_num].tex_buf.empty() ) {
2391 return;
2392 }
2393
2394 model_interp_config_buffer(&pm->vert_source, &pm->detail_buffers[detail_num], true);
2395 }
2396
interp_create_transparency_index_buffer(polymodel * pm,int mn)2397 void interp_create_transparency_index_buffer(polymodel *pm, int mn)
2398 {
2399 TRACE_SCOPE(tracing::ModelCreateTransparencyIndexBuffer);
2400
2401 const int NUM_VERTS_PER_TRI = 3;
2402
2403 bsp_info *sub_model = &pm->submodel[mn];
2404
2405 vertex_buffer *trans_buffer = &sub_model->trans_buffer;
2406
2407 trans_buffer->model_list = new(std::nothrow) poly_list;
2408 trans_buffer->vertex_offset = pm->submodel[mn].buffer.vertex_offset;
2409 trans_buffer->vertex_num_offset = pm->submodel[mn].buffer.vertex_num_offset;
2410 trans_buffer->stride = pm->submodel[mn].buffer.stride;
2411 trans_buffer->flags = pm->submodel[mn].buffer.flags;
2412
2413 poly_list *model_list = pm->submodel[mn].buffer.model_list;
2414
2415 // bail out if this buffer is empty
2416 if ( model_list == NULL || model_list->n_verts < 1 ) {
2417 return;
2418 }
2419
2420 SCP_vector<buffer_data> &tex_buffers = pm->submodel[mn].buffer.tex_buf;
2421 uint current_tri[NUM_VERTS_PER_TRI];
2422 bool transparent_tri = false;
2423 int num_tris = 0;
2424
2425 for ( int i = 0; i < (int)tex_buffers.size(); ++i ) {
2426 buffer_data *tex_buf = &tex_buffers[i];
2427
2428 if ( tex_buf->n_verts < 1 ) {
2429 continue;
2430 }
2431
2432 const uint *indices = tex_buf->get_index();
2433
2434 texture_map *tmap = &pm->maps[tex_buf->texture];
2435
2436 // skip if this is already designated to be a transparent pass by the modeller
2437 // if ( tmap->is_transparent ) {
2438 // continue;
2439 // }
2440
2441 int bitmap_handle = tmap->textures[TM_BASE_TYPE].GetTexture();
2442
2443 if ( bitmap_handle < 0 || !bm_has_alpha_channel(bitmap_handle) ) {
2444 continue;
2445 }
2446
2447 bitmap_lookup texture_lookup(bitmap_handle);
2448
2449 if ( !texture_lookup.valid() ) {
2450 continue;
2451 }
2452
2453 SCP_vector<int> transparent_indices;
2454
2455 transparent_tri = false;
2456 num_tris = 0;
2457
2458 for ( size_t j = 0; j < tex_buf->n_verts; ++j ) {
2459 uint index = indices[j];
2460
2461 // need the uv coords of the vert at this index
2462 float u = model_list->vert[index].texture_position.u;
2463 float v = model_list->vert[index].texture_position.v;
2464
2465 if ( texture_lookup.get_channel_alpha(u, v) < 0.95f) {
2466 transparent_tri = true;
2467 }
2468
2469 current_tri[num_tris] = index;
2470 num_tris++;
2471
2472 if ( num_tris == NUM_VERTS_PER_TRI ) {
2473 if ( transparent_tri ) {
2474 // we have a triangle and it's transparent.
2475 // shove index into the transparency buffer
2476 transparent_indices.push_back(current_tri[0]);
2477 transparent_indices.push_back(current_tri[1]);
2478 transparent_indices.push_back(current_tri[2]);
2479 }
2480
2481 transparent_tri = false;
2482 num_tris = 0;
2483 }
2484 }
2485
2486 if ( transparent_indices.empty() ) {
2487 continue;
2488 }
2489
2490 pm->flags |= PM_FLAG_TRANS_BUFFER;
2491 trans_buffer->flags |= VB_FLAG_TRANS;
2492
2493 trans_buffer->tex_buf.push_back ( buffer_data ( transparent_indices.size() ) );
2494
2495 buffer_data &new_buff = trans_buffer->tex_buf.back();
2496 new_buff.texture = tex_buf->texture;
2497
2498 for ( int j = 0; j < (int)transparent_indices.size(); ++j ) {
2499 new_buff.assign(j, transparent_indices[j]);
2500 }
2501 }
2502
2503 if ( trans_buffer->flags & VB_FLAG_TRANS ) {
2504 model_interp_config_buffer(&pm->vert_source, trans_buffer, true);
2505 }
2506 }
2507
model_interp_process_shield_mesh(polymodel * pm)2508 void model_interp_process_shield_mesh(polymodel * pm)
2509 {
2510 SCP_vector<vec3d> buffer;
2511
2512 if ( pm->shield.nverts <= 0 ) {
2513 return;
2514 }
2515
2516 int n_verts = 0;
2517
2518 for ( int i = 0; i < pm->shield.ntris; i++ ) {
2519 shield_tri *tri = &pm->shield.tris[i];
2520
2521 vec3d a = pm->shield.verts[tri->verts[0]].pos;
2522 vec3d b = pm->shield.verts[tri->verts[1]].pos;
2523 vec3d c = pm->shield.verts[tri->verts[2]].pos;
2524
2525 // recalculate triangle normals to solve some issues regarding triangle collision
2526 vec3d b_a;
2527 vec3d c_a;
2528
2529 vm_vec_sub(&b_a, &b, &a);
2530 vm_vec_sub(&c_a, &c, &a);
2531 vm_vec_cross(&tri->norm, &b_a, &c_a);
2532 vm_vec_normalize_safe(&tri->norm);
2533
2534 buffer.push_back(a);
2535 buffer.push_back(tri->norm);
2536
2537 buffer.push_back(b);
2538 buffer.push_back(tri->norm);
2539
2540 buffer.push_back(c);
2541 buffer.push_back(tri->norm);
2542
2543 n_verts += 3;
2544 }
2545
2546 if ( !buffer.empty() ) {
2547 pm->shield.buffer_id = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Static);
2548 pm->shield.buffer_n_verts = n_verts;
2549 gr_update_buffer_data(pm->shield.buffer_id, buffer.size() * sizeof(vec3d), &buffer[0]);
2550
2551 pm->shield.layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vec3d) * 2, 0);
2552 pm->shield.layout.add_vertex_component(vertex_format_data::NORMAL, sizeof(vec3d) * 2, sizeof(vec3d));
2553 } else {
2554 pm->shield.buffer_id = gr_buffer_handle::invalid();
2555 }
2556 }
2557
2558 // returns 1 if the thruster should be drawn
2559 // 0 if it shouldn't
model_should_render_engine_glow(int objnum,int bank_obj)2560 int model_should_render_engine_glow(int objnum, int bank_obj)
2561 {
2562 if ((bank_obj <= -1) || (objnum <= -1))
2563 return 1;
2564
2565 object *obj = &Objects[objnum];
2566
2567 if (obj->type == OBJ_SHIP) {
2568 ship_subsys *ssp;
2569 ship *shipp = &Ships[obj->instance];
2570 ship_info *sip = &Ship_info[shipp->ship_info_index];
2571
2572 Assert( bank_obj < sip->n_subsystems );
2573
2574 char subname[MAX_NAME_LEN];
2575 // shipp->subsystems isn't always valid here so don't use it
2576 strcpy_s(subname, sip->subsystems[bank_obj].subobj_name);
2577
2578 ssp = GET_FIRST(&shipp->subsys_list);
2579 while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
2580 if ( !strcmp(subname, ssp->system_info->subobj_name) ) {
2581 // this subsystem has 0 or less hits, ie. it's destroyed
2582 if ( ssp->current_hits <= 0 )
2583 return 0;
2584
2585 // see if the subsystem is disrupted, in which case it should be inoperable
2586 if ( ship_subsys_disrupted(ssp) )
2587 return 0;
2588
2589 return 1;
2590 }
2591 ssp = GET_NEXT( ssp );
2592 }
2593
2594 } else if (obj->type == OBJ_WEAPON) {
2595 // for weapons, if needed in the future
2596 }
2597
2598 // default to render glow
2599 return 1;
2600 }
2601
2602 // Goober5000
2603 // uses same algorithms as in ship_do_thruster_frame
model_interp_get_texture(texture_info * tinfo,fix base_frametime)2604 int model_interp_get_texture(texture_info *tinfo, fix base_frametime)
2605 {
2606 int texture, frame, num_frames;
2607 float cur_time, total_time;
2608
2609 // get texture
2610 num_frames = tinfo->GetNumFrames();
2611 texture = tinfo->GetTexture();
2612 total_time = tinfo->GetTotalTime();
2613
2614 // maybe animate it
2615 if (texture >= 0 && num_frames > 1)
2616 {
2617 // sanity check total_time first thing
2618 Assert(total_time > 0.0f);
2619
2620 cur_time = f2fl((game_get_overall_frametime() - base_frametime) % fl2f(total_time));
2621
2622 // get animation frame
2623 frame = fl2i((cur_time * num_frames) / total_time);
2624 CLAMP(frame, 0, num_frames - 1);
2625
2626 // advance to the correct frame
2627 texture += frame;
2628 }
2629
2630 // done
2631 return texture;
2632 }
2633
model_mix_two_team_colors(team_color * dest,team_color * a,team_color * b,float mix_factor)2634 void model_mix_two_team_colors(team_color* dest, team_color* a, team_color* b, float mix_factor)
2635 {
2636 dest->base.r = a->base.r * (1.0f - mix_factor) + b->base.r * mix_factor;
2637 dest->base.g = a->base.g * (1.0f - mix_factor) + b->base.g * mix_factor;
2638 dest->base.b = a->base.b * (1.0f - mix_factor) + b->base.b * mix_factor;
2639
2640 dest->stripe.r = a->stripe.r * (1.0f - mix_factor) + b->stripe.r * mix_factor;
2641 dest->stripe.g = a->stripe.g * (1.0f - mix_factor) + b->stripe.g * mix_factor;
2642 dest->stripe.b = a->stripe.b * (1.0f - mix_factor) + b->stripe.b * mix_factor;
2643 }
2644
model_get_team_color(team_color * clr,const SCP_string & team,const SCP_string & secondaryteam,fix timestamp,int fadetime)2645 bool model_get_team_color( team_color *clr, const SCP_string &team, const SCP_string &secondaryteam, fix timestamp, int fadetime )
2646 {
2647 Assert(clr != NULL);
2648
2649 if ( !stricmp(secondaryteam.c_str(), "none") ) {
2650 if (Team_Colors.find(team) != Team_Colors.end()) {
2651 *clr = Team_Colors[team];
2652 return true;
2653 } else
2654 return false;
2655 } else {
2656 if ( Team_Colors.find(secondaryteam) != Team_Colors.end()) {
2657 team_color temp_color;
2658 team_color start;
2659
2660 if (Team_Colors.find(team) != Team_Colors.end()) {
2661 start = Team_Colors[team];
2662 } else {
2663 start.base.r = 0.0f;
2664 start.base.g = 0.0f;
2665 start.base.b = 0.0f;
2666
2667 start.stripe.r = 0.0f;
2668 start.stripe.g = 0.0f;
2669 start.stripe.b = 0.0f;
2670 }
2671
2672 team_color end = Team_Colors[secondaryteam];
2673 float time_remaining = 0.0f;
2674 if (fadetime != 0) // avoid potential div-by-zero
2675 time_remaining = (f2fl(Missiontime - timestamp) * 1000)/fadetime;
2676 CLAMP(time_remaining, 0.0f, 1.0f);
2677 model_mix_two_team_colors(&temp_color, &start, &end, time_remaining);
2678
2679 *clr = temp_color;
2680 return true;
2681 } else
2682 return false;
2683 }
2684 }
2685
2686 //********************-----CLASS: texture_info-----********************//
texture_info()2687 texture_info::texture_info()
2688 {
2689 clear();
2690 }
texture_info(int bm_handle)2691 texture_info::texture_info(int bm_handle)
2692 {
2693 if(!bm_is_valid(bm_handle))
2694 {
2695 clear();
2696 return;
2697 }
2698
2699 this->original_texture = bm_handle;
2700 this->ResetTexture();
2701 }
clear()2702 void texture_info::clear()
2703 {
2704 texture = original_texture = -1;
2705 num_frames = 0;
2706 total_time = 1.0f;
2707 }
GetNumFrames()2708 int texture_info::GetNumFrames()
2709 {
2710 return num_frames;
2711 }
GetOriginalTexture()2712 int texture_info::GetOriginalTexture()
2713 {
2714 return original_texture;
2715 }
GetTexture()2716 int texture_info::GetTexture()
2717 {
2718 return texture;
2719 }
GetTotalTime()2720 float texture_info::GetTotalTime()
2721 {
2722 return total_time;
2723 }
LoadTexture(const char * filename,const char * dbg_name)2724 int texture_info::LoadTexture(const char *filename, const char *dbg_name)
2725 {
2726 if (strlen(filename) + 4 >= NAME_LENGTH) //Filenames are passed in without extension
2727 {
2728 mprintf(("Generated texture name %s is too long. Skipping...\n", filename));
2729 return -1;
2730 }
2731 this->original_texture = bm_load_either(filename, NULL, NULL, NULL, true, CF_TYPE_MAPS);
2732 if(this->original_texture < 0)
2733 nprintf(("Maps", "For \"%s\" I couldn't find %s.ani\n", dbg_name, filename));
2734 this->ResetTexture();
2735
2736 return texture;
2737 }
PageIn()2738 void texture_info::PageIn()
2739 {
2740 bm_page_in_texture(texture);
2741 }
2742
PageOut(bool release)2743 void texture_info::PageOut(bool release)
2744 {
2745 if (texture >= 0) {
2746 if (release) {
2747 bm_release(texture);
2748 texture = -1;
2749 num_frames = 0;
2750 total_time = 1.0f;
2751 } else {
2752 bm_unload(texture);
2753 }
2754 }
2755 }
ResetTexture()2756 int texture_info::ResetTexture()
2757 {
2758 return this->SetTexture(original_texture);
2759 }
SetTexture(int n_tex)2760 int texture_info::SetTexture(int n_tex)
2761 {
2762 if(n_tex != -1 && !bm_is_valid(n_tex))
2763 return texture;
2764
2765 //Set the new texture
2766 texture = n_tex;
2767
2768 //If it is intentionally invalid, blank everything else
2769 if(n_tex == -1)
2770 {
2771 num_frames = 0;
2772 total_time = 1.0f;
2773 }
2774 else
2775 {
2776 //Determine the num_frames and total_time values.
2777 int fps = 0;
2778 this->num_frames = 1;
2779
2780 bm_get_info(texture, NULL, NULL, NULL, &this->num_frames, &fps);
2781
2782 this->total_time = (num_frames / ((fps > 0) ? (float)fps : 1.0f));
2783 }
2784
2785 return texture;
2786 }
2787
2788 //********************-----CLASS: texture_map-----********************//
FindTexture(int bm_handle)2789 int texture_map::FindTexture(int bm_handle)
2790 {
2791 if(!bm_is_valid(bm_handle))
2792 return -1;
2793
2794 for(int i = 0; i < TM_NUM_TYPES; i++)
2795 {
2796 if (this->textures[i].GetTexture() == bm_handle)
2797 return i;
2798 }
2799 return -1;
2800 }
FindTexture(const char * fname)2801 int texture_map::FindTexture(const char* fname)
2802 {
2803 if(fname == NULL || !strlen(fname))
2804 return -1;
2805
2806 char buf[NAME_LENGTH];
2807 for(int i = 0; i < TM_NUM_TYPES; i++)
2808 {
2809 bm_get_filename(this->textures[i].GetTexture(), buf);
2810 if (!strextcmp(buf, fname)) {
2811 return i;
2812 }
2813 }
2814 return -1;
2815 }
2816
PageIn()2817 void texture_map::PageIn()
2818 {
2819 for(int i = 0; i < TM_NUM_TYPES; i++)
2820 this->textures[i].PageIn();
2821 }
2822
PageOut(bool release)2823 void texture_map::PageOut(bool release)
2824 {
2825 for(int i = 0; i < TM_NUM_TYPES; i++)
2826 this->textures[i].PageOut(release);
2827 }
2828
Clear()2829 void texture_map::Clear()
2830 {
2831 is_ambient = false;
2832 is_transparent = false;
2833
2834 for(int i = 0; i < TM_NUM_TYPES; i++)
2835 this->textures[i].clear();
2836 }
2837
ResetToOriginal()2838 void texture_map::ResetToOriginal()
2839 {
2840 for(int i = 0; i < TM_NUM_TYPES; i++)
2841 this->textures[i].ResetTexture();
2842 }
2843
bsp_polygon_data(ubyte * bsp_data)2844 bsp_polygon_data::bsp_polygon_data(ubyte* bsp_data)
2845 {
2846 Polygon_vertices.clear();
2847 Polygons.clear();
2848
2849 for (int i = 0; i < MAX_MODEL_TEXTURES; ++i) {
2850 Num_verts[i] = 0;
2851 Num_polies[i] = 0;
2852 }
2853
2854 Num_flat_verts = 0;
2855 Num_flat_polies = 0;
2856
2857 process_bsp(0, bsp_data);
2858 }
2859
process_bsp(int offset,ubyte * bsp_data)2860 void bsp_polygon_data::process_bsp(int offset, ubyte* bsp_data)
2861 {
2862 int id = w(bsp_data + offset);
2863 int size = w(bsp_data + offset + 4);
2864
2865 while (id != 0) {
2866 switch (id)
2867 {
2868 case OP_DEFPOINTS:
2869 process_defpoints(offset, bsp_data);
2870 break;
2871
2872 case OP_SORTNORM:
2873 process_sortnorm(offset, bsp_data);
2874 break;
2875
2876 case OP_FLATPOLY:
2877 process_flat(offset, bsp_data);
2878 break;
2879
2880 case OP_TMAPPOLY:
2881 process_tmap(offset, bsp_data);
2882 break;
2883
2884 case OP_BOUNDBOX:
2885 break;
2886
2887 default:
2888 return;
2889 }
2890
2891 offset += size;
2892 id = w(bsp_data + offset);
2893 size = w(bsp_data + offset + 4);
2894
2895 if (size < 1)
2896 id = OP_EOF;
2897 }
2898 }
2899
process_defpoints(int off,ubyte * bsp_data)2900 void bsp_polygon_data::process_defpoints(int off, ubyte* bsp_data)
2901 {
2902 int i, n;
2903 int nverts = w(off + bsp_data + 8);
2904 int offset = w(off + bsp_data + 16);
2905
2906 ubyte *normcount = off + bsp_data + 20;
2907 vec3d *src = vp(off + bsp_data + offset);
2908
2909 // Get pointer to lights
2910 Lights = off + bsp_data + 20 + nverts;
2911
2912 #ifndef NDEBUG
2913 modelstats_num_verts += nverts;
2914 #endif
2915
2916 Vertex_list.clear();
2917 Normal_list.clear();
2918
2919 for (n = 0; n < nverts; n++) {
2920 Vertex_list.push_back(*src);
2921 src++; // move to normal
2922
2923 for (i = 0; i < normcount[n]; i++) {
2924 Normal_list.push_back(*src);
2925 src++;
2926 }
2927 }
2928 }
2929
process_sortnorm(int offset,ubyte * bsp_data)2930 void bsp_polygon_data::process_sortnorm(int offset, ubyte* bsp_data)
2931 {
2932 int frontlist, backlist, prelist, postlist, onlist;
2933
2934 frontlist = w(bsp_data + offset + 36);
2935 backlist = w(bsp_data + offset + 40);
2936 prelist = w(bsp_data + offset + 44);
2937 postlist = w(bsp_data + offset + 48);
2938 onlist = w(bsp_data + offset + 52);
2939
2940 if (prelist) process_bsp(offset + prelist, bsp_data);
2941 if (backlist) process_bsp(offset + backlist, bsp_data);
2942 if (onlist) process_bsp(offset + onlist, bsp_data);
2943 if (frontlist) process_bsp(offset + frontlist, bsp_data);
2944 if (postlist) process_bsp(offset + postlist, bsp_data);
2945 }
2946
process_tmap(int offset,ubyte * bsp_data)2947 void bsp_polygon_data::process_tmap(int offset, ubyte* bsp_data)
2948 {
2949 int pof_tex = w(bsp_data + offset + 40);
2950 int n_vert = w(bsp_data + offset + 36);
2951
2952 if ( n_vert < 3 ) {
2953 // don't parse degenerate polygons
2954 return;
2955 }
2956
2957 ubyte *p = &bsp_data[offset + 8];
2958 model_tmap_vert *tverts;
2959
2960 tverts = (model_tmap_vert *)&bsp_data[offset + 44];
2961
2962 int problem_count = 0;
2963
2964 // make a polygon
2965 bsp_polygon polygon;
2966
2967 polygon.Start_index = (uint)Polygon_vertices.size();
2968 polygon.Num_verts = n_vert;
2969 polygon.texture = pof_tex;
2970
2971 // this polygon will be broken up into a triangle fan. first three verts make up the first triangle
2972 // additional verts are made into new tris
2973 Num_polies[pof_tex]++;
2974 Num_verts[pof_tex] += n_vert;
2975
2976 // stuff data making up the vertices of this polygon
2977 for ( int i = 0; i < n_vert; ++i ) {
2978 bsp_vertex vert;
2979
2980 vert.position = Vertex_list[(int)tverts[i].vertnum];
2981
2982 vert.tex_coord.u = tverts[i].u;
2983 vert.tex_coord.v = tverts[i].v;
2984
2985 vert.normal = Normal_list[(int)tverts[i].normnum];
2986
2987 // see if this normal is okay
2988 if (IS_VEC_NULL(&vert.normal))
2989 vert.normal = *vp(p);
2990
2991 problem_count += check_values(&vert.normal);
2992 vm_vec_normalize_safe(&vert.normal);
2993
2994 Polygon_vertices.push_back(vert);
2995 }
2996
2997 Polygons.push_back(polygon);
2998
2999 Parse_normal_problem_count += problem_count;
3000 }
3001
process_flat(int offset,ubyte * bsp_data)3002 void bsp_polygon_data::process_flat(int offset, ubyte* bsp_data)
3003 {
3004 int n_vert = w(bsp_data + offset + 36);
3005
3006 if (n_vert < 3) {
3007 // don't parse degenerate polygons
3008 return;
3009 }
3010
3011 short * verts = (short *)(bsp_data + offset + 44);
3012
3013 ubyte r = *(bsp_data + offset + 40);
3014 ubyte g = *(bsp_data + offset + 41);
3015 ubyte b = *(bsp_data + offset + 42);
3016
3017 bsp_polygon polygon;
3018
3019 polygon.Start_index = (uint)Polygon_vertices.size();
3020 polygon.Num_verts = n_vert;
3021 polygon.texture = -1;
3022
3023 Num_flat_polies++;
3024 Num_flat_verts += n_vert;
3025
3026 for (int i = 0; i < n_vert; i++) {
3027 bsp_vertex vert;
3028
3029 int vertnum = verts[i * 2 + 0];
3030 int norm = verts[i * 2 + 1];
3031
3032 vert.position = Vertex_list[vertnum];
3033 vert.normal = Normal_list[norm];
3034
3035 vert.r = r;
3036 vert.g = g;
3037 vert.b = b;
3038 vert.a = 255;
3039
3040 Polygon_vertices.push_back(vert);
3041 }
3042
3043 Polygons.push_back(polygon);
3044 }
3045
get_num_triangles(int texture)3046 int bsp_polygon_data::get_num_triangles(int texture)
3047 {
3048 if ( texture < 0 ) {
3049 return MAX(Num_flat_verts - 2 * Num_flat_polies, 0);
3050 }
3051
3052 return MAX(Num_verts[texture] - 2 * Num_polies[texture], 0);
3053 }
3054
get_num_lines(int texture)3055 int bsp_polygon_data::get_num_lines(int texture)
3056 {
3057 if (texture < 0) {
3058 return Num_flat_verts;
3059 }
3060
3061 return Num_verts[texture];
3062 }
3063
generate_triangles(int texture,vertex * vert_ptr,vec3d * norm_ptr)3064 void bsp_polygon_data::generate_triangles(int texture, vertex *vert_ptr, vec3d* norm_ptr)
3065 {
3066 int num_verts = 0;
3067
3068 for ( uint i = 0; i < Polygons.size(); ++i ) {
3069 if ( Polygons[i].texture != texture ) {
3070 continue;
3071 }
3072
3073 uint start_index = Polygons[i].Start_index;
3074 uint end_index = Polygons[i].Start_index + Polygons[i].Num_verts;
3075
3076 for ( uint j = start_index + 1; j < end_index - 1; ++j ) {
3077 // first vertex of this triangle. Always the first vertex of the polygon
3078 vertex* vert = &vert_ptr[num_verts];
3079 vert->world = Polygon_vertices[start_index].position;
3080 vert->texture_position = Polygon_vertices[start_index].tex_coord;
3081
3082 vec3d* norm = &norm_ptr[num_verts];
3083 *norm = Polygon_vertices[start_index].normal;
3084
3085 // second vertex of this triangle.
3086 vert = &vert_ptr[num_verts + 1];
3087 vert->world = Polygon_vertices[j].position;
3088 vert->texture_position = Polygon_vertices[j].tex_coord;
3089
3090 norm = &norm_ptr[num_verts + 1];
3091 *norm = Polygon_vertices[j].normal;
3092
3093 // third vertex of this triangle.
3094 vert = &vert_ptr[num_verts + 2];
3095 vert->world = Polygon_vertices[j+1].position;
3096 vert->texture_position = Polygon_vertices[j+1].tex_coord;
3097
3098 norm = &norm_ptr[num_verts + 2];
3099 *norm = Polygon_vertices[j+1].normal;
3100
3101 num_verts += 3;
3102 }
3103 }
3104 }
3105
generate_lines(int texture,vertex * vert_ptr)3106 void bsp_polygon_data::generate_lines(int texture, vertex *vert_ptr)
3107 {
3108 int num_verts = 0;
3109
3110 for (uint i = 0; i < Polygons.size(); ++i) {
3111 if (Polygons[i].texture != texture) {
3112 continue;
3113 }
3114
3115 uint start_index = Polygons[i].Start_index;
3116 uint end_index = Polygons[i].Start_index + Polygons[i].Num_verts;
3117
3118 for (uint j = start_index; j < end_index; ++j) {
3119 // first vertex of this triangle. Always the first vertex of the polygon
3120 vertex* vert = &vert_ptr[num_verts];
3121 vert->world = Polygon_vertices[j].position;
3122 vert->r = Polygon_vertices[j].r;
3123 vert->g = Polygon_vertices[j].g;
3124 vert->b = Polygon_vertices[j].b;
3125 vert->a = Polygon_vertices[j].a;
3126
3127 if ( j == end_index - 1 ) {
3128 vert = &vert_ptr[num_verts + 1];
3129 vert->world = Polygon_vertices[start_index].position;
3130 vert->r = Polygon_vertices[start_index].r;
3131 vert->g = Polygon_vertices[start_index].g;
3132 vert->b = Polygon_vertices[start_index].b;
3133 vert->a = Polygon_vertices[start_index].a;
3134 } else {
3135 vert = &vert_ptr[num_verts + 1];
3136 vert->world = Polygon_vertices[j + 1].position;
3137 vert->r = Polygon_vertices[j + 1].r;
3138 vert->g = Polygon_vertices[j + 1].g;
3139 vert->b = Polygon_vertices[j + 1].b;
3140 vert->a = Polygon_vertices[j + 1].a;
3141 }
3142
3143 num_verts += 2;
3144 }
3145 }
3146 }
3147