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