1 /*
2  * Copyright (C) Freespace Open 2013.  All rights reserved.
3  *
4  * All source code herein is the property of Freespace Open. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 #include "model/modelrender.h"
11 
12 #include "asteroid/asteroid.h"
13 #include "cmdline/cmdline.h"
14 #include "gamesequence/gamesequence.h"
15 #include "graphics/light.h"
16 #include "graphics/matrix.h"
17 #include "graphics/shadows.h"
18 #include "graphics/tmapper.h"
19 #include "graphics/uniforms.h"
20 #include "io/timer.h"
21 #include "jumpnode/jumpnode.h"
22 #include "math/staticrand.h"
23 #include "mod_table/mod_table.h"
24 #include "nebula/neb.h"
25 #include "particle/particle.h"
26 #include "render/3dinternal.h"
27 #include "render/batching.h"
28 #include "ship/ship.h"
29 #include "ship/shipfx.h"
30 #include "tracing/tracing.h"
31 #include "weapon/weapon.h"
32 
33 #include <algorithm>
34 
35 extern int Model_texturing;
36 extern int Model_polys;
37 extern int tiling;
38 extern float model_radius;
39 
40 extern const int MAX_ARC_SEGMENT_POINTS;
41 extern int Num_arc_segment_points;
42 extern vec3d Arc_segment_points[];
43 
44 extern bool Scene_framebuffer_in_frame;
45 color Wireframe_color;
46 
47 extern void interp_render_arc_segment( vec3d *v1, vec3d *v2, int depth );
48 
49 model_batch_buffer TransformBufferHandler;
50 
model_render_params()51 model_render_params::model_render_params() :
52 	Model_flags(MR_NORMAL),
53 	Debug_flags(0),
54 	Objnum(-1),
55 	Detail_level_locked(-1),
56 	Depth_scale(1500.0f),
57 	Warp_bitmap(-1),
58 	Warp_alpha(-1.0f),
59 	Xparent_alpha(1.0f),
60 	Forced_bitmap(-1),
61 	Insignia_bitmap(-1),
62 	Replacement_textures(NULL),
63 	Manage_replacement_textures(false),
64 	Team_color_set(false),
65 	Clip_plane_set(false),
66 	Animated_effect(-1),
67 	Animated_timer(0.0f),
68 	Thruster_info(),
69 	Normal_alpha(false)
70 {
71 	Warp_scale.xyz.x = 1.0f;
72 	Warp_scale.xyz.y = 1.0f;
73 	Warp_scale.xyz.z = 1.0f;
74 
75 	Clip_normal = vmd_zero_vector;
76 	Clip_pos = vmd_zero_vector;
77 
78 	if ( !Model_texturing )
79 		Model_flags |= MR_NO_TEXTURING;
80 
81 	if ( !Model_polys )	{
82 		Model_flags |= MR_NO_POLYS;
83 	}
84 
85 	gr_init_color(&Color, 0, 0, 0);
86 }
87 
~model_render_params()88 model_render_params::~model_render_params()
89 {
90 	if (Manage_replacement_textures)
91 		vm_free(Replacement_textures);
92 }
93 
get_model_flags()94 uint model_render_params::get_model_flags()
95 {
96 	return Model_flags;
97 }
98 
get_debug_flags()99 uint model_render_params::get_debug_flags()
100 {
101 	return Debug_flags;
102 }
103 
get_object_number()104 int model_render_params::get_object_number()
105 {
106 	return Objnum;
107 }
108 
get_detail_level_lock()109 int model_render_params::get_detail_level_lock()
110 {
111 	return Detail_level_locked;
112 }
113 
get_depth_scale()114 float model_render_params::get_depth_scale()
115 {
116 	return Depth_scale;
117 }
118 
get_warp_bitmap()119 int model_render_params::get_warp_bitmap()
120 {
121 	return Warp_bitmap;
122 }
123 
get_warp_alpha()124 float model_render_params::get_warp_alpha()
125 {
126 	return Warp_alpha;
127 }
128 
get_warp_scale()129 const vec3d& model_render_params::get_warp_scale()
130 {
131 	return Warp_scale;
132 }
133 
get_color()134 const color& model_render_params::get_color()
135 {
136 	return Color;
137 }
get_alpha()138 float model_render_params::get_alpha()
139 {
140 	return Xparent_alpha;
141 }
142 
get_forced_bitmap()143 int model_render_params::get_forced_bitmap()
144 {
145 	return Forced_bitmap;
146 }
147 
get_insignia_bitmap()148 int model_render_params::get_insignia_bitmap()
149 {
150 	return Insignia_bitmap;
151 }
152 
get_replacement_textures()153 const int* model_render_params::get_replacement_textures()
154 {
155 	return Replacement_textures;
156 }
157 
get_team_color()158 const team_color& model_render_params::get_team_color()
159 {
160 	return Current_team_color;
161 }
162 
get_clip_plane_pos()163 const vec3d& model_render_params::get_clip_plane_pos()
164 {
165 	return Clip_pos;
166 }
167 
get_clip_plane_normal()168 const vec3d& model_render_params::get_clip_plane_normal()
169 {
170 	return Clip_normal;
171 }
172 
get_animated_effect_num()173 int model_render_params::get_animated_effect_num()
174 {
175 	return Animated_effect;
176 }
177 
get_animated_effect_timer()178 float model_render_params::get_animated_effect_timer()
179 {
180 	return Animated_timer;
181 }
182 
set_animated_effect(int effect_num,float timer)183 void model_render_params::set_animated_effect(int effect_num, float timer)
184 {
185 	Animated_effect = effect_num;
186 	Animated_timer = timer;
187 }
188 
set_clip_plane(vec3d & pos,vec3d & normal)189 void model_render_params::set_clip_plane(vec3d &pos, vec3d &normal)
190 {
191 	Clip_plane_set = true;
192 
193 	Clip_normal = normal;
194 	Clip_pos = pos;
195 }
196 
is_clip_plane_set()197 bool model_render_params::is_clip_plane_set()
198 {
199 	return Clip_plane_set;
200 }
201 
set_team_color(team_color & clr)202 void model_render_params::set_team_color(team_color &clr)
203 {
204 	Team_color_set = true;
205 
206 	Current_team_color = clr;
207 }
208 
set_team_color(const SCP_string & team,const SCP_string & secondaryteam,fix timestamp,int fadetime)209 void model_render_params::set_team_color(const SCP_string &team, const SCP_string &secondaryteam, fix timestamp, int fadetime)
210 {
211 	Team_color_set = model_get_team_color(&Current_team_color, team, secondaryteam, timestamp, fadetime);
212 }
213 
is_team_color_set()214 bool model_render_params::is_team_color_set()
215 {
216 	return Team_color_set;
217 }
218 
set_replacement_textures(int * textures)219 void model_render_params::set_replacement_textures(int *textures)
220 {
221 	Replacement_textures = textures;
222 }
223 
set_replacement_textures(int modelnum,SCP_vector<texture_replace> & replacement_textures)224 void model_render_params::set_replacement_textures(int modelnum, SCP_vector<texture_replace>& replacement_textures)
225 {
226 	Replacement_textures = (int*)vm_malloc(MAX_REPLACEMENT_TEXTURES * sizeof(int));
227 
228 	for (int i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
229 		Replacement_textures[i] = -1;
230 
231 	Manage_replacement_textures = true;
232 
233 	polymodel* pm = model_get(modelnum);
234 
235 	for (auto tr : replacement_textures)
236 	{
237 		for (int i = 0; i < pm->n_textures; ++i)
238 		{
239 			texture_map *tmap = &pm->maps[i];
240 
241 			int tnum = tmap->FindTexture(tr.old_texture);
242 			if (tnum > -1)
243 				Replacement_textures[i * TM_NUM_TYPES + tnum] = bm_load(tr.new_texture);
244 		}
245 	}
246 }
247 
set_insignia_bitmap(int bitmap)248 void model_render_params::set_insignia_bitmap(int bitmap)
249 {
250 	Insignia_bitmap = bitmap;
251 }
252 
set_forced_bitmap(int bitmap)253 void model_render_params::set_forced_bitmap(int bitmap)
254 {
255 	Forced_bitmap = bitmap;
256 }
257 
set_alpha(float alpha)258 void model_render_params::set_alpha(float alpha)
259 {
260 	Xparent_alpha = alpha;
261 }
262 
set_color(color & clr)263 void model_render_params::set_color(color &clr)
264 {
265 	Color = clr;
266 }
267 
set_color(int r,int g,int b)268 void model_render_params::set_color(int r, int g, int b)
269 {
270 	gr_init_color( &Color, r, g, b );
271 }
272 
set_warp_params(int bitmap,float alpha,vec3d & scale)273 void model_render_params::set_warp_params(int bitmap, float alpha, vec3d &scale)
274 {
275 	Warp_bitmap = bitmap;
276 	Warp_alpha = alpha;
277 	Warp_scale = scale;
278 }
279 
set_depth_scale(float scale)280 void model_render_params::set_depth_scale(float scale)
281 {
282 	Depth_scale = scale;
283 }
284 
set_debug_flags(uint flags)285 void model_render_params::set_debug_flags(uint flags)
286 {
287 	Debug_flags = flags;
288 }
289 
set_object_number(int num)290 void model_render_params::set_object_number(int num)
291 {
292 	Objnum = num;
293 }
294 
set_flags(uint flags)295 void model_render_params::set_flags(uint flags)
296 {
297 	Model_flags = flags;
298 }
299 
set_detail_level_lock(int detail_level_lock)300 void model_render_params::set_detail_level_lock(int detail_level_lock)
301 {
302 	Detail_level_locked = detail_level_lock;
303 }
304 
set_thruster_info(mst_info & info)305 void model_render_params::set_thruster_info(mst_info &info)
306 {
307 	Thruster_info = info;
308 
309 	CLAMP(Thruster_info.length.xyz.z, 0.1f, 1.0f);
310 }
311 
get_thruster_info()312 const mst_info& model_render_params::get_thruster_info()
313 {
314 	return Thruster_info;
315 }
316 
set_normal_alpha(float min,float max)317 void model_render_params::set_normal_alpha(float min, float max)
318 {
319 	Normal_alpha = true;
320 	Normal_alpha_min = min;
321 	Normal_alpha_max = max;
322 }
323 
is_normal_alpha_set()324 bool model_render_params::is_normal_alpha_set()
325 {
326 	return Normal_alpha;
327 }
328 
get_normal_alpha_min()329 float model_render_params::get_normal_alpha_min()
330 {
331 	return Normal_alpha_min;
332 }
333 
get_normal_alpha_max()334 float model_render_params::get_normal_alpha_max()
335 {
336 	return Normal_alpha_max;
337 }
338 
set_outline_thickness(float thickness)339 void model_render_params::set_outline_thickness(float thickness) {
340 	Outline_thickness = thickness;
341 }
get_outline_thickness()342 float model_render_params::get_outline_thickness() {
343 	return Outline_thickness;
344 }
uses_thick_outlines()345 bool model_render_params::uses_thick_outlines() {
346 	return Outline_thickness > 0.0f;
347 }
348 
reset()349 void model_batch_buffer::reset()
350 {
351 	Submodel_matrices.clear();
352 
353 	Current_offset = 0;
354 }
355 
set_num_models(int n_models)356 void model_batch_buffer::set_num_models(int n_models)
357 {
358 	matrix4 init_mat;
359 
360 	vm_matrix4_set_identity(&init_mat);
361 
362 	Current_offset = Submodel_matrices.size();
363 
364 	for ( int i = 0; i < n_models; ++i ) {
365 		Submodel_matrices.push_back(init_mat);
366 	}
367 }
368 
set_model_transform(matrix4 & transform,int model_id)369 void model_batch_buffer::set_model_transform(matrix4 &transform, int model_id)
370 {
371 	Submodel_matrices[Current_offset + model_id] = transform;
372 }
373 
add_matrix(matrix4 & mat)374 void model_batch_buffer::add_matrix(matrix4 &mat)
375 {
376 	Submodel_matrices.push_back(mat);
377 }
378 
get_buffer_offset()379 size_t model_batch_buffer::get_buffer_offset()
380 {
381 	return Current_offset;
382 }
383 
allocate_memory()384 void model_batch_buffer::allocate_memory()
385 {
386 	auto size = Submodel_matrices.size() * sizeof(matrix4);
387 
388 	if ( Mem_alloc == NULL || Mem_alloc_size < size ) {
389 		if ( Mem_alloc != NULL ) {
390 			vm_free(Mem_alloc);
391 		}
392 
393 		Mem_alloc = vm_malloc(size);
394 	}
395 
396 	Mem_alloc_size = size;
397 	memcpy(Mem_alloc, &Submodel_matrices[0], size);
398 }
399 
submit_buffer_data()400 void model_batch_buffer::submit_buffer_data()
401 {
402 	if ( Submodel_matrices.empty() ) {
403 		return;
404 	}
405 
406 	allocate_memory();
407 
408 	gr_update_transform_buffer(Mem_alloc, Mem_alloc_size);
409 }
410 
model_draw_list()411 model_draw_list::model_draw_list():
412 Transformations()
413 {
414 	reset();
415 }
416 
reset()417 void model_draw_list::reset()
418 {
419 	Render_elements.clear();
420 	Render_keys.clear();
421 
422 	Transformations.clear();
423 
424 	Current_scale.xyz.x = 1.0f;
425 	Current_scale.xyz.y = 1.0f;
426 	Current_scale.xyz.z = 1.0f;
427 
428 	Render_initialized = false;
429 }
430 
sort_draws()431 void model_draw_list::sort_draws()
432 {
433 	std::sort(Render_keys.begin(), Render_keys.end(),
434 			  [this](const int a, const int b) { return model_draw_list::sort_draw_pair(this, a, b); });
435 }
436 
start_model_batch(int n_models)437 void model_draw_list::start_model_batch(int n_models)
438 {
439 	TransformBufferHandler.set_num_models(n_models);
440 }
441 
add_submodel_to_batch(int model_num)442 void model_draw_list::add_submodel_to_batch(int model_num)
443 {
444 	matrix4 transform;
445 
446 	transform = Transformations.get_transform();
447 
448 	// set scale
449 	vm_vec_scale(&transform.vec.rvec, Current_scale.xyz.x);
450 	vm_vec_scale(&transform.vec.uvec, Current_scale.xyz.y);
451 	vm_vec_scale(&transform.vec.fvec, Current_scale.xyz.z);
452 
453 	// set visibility
454 	transform.a1d[15] = 0.0f;
455 
456 	TransformBufferHandler.set_model_transform(transform, model_num);
457 }
458 
add_arc(vec3d * v1,vec3d * v2,color * primary,color * secondary,float arc_width)459 void model_draw_list::add_arc(vec3d *v1, vec3d *v2, color *primary, color *secondary, float arc_width)
460 {
461 	arc_effect new_arc;
462 
463 	new_arc.transform = Transformations.get_transform();
464 	new_arc.v1 = *v1;
465 	new_arc.v2 = *v2;
466 	new_arc.primary = *primary;
467 	new_arc.secondary = *secondary;
468 	new_arc.width = arc_width;
469 
470 	Arcs.push_back(new_arc);
471 }
472 
set_light_filter(int objnum,vec3d * pos,float rad)473 void model_draw_list::set_light_filter(int objnum, vec3d *pos, float rad)
474 {
475 	Scene_light_handler.setLightFilter(objnum, pos, rad);
476 
477 	Current_lights_set = Scene_light_handler.bufferLights();
478 }
479 
add_buffer_draw(model_material * render_material,indexed_vertex_source * vert_src,vertex_buffer * buffer,size_t texi,uint tmap_flags)480 void model_draw_list::add_buffer_draw(model_material *render_material, indexed_vertex_source *vert_src, vertex_buffer *buffer, size_t texi, uint tmap_flags)
481 {
482 	queued_buffer_draw draw_data;
483 	draw_data.render_material = *render_material;
484 
485 	if (Rendering_to_shadow_map) {
486 		draw_data.render_material.set_shadow_casting(true);
487 	} else {
488 		// If the zbuffer type is FULL then this buffer may be drawn in the deferred lighting part otherwise we need to
489 		// make sure that the deferred flag is disabled or else some parts of the rendered colors go missing
490 		// TODO: This should really be handled somewhere else. This feels like a crude hack...
491 		auto possibly_deferred = draw_data.render_material.get_depth_mode() == ZBUFFER_TYPE_FULL
492 			&& gr_is_capable(CAPABILITY_DEFERRED_LIGHTING) && !Cmdline_no_deferred_lighting;
493 
494 		if (possibly_deferred) {
495 			// Fog is handled differently in deferred shader situations
496 			draw_data.render_material.set_fog();
497 		}
498 
499 		draw_data.render_material.set_deferred_lighting(possibly_deferred ? Deferred_lighting : false);
500 		draw_data.render_material.set_high_dynamic_range(High_dynamic_range);
501 		draw_data.render_material.set_shadow_receiving(Shadow_quality != ShadowQuality::Disabled);
502 	}
503 
504 	if (tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS && buffer->flags & VB_FLAG_MODEL_ID) {
505 		vm_matrix4_set_identity(&draw_data.transform);
506 
507 		draw_data.scale.xyz.x = 1.0f;
508 		draw_data.scale.xyz.y = 1.0f;
509 		draw_data.scale.xyz.z = 1.0f;
510 
511 		draw_data.transform_buffer_offset = TransformBufferHandler.get_buffer_offset();
512 
513 		draw_data.render_material.set_batching(true);
514 	} else {
515 		draw_data.transform = Transformations.get_transform();
516 		draw_data.scale = Current_scale;
517 		draw_data.transform_buffer_offset = INVALID_SIZE;
518 		draw_data.render_material.set_batching(false);
519 	}
520 
521 	draw_data.sdr_flags = draw_data.render_material.get_shader_flags();
522 
523 	draw_data.vert_src = vert_src;
524 	draw_data.buffer = buffer;
525 	draw_data.texi = texi;
526 	draw_data.flags = tmap_flags;
527 	draw_data.lights = Current_lights_set;
528 
529 	Render_elements.push_back(draw_data);
530 	Render_keys.push_back((int) (Render_elements.size() - 1));
531 }
532 
render_buffer(queued_buffer_draw & render_elements)533 void model_draw_list::render_buffer(queued_buffer_draw &render_elements)
534 {
535 	GR_DEBUG_SCOPE("Render buffer");
536 	TRACE_SCOPE(tracing::RenderBuffer);
537 
538 	gr_bind_uniform_buffer(uniform_block_type::ModelData, render_elements.uniform_buffer_offset,
539 	                       sizeof(graphics::model_uniform_data), _dataBuffer.bufferHandle());
540 
541 	gr_render_model(&render_elements.render_material, render_elements.vert_src, render_elements.buffer, render_elements.texi);
542 }
543 
get_view_position()544 vec3d model_draw_list::get_view_position()
545 {
546 	matrix basis_world;
547 	matrix4 transform_mat = Transformations.get_transform();
548 	matrix orient;
549 	vec3d pos;
550 
551 	vm_matrix4_get_orientation(&orient, &transform_mat);
552 	vm_matrix4_get_offset(&pos, &transform_mat);
553 
554 	// get the world basis of our current local space.
555 	vm_matrix_x_matrix(&basis_world, &Object_matrix, &orient);
556 
557 	vec3d eye_pos_local;
558 	vm_vec_sub(&eye_pos_local, &Eye_position, &pos);
559 
560 	vec3d return_val;
561 	vm_vec_rotate(&return_val, &eye_pos_local, &basis_world);
562 
563 	return return_val;
564 }
565 
push_transform(vec3d * pos,matrix * orient)566 void model_draw_list::push_transform(vec3d *pos, matrix *orient)
567 {
568 	Transformations.push(pos, orient);
569 }
570 
pop_transform()571 void model_draw_list::pop_transform()
572 {
573 	Transformations.pop();
574 }
575 
set_scale(vec3d * scale)576 void model_draw_list::set_scale(vec3d *scale)
577 {
578 	if ( scale == NULL ) {
579 		Current_scale.xyz.x = 1.0f;
580 		Current_scale.xyz.y = 1.0f;
581 		Current_scale.xyz.z = 1.0f;
582 		return;
583 	}
584 
585 	Current_scale = *scale;
586 }
587 
init()588 void model_draw_list::init()
589 {
590 	reset();
591 
592 	for (auto& l : Lights) {
593 		if ( l.type == Light_Type::Directional || !Deferred_lighting ) {
594 			Scene_light_handler.addLight(&l);
595 		}
596 	}
597 
598 	TransformBufferHandler.reset();
599 }
600 
init_render(bool sort)601 void model_draw_list::init_render(bool sort)
602 {
603 	if ( sort ) {
604 		sort_draws();
605 	}
606 
607 	TransformBufferHandler.submit_buffer_data();
608 
609 	build_uniform_buffer();
610 
611 	Render_initialized = true;
612 }
613 
render_all(gr_zbuffer_type depth_mode)614 void model_draw_list::render_all(gr_zbuffer_type depth_mode)
615 {
616 	GR_DEBUG_SCOPE("Render draw list");
617 	TRACE_SCOPE(tracing::SubmitDraws);
618 
619 	Assertion(Render_initialized, "init_render must be called before any render_all call!");
620 
621 	Scene_light_handler.resetLightState();
622 
623 	for ( size_t i = 0; i < Render_keys.size(); ++i ) {
624 		int render_index = Render_keys[i];
625 
626 		if ( depth_mode == ZBUFFER_TYPE_DEFAULT || Render_elements[render_index].render_material.get_depth_mode() == depth_mode ) {
627 			render_buffer(Render_elements[render_index]);
628 		}
629 	}
630 
631 	gr_alpha_mask_set(0, 1.0f);
632 }
633 
render_arc(arc_effect & arc)634 void model_draw_list::render_arc(arc_effect &arc)
635 {
636 	g3_start_instance_matrix(&arc.transform);
637 
638 	model_render_arc(&arc.v1, &arc.v2, &arc.primary, &arc.secondary, arc.width);
639 
640 	g3_done_instance(true);
641 }
642 
render_arcs()643 void model_draw_list::render_arcs()
644 {
645 	int mode = gr_zbuffer_set(GR_ZBUFF_READ);
646 
647 	for ( size_t i = 0; i < Arcs.size(); ++i ) {
648 		render_arc(Arcs[i]);
649 	}
650 
651 	gr_zbuffer_set(mode);
652 }
653 
add_insignia(model_render_params * params,polymodel * pm,int detail_level,int bitmap_num)654 void model_draw_list::add_insignia(model_render_params *params, polymodel *pm, int detail_level, int bitmap_num)
655 {
656 	insignia_draw_data new_insignia;
657 
658 	new_insignia.transform = Transformations.get_transform();
659 	new_insignia.pm = pm;
660 	new_insignia.detail_level = detail_level;
661 	new_insignia.bitmap_num = bitmap_num;
662 
663 	new_insignia.clip = params->is_clip_plane_set();
664 	new_insignia.clip_normal = params->get_clip_plane_normal();
665 	new_insignia.clip_position = params->get_clip_plane_pos();
666 
667 	Insignias.push_back(new_insignia);
668 }
669 
render_insignia(insignia_draw_data & insignia_info)670 void model_draw_list::render_insignia(insignia_draw_data &insignia_info)
671 {
672 	if ( insignia_info.clip ) {
673 		vec3d tmp;
674 		vec3d pos;
675 
676 		vm_matrix4_get_offset(&pos, &insignia_info.transform);
677 		vm_vec_sub(&tmp, &pos, &insignia_info.clip_position);
678 		vm_vec_normalize(&tmp);
679 
680 		if ( vm_vec_dot(&tmp, &insignia_info.clip_normal) < 0.0f) {
681 			return;
682 		}
683 	}
684 
685 	g3_start_instance_matrix(&insignia_info.transform);
686 
687 	model_render_insignias(&insignia_info);
688 
689 	g3_done_instance(true);
690 }
691 
render_insignias()692 void model_draw_list::render_insignias()
693 {
694 	for ( size_t i = 0; i < Insignias.size(); ++i ) {
695 		render_insignia(Insignias[i]);
696 	}
697 }
698 
add_outline(vertex * vert_array,int n_verts,color * clr)699 void model_draw_list::add_outline(vertex* vert_array, int n_verts, color *clr)
700 {
701 	outline_draw draw_info;
702 
703 	draw_info.vert_array = vert_array;
704 	draw_info.n_verts = n_verts;
705 	draw_info.clr = *clr;
706 	draw_info.transform = Transformations.get_transform();
707 
708 	Outlines.push_back(draw_info);
709 }
710 
render_outlines()711 void model_draw_list::render_outlines()
712 {
713 	gr_clear_states();
714 
715 	for ( size_t i = 0; i < Outlines.size(); ++i ) {
716 		render_outline(Outlines[i]);
717 	}
718 }
719 
render_outline(outline_draw & outline_info)720 void model_draw_list::render_outline(outline_draw &outline_info)
721 {
722 	g3_start_instance_matrix(&outline_info.transform);
723 
724 	material material_instance;
725 
726 	material_instance.set_depth_mode(ZBUFFER_TYPE_READ);
727 	material_instance.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA);
728 	material_instance.set_color(outline_info.clr);
729 
730 	g3_render_primitives(&material_instance, outline_info.vert_array, outline_info.n_verts, PRIM_TYPE_LINES, false);
731 
732 	g3_done_instance(true);
733 }
734 
sort_draw_pair(model_draw_list * target,const int a,const int b)735 bool model_draw_list::sort_draw_pair(model_draw_list* target, const int a, const int b)
736 {
737 	queued_buffer_draw *draw_call_a = &target->Render_elements[a];
738 	queued_buffer_draw *draw_call_b = &target->Render_elements[b];
739 
740 	if ( draw_call_a->sdr_flags != draw_call_b->sdr_flags ) {
741 		return draw_call_a->sdr_flags < draw_call_b->sdr_flags;
742 	}
743 
744 	if ( draw_call_a->vert_src->Vbuffer_handle != draw_call_b->vert_src->Vbuffer_handle ) {
745 		return draw_call_a->vert_src->Vbuffer_handle.value() < draw_call_b->vert_src->Vbuffer_handle.value();
746 	}
747 
748 	if ( draw_call_a->vert_src->Ibuffer_handle != draw_call_b->vert_src->Ibuffer_handle ) {
749 		return draw_call_a->vert_src->Ibuffer_handle.value() < draw_call_b->vert_src->Ibuffer_handle.value();
750 	}
751 
752 	if ( draw_call_a->render_material.get_texture_map(TM_BASE_TYPE) != draw_call_b->render_material.get_texture_map(TM_BASE_TYPE) ) {
753 		return draw_call_a->render_material.get_texture_map(TM_BASE_TYPE) < draw_call_b->render_material.get_texture_map(TM_BASE_TYPE);
754 	}
755 
756 	if ( draw_call_a->render_material.get_texture_map(TM_SPECULAR_TYPE) != draw_call_b->render_material.get_texture_map(TM_SPECULAR_TYPE) ) {
757 		return draw_call_a->render_material.get_texture_map(TM_SPECULAR_TYPE) < draw_call_b->render_material.get_texture_map(TM_SPECULAR_TYPE);
758 	}
759 
760 	if ( draw_call_a->render_material.get_texture_map(TM_SPEC_GLOSS_TYPE) != draw_call_b->render_material.get_texture_map(TM_SPEC_GLOSS_TYPE) ) {
761 		return draw_call_a->render_material.get_texture_map(TM_SPEC_GLOSS_TYPE) < draw_call_b->render_material.get_texture_map(TM_SPEC_GLOSS_TYPE);
762 	}
763 
764 	if ( draw_call_a->render_material.get_texture_map(TM_GLOW_TYPE) != draw_call_b->render_material.get_texture_map(TM_GLOW_TYPE) ) {
765 		return draw_call_a->render_material.get_texture_map(TM_GLOW_TYPE) < draw_call_b->render_material.get_texture_map(TM_GLOW_TYPE);
766 	}
767 
768 	if ( draw_call_a->render_material.get_texture_map(TM_NORMAL_TYPE) != draw_call_b->render_material.get_texture_map(TM_NORMAL_TYPE) ) {
769 		return draw_call_a->render_material.get_texture_map(TM_NORMAL_TYPE) < draw_call_b->render_material.get_texture_map(TM_NORMAL_TYPE);
770 	}
771 
772 	if ( draw_call_a->render_material.get_texture_map(TM_HEIGHT_TYPE) != draw_call_b->render_material.get_texture_map(TM_HEIGHT_TYPE) ) {
773 		return draw_call_a->render_material.get_texture_map(TM_HEIGHT_TYPE) < draw_call_b->render_material.get_texture_map(TM_HEIGHT_TYPE);
774 	}
775 
776 	if ( draw_call_a->render_material.get_texture_map(TM_AMBIENT_TYPE) != draw_call_b->render_material.get_texture_map(TM_AMBIENT_TYPE) ) {
777 		return draw_call_a->render_material.get_texture_map(TM_AMBIENT_TYPE) < draw_call_b->render_material.get_texture_map(TM_AMBIENT_TYPE);
778 	}
779 
780 	if ( draw_call_a->render_material.get_texture_map(TM_MISC_TYPE) != draw_call_b->render_material.get_texture_map(TM_MISC_TYPE) ) {
781 		return draw_call_a->render_material.get_texture_map(TM_MISC_TYPE) < draw_call_b->render_material.get_texture_map(TM_MISC_TYPE);
782 	}
783 
784 	return draw_call_a->lights.index_start < draw_call_b->lights.index_start;
785 }
build_uniform_buffer()786 void model_draw_list::build_uniform_buffer() {
787 	GR_DEBUG_SCOPE("Build model uniform buffer");
788 
789 	TRACE_SCOPE(tracing::BuildModelUniforms);
790 
791 	_dataBuffer = gr_get_uniform_buffer(uniform_block_type::ModelData, Render_keys.size());
792 
793 	for (auto render_index : Render_keys) {
794 		auto& queued_draw = Render_elements[render_index];
795 
796 		// Set lighting here so that it can be captured by the uniform conversion below
797 		if ( queued_draw.render_material.is_lit() ) {
798 			Scene_light_handler.setLights(&queued_draw.lights);
799 		} else {
800 			gr_set_lighting(false, false);
801 
802 			Scene_light_handler.resetLightState();
803 		}
804 
805 		auto element = _dataBuffer.aligner().addTypedElement<graphics::model_uniform_data>();
806 		graphics::uniforms::convert_model_material(element,
807 												   queued_draw.render_material,
808 												   queued_draw.transform,
809 												   queued_draw.scale,
810 												   queued_draw.transform_buffer_offset);
811 		queued_draw.uniform_buffer_offset = _dataBuffer.getCurrentAlignerOffset();
812 	}
813 
814 	TRACE_SCOPE(tracing::UploadModelUniforms);
815 
816 	_dataBuffer.submitData();
817 }
~model_draw_list()818 model_draw_list::~model_draw_list() {
819 	reset();
820 }
821 
model_render_add_lightning(model_draw_list * scene,model_render_params * interp,polymodel * pm,submodel_instance * smi)822 void model_render_add_lightning( model_draw_list *scene, model_render_params* interp, polymodel *pm, submodel_instance *smi )
823 {
824 	int i;
825 	float width = 0.9f;
826 	color primary, secondary;
827 
828 	Assert( smi->num_arcs > 0 );
829 
830 	if ( interp->get_model_flags() & MR_SHOW_OUTLINE_PRESET ) {
831 		return;
832 	}
833 
834 	extern int Interp_lightning;
835 	if ( !Interp_lightning ) {
836  		return;
837  	}
838 
839 	// try and scale the size a bit so that it looks equally well on smaller vessels
840 	if ( pm->rad < 500.0f ) {
841 		width *= (pm->rad * 0.01f);
842 
843 		if ( width < 0.2f ) {
844 			width = 0.2f;
845 		}
846 	}
847 
848 	for ( i = 0; i < smi->num_arcs; i++ ) {
849 		// pick a color based upon arc type
850 		switch ( smi->arc_type[i] ) {
851 			// "normal", FreeSpace 1 style arcs
852 		case MARC_TYPE_NORMAL:
853 			if ( Random::flip_coin() )	{
854 				gr_init_color(&primary, std::get<0>(Arc_color_damage_p1), std::get<1>(Arc_color_damage_p1), std::get<2>(Arc_color_damage_p1));
855 			} else {
856 				gr_init_color(&primary, std::get<0>(Arc_color_damage_p2), std::get<1>(Arc_color_damage_p2), std::get<2>(Arc_color_damage_p2));
857 			}
858 
859 			gr_init_color(&primary, std::get<0>(Arc_color_damage_s1), std::get<1>(Arc_color_damage_s1), std::get<2>(Arc_color_damage_s1));
860 			break;
861 
862 			// "EMP" style arcs
863 		case MARC_TYPE_EMP:
864 			if ( Random::flip_coin() )	{
865 				gr_init_color(&primary, std::get<0>(Arc_color_emp_p1), std::get<1>(Arc_color_emp_p1), std::get<2>(Arc_color_emp_p1));
866 			} else {
867 				gr_init_color(&primary, std::get<0>(Arc_color_emp_p2), std::get<1>(Arc_color_emp_p2), std::get<2>(Arc_color_emp_p2));
868 			}
869 
870 			gr_init_color(&primary, std::get<0>(Arc_color_emp_s1), std::get<1>(Arc_color_emp_s1), std::get<2>(Arc_color_emp_s1));
871 			break;
872 
873 		default:
874 			Int3();
875 		}
876 
877 		// render the actual arc segment
878 		scene->add_arc(&smi->arc_pts[i][0], &smi->arc_pts[i][1], &primary, &secondary, width);
879 	}
880 }
881 
model_render_determine_depth(int obj_num,int model_num,matrix * orient,vec3d * pos,int detail_level_locked)882 float model_render_determine_depth(int obj_num, int model_num, matrix* orient, vec3d* pos, int detail_level_locked)
883 {
884 	vec3d closest_pos;
885 	float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
886 
887 	if ( detail_level_locked < 0 ) {
888 		switch (Detail.detail_distance) {
889 		case 0:		// lowest
890 			depth /= The_mission.ai_profile->detail_distance_mult[0];
891 			break;
892 		case 1:		// lower than normal
893 			depth /= The_mission.ai_profile->detail_distance_mult[1];
894 			break;
895 		case 2:		// default
896 			depth /= The_mission.ai_profile->detail_distance_mult[2];
897 			break;
898 		case 3:		// above normal
899 			depth /= The_mission.ai_profile->detail_distance_mult[3];
900 			break;
901 		case 4:		// even more normal
902 			depth /= The_mission.ai_profile->detail_distance_mult[4];
903 			break;
904 		}
905 
906 		// nebula ?
907 		if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
908 			depth *= neb2_get_lod_scale(obj_num);
909 		}
910 
911 	}
912 
913 	return depth;
914 }
915 
model_render_determine_detail(float depth,int,int model_num,matrix *,vec3d *,int,int detail_level_locked)916 int model_render_determine_detail(float depth, int  /*obj_num*/, int model_num, matrix*  /*orient*/, vec3d*  /*pos*/, int  /*flags*/, int detail_level_locked)
917 {
918 	int tmp_detail_level = Game_detail_level;
919 
920 	polymodel *pm = model_get(model_num);
921 
922 	Assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS );
923 
924 	int i;
925 
926 	if ( pm->n_detail_levels > 1 ) {
927 		if ( detail_level_locked >= 0 ) {
928 			i = detail_level_locked+1;
929 		} else {
930 
931 #if MAX_DETAIL_LEVEL != 4
932 #error Code in modelrender.cpp assumes MAX_DETAIL_LEVEL == 4
933 #endif
934 			for ( i = 0; i < pm->n_detail_levels; i++ )	{
935 				if ( depth <= pm->detail_depth[i] ) {
936 					break;
937 				}
938 			}
939 
940 			// If no valid detail depths specified, use highest.
941 			if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f) )	{
942 				i = 1;
943 			}
944 		}
945 
946 		int detail_level = i - 1 - tmp_detail_level;
947 
948 		if ( detail_level < 0 ) {
949 			return 0;
950 		} else if ( detail_level >= pm->n_detail_levels ) {
951 			return pm->n_detail_levels - 1;
952 		}
953 
954 		return detail_level;
955 	} else {
956 		return 0;
957 	}
958 }
959 
model_render_buffers(model_draw_list * scene,model_material * rendering_material,model_render_params * interp,vertex_buffer * buffer,polymodel * pm,int mn,int detail_level,uint tmap_flags)960 void model_render_buffers(model_draw_list* scene, model_material *rendering_material, model_render_params* interp, vertex_buffer *buffer, polymodel *pm, int mn, int detail_level, uint tmap_flags)
961 {
962 	bsp_info *model = NULL;
963 	const uint model_flags = interp->get_model_flags();
964 	const uint debug_flags = interp->get_debug_flags();
965 	const int obj_num = interp->get_object_number();
966 
967 	Assert(buffer != NULL);
968 	Assert(detail_level >= 0);
969 
970 	if ( (mn >= 0) && (mn < pm->n_models) ) {
971 		model = &pm->submodel[mn];
972 	}
973 
974 	bool render_as_thruster = (model != NULL) && model->is_thruster && (model_flags & MR_SHOW_THRUSTERS);
975 
976 	vec3d scale;
977 
978 	if ( render_as_thruster ) {
979 		scale.xyz.x = 1.0f;
980 		scale.xyz.y = 1.0f;
981 
982 		scale.xyz.z = 1.0f;
983 		rendering_material->set_thrust_scale(interp->get_thruster_info().length.xyz.z);
984 	} else {
985 		scale = interp->get_warp_scale();
986 		rendering_material->set_thrust_scale();
987 	}
988 
989 	scene->set_scale(&scale);
990 
991 	if ( tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS && (mn >= 0) && (mn < pm->n_models) ) {
992 		scene->add_submodel_to_batch(mn);
993 		return;
994 	}
995 
996 	fix base_frametime = model_render_determine_base_frametime(obj_num, model_flags);
997 
998 	texture_info tex_replace[TM_NUM_TYPES];
999 
1000 	int no_texturing = model_flags & MR_NO_TEXTURING;
1001 
1002 	int forced_texture = -2;
1003 	float forced_alpha = 1.0f;
1004 	int forced_blend_filter = GR_ALPHABLEND_NONE;
1005 
1006 	if ( interp->get_forced_bitmap() >= 0 ) {
1007 		forced_texture = interp->get_forced_bitmap();
1008 	} else if ( interp->get_warp_bitmap() >= 0 ) {
1009 		forced_texture = interp->get_warp_bitmap();
1010 		forced_alpha = interp->get_warp_alpha();
1011 		forced_blend_filter = GR_ALPHABLEND_FILTER;
1012 	} else if ( render_as_thruster ) {
1013 		if ( ( interp->get_thruster_info().primary_bitmap >= 0 ) && ( interp->get_thruster_info().length.xyz.z > 0.0f ) ) {
1014 			forced_texture = interp->get_thruster_info().primary_bitmap;
1015 		} else {
1016 			forced_texture = -1;
1017 		}
1018 
1019 		forced_alpha = 1.2f;
1020 		forced_blend_filter = GR_ALPHABLEND_FILTER;
1021 	} else if ( model_flags & MR_ALL_XPARENT ) {
1022 		forced_alpha = interp->get_alpha();
1023 		forced_blend_filter = GR_ALPHABLEND_FILTER;
1024 	}
1025 
1026 	int texture_maps[TM_NUM_TYPES] = { -1 };
1027 	size_t buffer_size = buffer->tex_buf.size();
1028 	const int *replacement_textures = interp->get_replacement_textures();
1029 
1030 	for ( size_t i = 0; i < buffer_size; i++ ) {
1031 		int tmap_num = buffer->tex_buf[i].texture;
1032 		texture_map *tmap = &pm->maps[tmap_num];
1033 		int rt_begin_index = tmap_num*TM_NUM_TYPES;
1034 		float alpha = 1.0f;
1035 
1036 		texture_maps[TM_BASE_TYPE] = -1;
1037 		texture_maps[TM_GLOW_TYPE] = -1;
1038 		texture_maps[TM_SPECULAR_TYPE] = -1;
1039 		texture_maps[TM_NORMAL_TYPE] = -1;
1040 		texture_maps[TM_HEIGHT_TYPE] = -1;
1041 		texture_maps[TM_MISC_TYPE] = -1;
1042 		texture_maps[TM_SPEC_GLOSS_TYPE] = -1;
1043 		texture_maps[TM_AMBIENT_TYPE] = -1;
1044 
1045 		if (forced_texture != -2) {
1046 			texture_maps[TM_BASE_TYPE] = forced_texture;
1047 			alpha = forced_alpha;
1048 
1049 			if (interp->get_warp_bitmap() >= 0) {
1050 				texture_maps[TM_GLOW_TYPE] = forced_texture;
1051 			}
1052 
1053 		} else if ( !no_texturing ) {
1054 			// pick the texture, animating it if necessary
1055 			if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_BASE_TYPE] == REPLACE_WITH_INVISIBLE) ) {
1056 				// invisible textures aren't rendered, but we still have to skip assigning the underlying model texture
1057 				texture_maps[TM_BASE_TYPE] = -1;
1058 			} else if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0) ) {
1059 				// an underlying texture is replaced with a real new texture
1060 				tex_replace[TM_BASE_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_BASE_TYPE]);
1061 				texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], base_frametime);
1062 			} else {
1063 				// we just use the underlying texture
1064 				texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], base_frametime);
1065 			}
1066 
1067 			if ( texture_maps[TM_BASE_TYPE] < 0 ) {
1068 				continue;
1069 			}
1070 
1071 			// doing glow maps?
1072 			if ( !(model_flags & MR_NO_GLOWMAPS) ) {
1073 				texture_info *tglow = &tmap->textures[TM_GLOW_TYPE];
1074 
1075 				if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_GLOW_TYPE] >= 0) ) {
1076 					tex_replace[TM_GLOW_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_GLOW_TYPE]);
1077 					texture_maps[TM_GLOW_TYPE] = model_interp_get_texture(&tex_replace[TM_GLOW_TYPE], base_frametime);
1078 				} else if (tglow->GetTexture() >= 0) {
1079 					// shockwaves are special, their current frame has to come out of the shockwave code to get the timing correct
1080 					if ( (obj_num >= 0) && (Objects[obj_num].type == OBJ_SHOCKWAVE) && (tglow->GetNumFrames() > 1) ) {
1081 						texture_maps[TM_GLOW_TYPE] = tglow->GetTexture() + shockwave_get_framenum(Objects[obj_num].instance, tglow->GetTexture());
1082 					} else {
1083 						texture_maps[TM_GLOW_TYPE] = model_interp_get_texture(tglow, base_frametime);
1084 					}
1085 				}
1086 			}
1087 
1088 			if (!(debug_flags & MR_DEBUG_NO_SPEC)) {
1089 				if (replacement_textures != NULL && replacement_textures[rt_begin_index + TM_SPECULAR_TYPE] >= 0) {
1090 					tex_replace[TM_SPECULAR_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_SPECULAR_TYPE]);
1091 					texture_maps[TM_SPECULAR_TYPE] = model_interp_get_texture(&tex_replace[TM_SPECULAR_TYPE], base_frametime);
1092 				}
1093 				else {
1094 					texture_maps[TM_SPECULAR_TYPE] = model_interp_get_texture(&tmap->textures[TM_SPECULAR_TYPE], base_frametime);
1095 				}
1096 			}
1097 
1098 			if ( replacement_textures != NULL && replacement_textures[rt_begin_index + TM_SPEC_GLOSS_TYPE] >= 0 ) {
1099 				tex_replace[TM_SPEC_GLOSS_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_SPEC_GLOSS_TYPE]);
1100 				texture_maps[TM_SPEC_GLOSS_TYPE] = model_interp_get_texture(&tex_replace[TM_SPEC_GLOSS_TYPE], base_frametime);
1101 			} else {
1102 				texture_maps[TM_SPEC_GLOSS_TYPE] = model_interp_get_texture(&tmap->textures[TM_SPEC_GLOSS_TYPE], base_frametime);
1103 			}
1104 
1105 			if (detail_level < 2) {
1106 				// likewise, etc.
1107 				texture_info *norm_map = &tmap->textures[TM_NORMAL_TYPE];
1108 				texture_info *height_map = &tmap->textures[TM_HEIGHT_TYPE];
1109 				texture_info *ambient_map = &tmap->textures[TM_AMBIENT_TYPE];
1110 				texture_info *misc_map = &tmap->textures[TM_MISC_TYPE];
1111 
1112 				if (replacement_textures != NULL) {
1113 					if (replacement_textures[rt_begin_index + TM_NORMAL_TYPE] >= 0) {
1114 						tex_replace[TM_NORMAL_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_NORMAL_TYPE]);
1115 						norm_map = &tex_replace[TM_NORMAL_TYPE];
1116 					}
1117 
1118 					if (replacement_textures[rt_begin_index + TM_HEIGHT_TYPE] >= 0) {
1119 						tex_replace[TM_HEIGHT_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_HEIGHT_TYPE]);
1120 						height_map = &tex_replace[TM_HEIGHT_TYPE];
1121 					}
1122 
1123 					if (replacement_textures[rt_begin_index + TM_AMBIENT_TYPE] >= 0) {
1124 						tex_replace[TM_AMBIENT_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_AMBIENT_TYPE]);
1125 						ambient_map = &tex_replace[TM_AMBIENT_TYPE];
1126 					}
1127 
1128 					if (replacement_textures[rt_begin_index + TM_MISC_TYPE] >= 0) {
1129 						tex_replace[TM_MISC_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_MISC_TYPE]);
1130 						misc_map = &tex_replace[TM_MISC_TYPE];
1131 					}
1132 				}
1133 
1134 				if (debug_flags & MR_DEBUG_NO_DIFFUSE)  texture_maps[TM_BASE_TYPE] = -1;
1135 				if (debug_flags & MR_DEBUG_NO_GLOW)		  texture_maps[TM_GLOW_TYPE] = -1;
1136 				if (debug_flags & MR_DEBUG_NO_SPEC)		  texture_maps[TM_SPECULAR_TYPE] = -1;
1137 				if (debug_flags & MR_DEBUG_NO_REFLECT)	  texture_maps[TM_SPEC_GLOSS_TYPE] = -1;
1138 				if (!(debug_flags & MR_DEBUG_NO_MISC))    texture_maps[TM_MISC_TYPE] = model_interp_get_texture(misc_map, base_frametime);
1139 				if (!(debug_flags & MR_DEBUG_NO_NORMAL) && Detail.lighting > 0)  texture_maps[TM_NORMAL_TYPE] = model_interp_get_texture(norm_map, base_frametime);
1140 				if (!(debug_flags & MR_DEBUG_NO_AMBIENT) && Detail.lighting > 0) texture_maps[TM_AMBIENT_TYPE] = model_interp_get_texture(ambient_map, base_frametime);
1141 				if (!(debug_flags & MR_DEBUG_NO_HEIGHT) && Detail.lighting > 1)  texture_maps[TM_HEIGHT_TYPE] = model_interp_get_texture(height_map, base_frametime);
1142 			}
1143 		} else {
1144 			alpha = forced_alpha;
1145 
1146 			//Check for invisible or transparent textures so they don't show up in the shadow maps - Valathil
1147 			if ( Rendering_to_shadow_map ) {
1148 				if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0) ) {
1149 					tex_replace[TM_BASE_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_BASE_TYPE]);
1150 					texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], base_frametime);
1151 				} else {
1152 					texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], base_frametime);
1153 				}
1154 
1155 				if ( texture_maps[TM_BASE_TYPE] <= 0 ) {
1156 					continue;
1157 				}
1158 			}
1159 		}
1160 
1161 		if ( (texture_maps[TM_BASE_TYPE] == -1) && !no_texturing && !(debug_flags & MR_DEBUG_NO_DIFFUSE) ) {
1162 			continue;
1163 		}
1164 
1165 		bool use_blending = false;
1166 
1167 		// trying to get transparent textures-Bobboau
1168 		if (tmap->is_transparent) {
1169 			// for special shockwave/warp map usage
1170 			alpha = (interp->get_warp_alpha() != -1.0f) ? interp->get_warp_alpha() : 0.8f;
1171 			use_blending = true;
1172 		} else if ( buffer->flags & VB_FLAG_TRANS ) {
1173 			use_blending = true;
1174 		}
1175 
1176 		if (forced_blend_filter != GR_ALPHABLEND_NONE) {
1177 			use_blending = true;
1178 		}
1179 
1180 		bool use_depth_test;
1181 
1182 		if ( use_blending ) {
1183 			use_depth_test = true;
1184 		} else {
1185 			if ( (model_flags & MR_NO_ZBUFFER) || (model_flags & MR_ALL_XPARENT) ) {
1186 				use_depth_test = false;
1187 			} else {
1188 				use_depth_test = true;
1189 			}
1190 		}
1191 
1192 		gr_alpha_blend blend_mode = model_render_determine_blend_mode(texture_maps[TM_BASE_TYPE], use_blending);
1193 		gr_zbuffer_type depth_mode = material_determine_depth_mode(use_depth_test, use_blending);
1194 
1195 		rendering_material->set_depth_mode(depth_mode);
1196 		rendering_material->set_blend_mode(blend_mode);
1197 
1198 		color clr = interp->get_color();
1199 		model_render_determine_color(&clr, alpha, blend_mode, no_texturing ? true : false, rendering_material->is_desaturated());
1200 		rendering_material->set_color(clr);
1201 
1202 		if ( (tmap_flags & TMAP_FLAG_TEXTURED) && (buffer->flags & VB_FLAG_UV1) ) {
1203 			rendering_material->set_texture_map(TM_BASE_TYPE,	texture_maps[TM_BASE_TYPE]);
1204 
1205 			if ( texture_maps[TM_BASE_TYPE] >= 0 && bm_has_alpha_channel(texture_maps[TM_BASE_TYPE]) ) {
1206 				rendering_material->set_texture_type(material::TEX_TYPE_XPARENT);
1207 			}
1208 
1209 			rendering_material->set_texture_map(TM_GLOW_TYPE,	texture_maps[TM_GLOW_TYPE]);
1210 			rendering_material->set_texture_map(TM_SPECULAR_TYPE, texture_maps[TM_SPECULAR_TYPE]);
1211 			rendering_material->set_texture_map(TM_SPEC_GLOSS_TYPE, texture_maps[TM_SPEC_GLOSS_TYPE]);
1212 			rendering_material->set_texture_map(TM_NORMAL_TYPE, texture_maps[TM_NORMAL_TYPE]);
1213 			rendering_material->set_texture_map(TM_HEIGHT_TYPE, texture_maps[TM_HEIGHT_TYPE]);
1214 			rendering_material->set_texture_map(TM_AMBIENT_TYPE, texture_maps[TM_AMBIENT_TYPE]);
1215 			rendering_material->set_texture_map(TM_MISC_TYPE,	texture_maps[TM_MISC_TYPE]);
1216 		}
1217 
1218 		scene->add_buffer_draw(rendering_material, &pm->vert_source, buffer, i, tmap_flags);
1219 	}
1220 }
1221 
model_render_children_buffers(model_draw_list * scene,model_material * rendering_material,model_render_params * interp,polymodel * pm,polymodel_instance * pmi,int mn,int detail_level,uint tmap_flags,bool trans_buffer)1222 void model_render_children_buffers(model_draw_list* scene, model_material *rendering_material, model_render_params* interp, polymodel* pm, polymodel_instance *pmi, int mn, int detail_level, uint tmap_flags, bool trans_buffer)
1223 {
1224 	int i;
1225 
1226 	if ( (mn < 0) || (mn >= pm->n_models) ) {
1227 		Int3();
1228 		return;
1229 	}
1230 
1231 	bsp_info *sm = &pm->submodel[mn];
1232 	submodel_instance *smi = nullptr;
1233 
1234 	if ( pmi != nullptr ) {
1235 		smi = &pmi->submodel[mn];
1236 		if ( smi->blown_off ) {
1237 			return;
1238 		}
1239 	}
1240 
1241 	const uint model_flags = interp->get_model_flags();
1242 
1243 	if (sm->is_thruster) {
1244 		if ( !( model_flags & MR_SHOW_THRUSTERS ) ) {
1245 			return;
1246 		}
1247 
1248 		rendering_material->set_lighting(false);
1249 	}
1250 
1251 	vec3d view_pos = scene->get_view_position();
1252 
1253 	if ( !model_render_check_detail_box(&view_pos, pm, mn, model_flags) ) {
1254 		return;
1255 	}
1256 
1257 	// Get submodel rotation data and use submodel orientation matrix
1258 	// to put together a matrix describing the final orientation of
1259 	// the submodel relative to its parent
1260 	matrix submodel_orient = vmd_identity_matrix;
1261 
1262 	if ( smi != nullptr ) {
1263 		submodel_orient = smi->canonical_orient;
1264 	}
1265 
1266 	scene->push_transform(&sm->offset, &submodel_orient);
1267 
1268 	if ( (model_flags & MR_SHOW_OUTLINE || model_flags & MR_SHOW_OUTLINE_HTL || model_flags & MR_SHOW_OUTLINE_PRESET) &&
1269 		sm->outline_buffer != nullptr ) {
1270 		color outline_color = interp->get_color();
1271 		scene->add_outline(sm->outline_buffer, sm->n_verts_outline, &outline_color);
1272 	} else {
1273 		if ( trans_buffer && sm->trans_buffer.flags & VB_FLAG_TRANS ) {
1274 			model_render_buffers(scene, rendering_material, interp, &sm->trans_buffer, pm, mn, detail_level, tmap_flags);
1275 		} else {
1276 			model_render_buffers(scene, rendering_material, interp, &sm->buffer, pm, mn, detail_level, tmap_flags);
1277 		}
1278 	}
1279 
1280 	if ( smi != nullptr && smi->num_arcs > 0 ) {
1281 		model_render_add_lightning( scene, interp, pm, smi );
1282 	}
1283 
1284 	i = sm->first_child;
1285 
1286 	while ( i >= 0 ) {
1287 		if ( !pm->submodel[i].is_thruster ) {
1288 			model_render_children_buffers( scene, rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
1289 		}
1290 
1291 		i = pm->submodel[i].next_sibling;
1292 	}
1293 
1294 	if ( sm->is_thruster ) {
1295 		rendering_material->set_lighting(true);
1296 	}
1297 
1298 	scene->pop_transform();
1299 }
1300 
model_render_determine_light_factor(model_render_params * interp,vec3d * pos,uint flags)1301 float model_render_determine_light_factor(model_render_params* interp, vec3d *pos, uint flags)
1302 {
1303 	if ( flags & MR_IS_ASTEROID ) {
1304 		// Dim it based on distance
1305 		float depth = vm_vec_dist_quick( pos, &Eye_position );
1306 		if ( depth > interp->get_depth_scale() )	{
1307 			float temp_light = interp->get_depth_scale()/depth;
1308 
1309 			if ( temp_light > 1.0f )	{
1310  				return 1.0f;
1311  			}
1312 
1313 			return temp_light;
1314 		}
1315 	}
1316 
1317 	return 1.0f;
1318 }
1319 
model_render_determine_box_scale()1320 float model_render_determine_box_scale()
1321 {
1322 	float box_scale = 1.2f;
1323 
1324 	// scale the render box settings based on the "Model Detail" slider
1325 	switch ( Detail.detail_distance ) {
1326 	case 0:		// 1st dot is 20%
1327 		box_scale = 0.2f;
1328 		break;
1329 	case 1:		// 2nd dot is 50%
1330 		box_scale = 0.5f;
1331 		break;
1332 	case 2:		// 3rd dot is 80%
1333 		box_scale = 0.8f;
1334 		break;
1335 	case 3:		// 4th dot is 100% (this is the default setting for "High" and "Very High" settings)
1336 		box_scale = 1.0f;
1337 		break;
1338 	case 4:		// 5th dot (max) is 120%
1339 	default:
1340 		box_scale = 1.2f;
1341 		break;
1342 	}
1343 
1344 	return box_scale;
1345 }
1346 
model_render_determine_base_frametime(int objnum,uint flags)1347 fix model_render_determine_base_frametime(int objnum, uint flags)
1348 {
1349 	// Goober5000
1350 	fix base_frametime = 0;
1351 
1352 	if ( objnum >= 0 ) {
1353 		object *objp = &Objects[objnum];
1354 
1355 		if ( objp->type == OBJ_SHIP ) {
1356 			base_frametime = Ships[objp->instance].base_texture_anim_frametime;
1357 		}
1358 	} else if ( flags & MR_SKYBOX ) {
1359 		base_frametime = Skybox_timestamp;
1360 	}
1361 
1362 	return base_frametime;
1363 }
1364 
model_render_determine_autocenter(vec3d * auto_back,polymodel * pm,int detail_level,uint flags)1365 bool model_render_determine_autocenter(vec3d *auto_back, polymodel *pm, int detail_level, uint flags)
1366 {
1367 	if ( flags & MR_AUTOCENTER ) {
1368 		// standard autocenter using data in model
1369 		if ( pm->flags & PM_FLAG_AUTOCEN ) {
1370 			*auto_back = pm->autocenter;
1371 			vm_vec_scale(auto_back, -1.0f);
1372 			return true;
1373 		} else if ( flags & MR_IS_MISSILE ) {
1374 			// fake autocenter if we are a missile and don't already have autocen info
1375 			auto_back->xyz.x = -( (pm->submodel[pm->detail[detail_level]].max.xyz.x + pm->submodel[pm->detail[detail_level]].min.xyz.x) / 2.0f );
1376 			auto_back->xyz.y = -( (pm->submodel[pm->detail[detail_level]].max.xyz.y + pm->submodel[pm->detail[detail_level]].min.xyz.y) / 2.0f );
1377 			auto_back->xyz.z = -( (pm->submodel[pm->detail[detail_level]].max.xyz.z + pm->submodel[pm->detail[detail_level]].min.xyz.z) / 2.0f );
1378 			return true;
1379 		}
1380 	}
1381 
1382 	return false;
1383 }
1384 
model_render_determine_color(color * clr,float alpha,gr_alpha_blend blend_mode,bool no_texturing,bool desaturate)1385 void model_render_determine_color(color *clr, float alpha, gr_alpha_blend blend_mode, bool no_texturing, bool desaturate)
1386 {
1387 	clr->alpha = static_cast<ubyte>((alpha * 255.0f));
1388 
1389 	if ( no_texturing || desaturate ) {
1390 		// don't override the given color if we're not texturing or we're desaturating
1391 		return;
1392 	}
1393 
1394 	if ( blend_mode == ALPHA_BLEND_ADDITIVE ) {
1395 		clr->red = clr->green = clr->blue = clr->alpha;
1396 		clr->alpha = 255;
1397 	} else {
1398 		clr->red = clr->green = clr->blue = 255;
1399 	}
1400 }
1401 
model_render_determine_blend_mode(int base_bitmap,bool blending)1402 gr_alpha_blend model_render_determine_blend_mode(int base_bitmap, bool blending)
1403 {
1404 	if ( blending ) {
1405 		if ( base_bitmap >= 0 && bm_has_alpha_channel(base_bitmap) ) {
1406 			return ALPHA_BLEND_PREMULTIPLIED;
1407 		}
1408 
1409 		return ALPHA_BLEND_ADDITIVE;
1410 	}
1411 
1412 	return ALPHA_BLEND_ALPHA_BLEND_ALPHA;
1413 }
1414 
model_render_check_detail_box(vec3d * view_pos,polymodel * pm,int submodel_num,uint flags)1415 bool model_render_check_detail_box(vec3d *view_pos, polymodel *pm, int submodel_num, uint flags)
1416 {
1417 	Assert(pm != NULL);
1418 
1419 	bsp_info *model = &pm->submodel[submodel_num];
1420 
1421 	float box_scale = model_render_determine_box_scale();
1422 	if (model->do_not_scale_detail_distances) {
1423 		box_scale = 1.0f;
1424 	}
1425 
1426 	if ( !( flags & MR_FULL_DETAIL ) && model->use_render_box ) {
1427 		vec3d box_min, box_max, offset;
1428 
1429 		if (model->use_render_box_offset) {
1430 			offset = model->render_box_offset;
1431 		} else {
1432 			model_find_submodel_offset(&offset, pm, submodel_num);
1433 		}
1434 
1435 		vm_vec_copy_scale(&box_min, &model->render_box_min, box_scale);
1436 		vm_vec_copy_scale(&box_max, &model->render_box_max, box_scale);
1437 
1438 		if ( (-model->use_render_box + in_box(&box_min, &box_max, &offset, view_pos)) ) {
1439 			return false;
1440 		}
1441 	}
1442 
1443 	if ( !(flags & MR_FULL_DETAIL) && model->use_render_sphere ) {
1444 		float sphere_radius = model->render_sphere_radius * box_scale;
1445 
1446 		// TODO: doesn't consider submodel rotations yet -zookeeper
1447 		vec3d offset;
1448 
1449 		if (model->use_render_sphere_offset) {
1450 			offset = model->render_sphere_offset;
1451 		} else {
1452 			model_find_submodel_offset(&offset, pm, submodel_num);
1453 		}
1454 
1455 		if ( (-model->use_render_sphere + in_sphere(&offset, sphere_radius, view_pos)) ) {
1456 			return false;
1457 		}
1458 	}
1459 
1460 	return true;
1461 }
1462 
submodel_render_immediate(model_render_params * render_info,polymodel * pm,polymodel_instance * pmi,int submodel_num,matrix * orient,vec3d * pos)1463 void submodel_render_immediate(model_render_params *render_info, polymodel *pm, polymodel_instance *pmi, int submodel_num, matrix *orient, vec3d * pos)
1464 {
1465 	Assert(pm->id == pmi->model_num);
1466 
1467 	model_draw_list model_list;
1468 
1469 	model_list.init();
1470 
1471 	submodel_render_queue(render_info, &model_list, pm, pmi, submodel_num, orient, pos);
1472 
1473 	model_list.init_render();
1474 	model_list.render_all();
1475 
1476 	gr_zbias(0);
1477 	gr_zbuffer_set(ZBUFFER_TYPE_READ);
1478 	gr_set_cull(0);
1479 	gr_set_fill_mode(GR_FILL_MODE_SOLID);
1480 
1481 	gr_clear_states();
1482 
1483 	gr_reset_lighting();
1484 	gr_set_lighting(false, false);
1485 }
1486 
submodel_render_queue(model_render_params * render_info,model_draw_list * scene,polymodel * pm,polymodel_instance * pmi,int submodel_num,matrix * orient,vec3d * pos)1487 void submodel_render_queue(model_render_params *render_info, model_draw_list *scene, polymodel *pm, polymodel_instance *pmi, int submodel_num, matrix *orient, vec3d * pos)
1488 {
1489 	Assert(pm->id == pmi->model_num);
1490 
1491 	model_material rendering_material;
1492 
1493 	//MONITOR_INC( NumModelsRend, 1 );
1494 
1495 	if ( !( Game_detail_flags & DETAIL_FLAG_MODELS ) )	return;
1496 
1497 	if ( render_info->is_clip_plane_set() ) {
1498 		rendering_material.set_clip_plane(render_info->get_clip_plane_normal(), render_info->get_clip_plane_pos());
1499 	}
1500 
1501 	if ( render_info->is_team_color_set() ) {
1502 		rendering_material.set_team_color(render_info->get_team_color());
1503 	}
1504 
1505 	uint flags = render_info->get_model_flags();
1506 	int objnum = render_info->get_object_number();
1507 
1508 	// Set the flags we will pass to the tmapper
1509 	uint tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
1510 
1511 	// if we're in nebula mode
1512 	if( ( The_mission.flags[Mission::Mission_Flags::Fullneb] ) && ( Neb2_render_mode != NEB2_RENDER_NONE ) ) {
1513 		tmap_flags |= TMAP_FLAG_PIXEL_FOG;
1514 	}
1515 
1516 	if ( !( flags & MR_NO_TEXTURING ) )	{
1517 		tmap_flags |= TMAP_FLAG_TEXTURED;
1518 
1519 		if ( ( pm->flags & PM_FLAG_ALLOW_TILING ) && tiling )
1520 			tmap_flags |= TMAP_FLAG_TILED;
1521 
1522 		if ( !( flags & MR_NO_CORRECT ) )	{
1523 			tmap_flags |= TMAP_FLAG_CORRECT;
1524 		}
1525 	}
1526 
1527 	if ( render_info->get_animated_effect_num() >= 0 ) {
1528 		tmap_flags |= TMAP_ANIMATED_SHADER;
1529 		rendering_material.set_animated_effect(render_info->get_animated_effect_num(), render_info->get_animated_effect_timer());
1530 	}
1531 
1532 	bool is_outlines_only_htl = (flags & MR_NO_POLYS) && (flags & MR_SHOW_OUTLINE_HTL);
1533 
1534 	//set to true since D3d and OGL need the api matrices set
1535 	scene->push_transform(pos, orient);
1536 
1537 
1538 	vec3d auto_back = ZERO_VECTOR;
1539 	bool set_autocen = model_render_determine_autocenter(&auto_back, pm, render_info->get_detail_level_lock(), flags);
1540 
1541 	if ( set_autocen ) {
1542 		scene->push_transform(&auto_back, NULL);
1543 	}
1544 
1545 	if (is_outlines_only_htl) {
1546 		rendering_material.set_fill_mode(GR_FILL_MODE_WIRE);
1547 
1548 		color outline_color = render_info->get_color();
1549 		rendering_material.set_color(outline_color);
1550 
1551 		tmap_flags &= ~TMAP_FLAG_RGB;
1552 	} else {
1553 		rendering_material.set_fill_mode(GR_FILL_MODE_SOLID);
1554 	}
1555 
1556 	rendering_material.set_light_factor(1.0f);
1557 
1558 	if ( !( flags & MR_NO_LIGHTING ) ) {
1559 		scene->set_light_filter(-1, pos, pm->submodel[submodel_num].rad);
1560 		rendering_material.set_lighting(true);
1561 	} else {
1562 		rendering_material.set_lighting(false);
1563 	}
1564 
1565 	// fixes disappearing HUD in OGL - taylor
1566 	rendering_material.set_cull_mode(true);
1567 
1568 	// RT - Put this here to fog debris
1569 	if( tmap_flags & TMAP_FLAG_PIXEL_FOG ) {
1570 		float fog_near, fog_far;
1571 		object *obj = NULL;
1572 
1573 		if (objnum >= 0)
1574 			obj = &Objects[objnum];
1575 
1576 		neb2_get_adjusted_fog_values(&fog_near, &fog_far, nullptr, obj);
1577 		unsigned char r, g, b;
1578 		neb2_get_fog_color(&r, &g, &b);
1579 
1580 		rendering_material.set_fog(r, g, b, fog_near, fog_far);
1581 	} else {
1582 		rendering_material.set_fog();
1583 	}
1584 
1585 	if(Rendering_to_shadow_map) {
1586 		rendering_material.set_depth_bias(-1024);
1587 	} else {
1588 		rendering_material.set_depth_bias(0);
1589 	}
1590 
1591 	vec3d view_pos = scene->get_view_position();
1592 
1593 	if ( model_render_check_detail_box(&view_pos, pm, submodel_num, flags) ) {
1594 		model_render_buffers(scene, &rendering_material, render_info, &pm->submodel[submodel_num].buffer, pm, submodel_num, 0, tmap_flags);
1595 
1596 		if ( pm->flags & PM_FLAG_TRANS_BUFFER && pm->submodel[submodel_num].trans_buffer.flags & VB_FLAG_TRANS ) {
1597 			model_render_buffers(scene, &rendering_material, render_info, &pm->submodel[submodel_num].trans_buffer, pm, submodel_num, 0, tmap_flags);
1598 		}
1599 	}
1600 
1601 	if ( pmi->submodel[submodel_num].num_arcs > 0 )	{
1602 		model_render_add_lightning( scene, render_info, pm, &pmi->submodel[submodel_num] );
1603 	}
1604 
1605 	if ( set_autocen ) {
1606 		scene->pop_transform();
1607 	}
1608 
1609 	scene->pop_transform();
1610 }
1611 
model_render_glowpoint(int point_num,vec3d * pos,matrix * orient,glow_point_bank * bank,glow_point_bank_override * gpo,polymodel * pm,polymodel_instance * pmi,ship * shipp,bool use_depth_buffer)1612 void model_render_glowpoint(int point_num, vec3d *pos, matrix *orient, glow_point_bank *bank, glow_point_bank_override *gpo, polymodel *pm, polymodel_instance *pmi, ship* shipp, bool use_depth_buffer)
1613 {
1614 	glow_point *gpt = &bank->points[point_num];
1615 	vec3d loc_offset = gpt->pnt;
1616 	vec3d loc_norm = gpt->norm;
1617 	vec3d world_pnt;
1618 	vec3d world_norm;
1619 	vec3d tempv;
1620 	vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
1621 	bool submodel_rotation = false;
1622 
1623 	if ( bank->submodel_parent > 0 && pm->submodel[bank->submodel_parent].can_move && shipp != NULL ) {
1624 		model_find_submodel_offset(&submodel_static_offset, pm, bank->submodel_parent);
1625 
1626 		submodel_rotation = true;
1627 	}
1628 
1629 	if ( submodel_rotation ) {
1630 		vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
1631 
1632 		tempv = loc_offset;
1633 		find_submodel_instance_point_normal(&loc_offset, &loc_norm, pm, pmi, bank->submodel_parent, &tempv, &loc_norm);
1634 	}
1635 
1636 	vm_vec_unrotate(&world_pnt, &loc_offset, orient);
1637 	vm_vec_add2(&world_pnt, pos);
1638 
1639 	vm_vec_unrotate(&world_norm, &loc_norm, orient);
1640 
1641 	if (shipp != nullptr) {
1642 		// don't render if its on the wrong side of the portal
1643 		WarpEffect* warp_effect = nullptr;
1644 
1645 		if ((shipp->is_arriving()) && (shipp->warpin_effect != nullptr)
1646 			&& Warp_params[shipp->warpin_params_index].warp_type != WT_HYPERSPACE) {
1647 			warp_effect = shipp->warpin_effect;
1648 		}
1649 		else if ((shipp->flags[Ship::Ship_Flags::Depart_warp]) && (shipp->warpout_effect != nullptr)
1650 			&& Warp_params[shipp->warpout_params_index].warp_type != WT_HYPERSPACE) {
1651 			warp_effect = shipp->warpout_effect;
1652 		}
1653 
1654 		if (warp_effect != nullptr && point_is_clipped_by_warp(&world_pnt, warp_effect))
1655 			return;
1656 	}
1657 
1658 	switch ((gpo && gpo->type_override)?gpo->type:bank->type)
1659 	{
1660 	case 0:
1661 		{
1662 			float d,pulse = 1.0f;
1663 
1664 			if ( IS_VEC_NULL(&world_norm) ) {
1665 				d = 1.0f;	//if given a nul vector then always show it
1666 			} else {
1667 				vm_vec_sub(&tempv,&View_position,&world_pnt);
1668 				vm_vec_normalize(&tempv);
1669 
1670 				d = vm_vec_dot(&tempv,&world_norm);
1671 				d -= 0.25;
1672 			}
1673 
1674 			float w = gpt->radius;
1675 			if (d > 0.0f) {
1676 				vertex p;
1677 
1678 				d *= 3.0f;
1679 
1680 				if (d > 1.0f)
1681 					d = 1.0f;
1682 
1683 
1684 				// fade them in the nebula as well
1685 				if ( The_mission.flags[Mission::Mission_Flags::Fullneb] ) {
1686 					//vec3d npnt;
1687 					//vm_vec_add(&npnt, &loc_offset, pos);
1688 
1689 					d *= neb2_get_fog_visibility(&world_pnt, NEB_FOG_VISIBILITY_MULT_GLOWPOINT);
1690 					w *= 1.5;	//make it bigger in a nebula
1691 				}
1692 
1693 				g3_transfer_vertex(&p, &world_pnt);
1694 
1695 				p.r = p.g = p.b = p.a = (ubyte)(255.0f * MAX(d,0.0f));
1696 
1697 				if((gpo && gpo->glow_bitmap_override)?(gpo->glow_bitmap > -1):(bank->glow_bitmap > -1)) {
1698 					int gpflags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_EMISSIVE;
1699 
1700 					if (use_depth_buffer)
1701 						gpflags |= TMAP_FLAG_SOFT_QUAD;
1702 
1703 					int bitmap_id = (gpo && gpo->glow_bitmap_override) ? gpo->glow_bitmap : bank->glow_bitmap;
1704 
1705 					if ( use_depth_buffer ) {
1706 						batching_add_volume_bitmap(bitmap_id, &p, 0, (w * 0.5f), d * pulse);
1707 					} else {
1708 						batching_add_bitmap(bitmap_id, &p, 0, (w * 0.5f), d * pulse);
1709 					}
1710 				}
1711 			} //d>0.0f
1712 			if ( gpo && gpo->pulse_type ) {
1713 				int period;
1714 
1715 				if(gpo->pulse_period_override) {
1716 					period = gpo->pulse_period;
1717 				} else {
1718 					if(gpo->on_time_override) {
1719 						period = 2 * gpo->on_time;
1720 					} else {
1721 						period = 2 * bank->on_time;
1722 					}
1723 				}
1724 
1725 				int x = 0;
1726 
1727 				if ( (gpo && gpo->off_time_override) ? gpo->off_time : bank->off_time ) {
1728 					x = (timestamp() - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % ( ((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) ) - ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time);
1729 				} else {
1730 					x = (timestamp() - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % gpo->pulse_period;
1731 				}
1732 
1733 				switch ( gpo->pulse_type ) {
1734 
1735 				case PULSE_SIN:
1736 					pulse = gpo->pulse_bias + gpo->pulse_amplitude * pow(sin( PI2 / period * x),gpo->pulse_exponent);
1737 					break;
1738 
1739 				case PULSE_COS:
1740 					pulse = gpo->pulse_bias + gpo->pulse_amplitude * pow(cos( PI2 / period * x),gpo->pulse_exponent);
1741 					break;
1742 
1743 				case PULSE_SHIFTTRI:
1744 					x += period / 4;
1745 					if((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) {
1746 						x %= ( ((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) );
1747 					} else {
1748 						x %= gpo->pulse_period;
1749 					}
1750 					FALLTHROUGH;
1751 
1752 				case PULSE_TRI:
1753 					float inv;
1754 					if( x > period / 2) {
1755 						inv = -1;
1756 					} else {
1757 						inv = 1;
1758 					}
1759 					if( x > period / 4) {
1760 						pulse = gpo->pulse_bias + gpo->pulse_amplitude * inv * pow( 1.0f - ((x - period / 4.0f) * 4 / period) ,gpo->pulse_exponent);
1761 					} else {
1762 						pulse = gpo->pulse_bias + gpo->pulse_amplitude * inv * pow( (x * 4.0f / period) ,gpo->pulse_exponent);
1763 					}
1764 					break;
1765 				}
1766 			}
1767 
1768 			if ( Detail.lighting > 3 && Deferred_lighting && gpo && gpo->is_lightsource ) {
1769 				if ( gpo->lightcone ) {
1770 					vec3d cone_dir_rot;
1771 					vec3d cone_dir_model;
1772 					vec3d cone_dir_world;
1773 					vec3d cone_dir_screen;
1774 					vec3d unused;
1775 
1776 					if ( gpo->rotating ) {
1777 						vm_rot_point_around_line(&cone_dir_rot, &gpo->cone_direction, PI * timestamp() * 0.000033333f * gpo->rotation_speed, &vmd_zero_vector, &gpo->rotation_axis);
1778 					} else {
1779 						cone_dir_rot = gpo->cone_direction;
1780 					}
1781 
1782 					find_submodel_instance_point_normal(&unused, &cone_dir_model, pm, pmi, bank->submodel_parent, &unused, &cone_dir_rot);
1783 					vm_vec_unrotate(&cone_dir_world, &cone_dir_model, orient);
1784 					vm_vec_rotate(&cone_dir_screen, &cone_dir_world, &Eye_matrix);
1785 					cone_dir_screen.xyz.z = -cone_dir_screen.xyz.z;
1786 					light_add_cone(&world_pnt, &cone_dir_screen, gpo->cone_angle, gpo->cone_inner_angle, gpo->dualcone, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
1787 				} else {
1788 					light_add_point(&world_pnt, 1.0f, w * gpo->radius_multi, 1, pulse * gpo->light_color.xyz.x + (1.0f-pulse) * gpo->light_mix_color.xyz.x, pulse * gpo->light_color.xyz.y + (1.0f-pulse) * gpo->light_mix_color.xyz.y, pulse * gpo->light_color.xyz.z + (1.0f-pulse) * gpo->light_mix_color.xyz.z, -1);
1789 				}
1790 			}
1791 			break;
1792 		}
1793 
1794 	case 1:
1795 		{
1796 			vertex verts[4];
1797 			vec3d fvec, top1, bottom1, top2, bottom2, start, end;
1798 
1799 			vm_vec_add2(&loc_norm, &loc_offset);
1800 
1801 			vm_vec_rotate(&start, &loc_offset, orient);
1802 			vm_vec_rotate(&end, &loc_norm, orient);
1803 			vm_vec_sub(&fvec, &end, &start);
1804 
1805 			vm_vec_normalize(&fvec);
1806 
1807 			moldel_calc_facing_pts(&top1, &bottom1, &fvec, &loc_offset, gpt->radius, 1.0f, &View_position);
1808 			moldel_calc_facing_pts(&top2, &bottom2, &fvec, &loc_norm, gpt->radius, 1.0f, &View_position);
1809 
1810 			g3_transfer_vertex(&verts[0], &bottom1);
1811 			g3_transfer_vertex(&verts[1], &bottom2);
1812 			g3_transfer_vertex(&verts[2], &top2);
1813 			g3_transfer_vertex(&verts[3], &top1);
1814 
1815 			verts[0].texture_position.u = 0.0f;
1816 			verts[0].texture_position.v = 0.0f;
1817 
1818 			verts[1].texture_position.u = 1.0f;
1819 			verts[1].texture_position.v = 0.0f;
1820 
1821 			verts[2].texture_position.u = 1.0f;
1822 			verts[2].texture_position.v = 1.0f;
1823 
1824 			verts[3].texture_position.u = 0.0f;
1825 			verts[3].texture_position.v = 1.0f;
1826 
1827 			vm_vec_sub(&tempv,&View_position,&loc_offset);
1828 			vm_vec_normalize(&tempv);
1829 
1830 			if ( The_mission.flags[Mission::Mission_Flags::Fullneb] ) {
1831 				batching_add_quad(bank->glow_neb_bitmap, verts);
1832 			} else {
1833 				batching_add_quad(bank->glow_bitmap, verts);
1834 			}
1835 
1836 			break;
1837 		}
1838 	}
1839 }
1840 
model_render_set_glow_points(polymodel * pm,int objnum)1841 void model_render_set_glow_points(polymodel *pm, int objnum)
1842 {
1843 	int time = timestamp();
1844 	glow_point_bank_override *gpo = NULL;
1845 	bool override_all = false;
1846 	SCP_unordered_map<int, void*>::iterator gpoi;
1847 	ship_info *sip = NULL;
1848 	ship *shipp = NULL;
1849 
1850 	if ( Glowpoint_override ) {
1851 		return;
1852 	}
1853 
1854 	if ( objnum > -1 ) {
1855 		object *objp = &Objects[objnum];
1856 
1857 		if ( objp != NULL && objp->type == OBJ_SHIP ) {
1858 			shipp = &Ships[Objects[objnum].instance];
1859 			sip = &Ship_info[shipp->ship_info_index];
1860 			gpoi = sip->glowpoint_bank_override_map.find(-1);
1861 
1862 			if (gpoi != sip->glowpoint_bank_override_map.end()) {
1863 				override_all = true;
1864 				gpo = (glow_point_bank_override*)sip->glowpoint_bank_override_map[-1];
1865 			}
1866 		}
1867 	}
1868 
1869 	for ( int i = 0; i < pm->n_glow_point_banks; i++ ) { //glow point blink code -Bobboau
1870 		glow_point_bank *bank = &pm->glow_point_banks[i];
1871 
1872 		if ( !override_all && sip ) {
1873 			gpoi = sip->glowpoint_bank_override_map.find(i);
1874 
1875 			if ( gpoi != sip->glowpoint_bank_override_map.end() ) {
1876 				gpo = (glow_point_bank_override*) sip->glowpoint_bank_override_map[i];
1877 			} else {
1878 				gpo = NULL;
1879 			}
1880 		}
1881 
1882 		if ( bank->glow_timestamp == 0 ) {
1883 			bank->glow_timestamp=time;
1884 		}
1885 
1886 		int on_time = (gpo && gpo->on_time_override) ? gpo->on_time : bank->on_time;
1887 		int off_time = (gpo && gpo->off_time_override) ? gpo->off_time : bank->off_time;
1888 		int disp_time = (gpo && gpo->disp_time_override) ? gpo->disp_time : bank->disp_time;
1889 
1890 
1891 		if (off_time) {
1892 			bool glow_state = ((time - disp_time) % (on_time + off_time)) < on_time;
1893 
1894 			if ( glow_state != bank->is_on )
1895 					bank->glow_timestamp = time;
1896 
1897 			bank->is_on = glow_state;
1898 		}
1899 	}
1900 }
1901 
model_render_glow_points(polymodel * pm,polymodel_instance * pmi,ship * shipp,matrix * orient,vec3d * pos,bool use_depth_buffer=true)1902 void model_render_glow_points(polymodel *pm, polymodel_instance *pmi, ship *shipp, matrix *orient, vec3d *pos, bool use_depth_buffer = true)
1903 {
1904 	Assert(pmi == nullptr || pm->id == pmi->model_num);
1905 
1906 	if ( Rendering_to_shadow_map ) {
1907 		return;
1908 	}
1909 
1910 	int i, j;
1911 
1912 	int cull = gr_set_cull(0);
1913 
1914 	glow_point_bank_override *gpo = NULL;
1915 	bool override_all = false;
1916 	SCP_unordered_map<int, void*>::iterator gpoi;
1917 	ship_info *sip = NULL;
1918 
1919 	if ( shipp ) {
1920 		sip = &Ship_info[shipp->ship_info_index];
1921 		gpoi = sip->glowpoint_bank_override_map.find(-1);
1922 
1923 		if(gpoi != sip->glowpoint_bank_override_map.end()) {
1924 			override_all = true;
1925 			gpo = (glow_point_bank_override*) sip->glowpoint_bank_override_map[-1];
1926 		}
1927 	}
1928 
1929 	for (i = 0; i < pm->n_glow_point_banks; i++ ) {
1930 		glow_point_bank *bank = &pm->glow_point_banks[i];
1931 
1932 		if(!override_all && sip) {
1933 			gpoi = sip->glowpoint_bank_override_map.find(i);
1934 			if(gpoi != sip->glowpoint_bank_override_map.end()) {
1935 				gpo = (glow_point_bank_override*) sip->glowpoint_bank_override_map[i];
1936 			} else {
1937 				gpo = NULL;
1938 			}
1939 		}
1940 
1941 		//Only continue if there actually is a glowpoint bitmap available
1942 		if (bank->glow_bitmap == -1)
1943 			continue;
1944 
1945 		if (pmi != nullptr) {
1946 			auto smi = &pmi->submodel[bank->submodel_parent];
1947 			if (smi->blown_off) {
1948 				continue;
1949 			}
1950 		}
1951 
1952 		if ((gpo && gpo->off_time_override && !gpo->off_time)?gpo->is_on:bank->is_on) {
1953 			if ( (shipp != NULL) && !(shipp->glow_point_bank_active[i]) )
1954 				continue;
1955 
1956 			for (j = 0; j < bank->num_points; j++) {
1957 				Assert( bank->points != NULL );
1958 				int flick;
1959 
1960 				if (pmi != nullptr && pmi->submodel[pm->detail[0]].num_arcs > 0) {
1961 					flick = static_rand( timestamp() % 20 ) % (pmi->submodel[pm->detail[0]].num_arcs + j); //the more damage, the more arcs, the more likely the lights will fail
1962 				} else {
1963 					flick = 1;
1964 				}
1965 
1966 				if (flick == 1) {
1967 					model_render_glowpoint(j, pos, orient, bank, gpo, pm, pmi, shipp, use_depth_buffer);
1968 				} // flick
1969 			} // for slot
1970 		} // bank is on
1971 	} // for bank
1972 
1973 	gr_set_cull(cull);
1974 }
1975 
model_queue_render_thrusters(model_render_params * interp,polymodel * pm,int objnum,ship * shipp,matrix * orient,vec3d * pos)1976 void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, int objnum, ship *shipp, matrix *orient, vec3d *pos)
1977 {
1978 	int i, j;
1979 	int n_q = 0;
1980 	size_t 	k;
1981 	vec3d norm, norm2, fvec, pnt, npnt;
1982 	thruster_bank *bank = nullptr;
1983 	polymodel_instance *pmi = nullptr;
1984 	vertex p;
1985 	bool do_render = false;
1986 
1987 	if ( Rendering_to_shadow_map ) {
1988 		return;
1989 	}
1990 
1991 	if ( pm == NULL ) {
1992 		Int3();
1993 		return;
1994 	}
1995 
1996 	if ( !(interp->get_model_flags() & MR_SHOW_THRUSTERS) ) {
1997 		return;
1998 	}
1999 
2000 	// get an initial count to figure out how man geo batchers we need allocated
2001 	for (i = 0; i < pm->n_thrusters; i++ ) {
2002 		bank = &pm->thrusters[i];
2003 		n_q += bank->num_points;
2004 	}
2005 
2006 	if (n_q <= 0) {
2007 		return;
2008 	}
2009 
2010 	const mst_info& thruster_info = interp->get_thruster_info();
2011 
2012 	// primary_thruster_batcher
2013 	if (thruster_info.primary_glow_bitmap >= 0) {
2014 		do_render = true;
2015 	}
2016 
2017 	// secondary_thruster_batcher
2018 	if (thruster_info.secondary_glow_bitmap >= 0) {
2019 		do_render = true;
2020 	}
2021 
2022 	// tertiary_thruster_batcher
2023 	if (thruster_info.tertiary_glow_bitmap >= 0) {
2024 		do_render = true;
2025 	}
2026 
2027 	if (do_render == false) {
2028 		return;
2029 	}
2030 
2031 	// this is used for the secondary thruster glows
2032 	// it only needs to be calculated once so I'm doing it here -Bobboau
2033 	norm.xyz.z = -1.0f;
2034 	norm.xyz.x = 1.0f;
2035 	norm.xyz.y = -1.0f;
2036 	norm.xyz.x *= thruster_info.rotvel.xyz.y/2;
2037 	norm.xyz.y *= thruster_info.rotvel.xyz.x/2;
2038 	vm_vec_normalize(&norm);
2039 
2040 	for (i = 0; i < pm->n_thrusters; i++ ) {
2041 		vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
2042 		bool submodel_rotation = false;
2043 
2044 		bank = &pm->thrusters[i];
2045 
2046 		// don't draw this thruster if the engine is destroyed or just not on
2047 		if ( !model_should_render_engine_glow(objnum, bank->obj_num) )
2048 			continue;
2049 
2050 		// If bank is attached to a submodel, prepare to account for rotations
2051 		//
2052 		// TODO: This won't work in the ship lab, because the lab code doesn't
2053 		// set the the necessary submodel instance info needed here. The second
2054 		// condition is thus a hack to disable the feature while in the lab, and
2055 		// can be removed if the lab is re-structured accordingly. -zookeeper
2056 		if ( bank->submodel_num > -1 && pm->submodel[bank->submodel_num].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
2057 			model_find_submodel_offset(&submodel_static_offset, pm, bank->submodel_num);
2058 
2059 			submodel_rotation = true;
2060 		}
2061 
2062 		for (j = 0; j < bank->num_points; j++) {
2063 			Assert( bank->points != NULL );
2064 
2065 			float d;
2066 			vec3d tempv;
2067 			glow_point *gpt = &bank->points[j];
2068 			vec3d loc_offset = gpt->pnt;
2069 			vec3d loc_norm = gpt->norm;
2070 			vec3d world_pnt;
2071 			vec3d world_norm;
2072 
2073 			if ( submodel_rotation ) {
2074 				vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
2075 				tempv = loc_offset;
2076 
2077 				if (pmi == nullptr)
2078 					pmi = model_get_instance(shipp->model_instance_num);
2079 
2080 				find_submodel_instance_point_normal(&loc_offset, &loc_norm, pm, pmi, bank->submodel_num, &tempv, &loc_norm);
2081 			}
2082 
2083 			vm_vec_unrotate(&world_pnt, &loc_offset, orient);
2084 			vm_vec_add2(&world_pnt, pos);
2085 
2086 			if (shipp) {
2087 				// if ship is warping out, check position of the engine glow to the warp plane
2088 				WarpEffect* warp_effect = nullptr;
2089 
2090 				if ((shipp->is_arriving()) && (shipp->warpin_effect != nullptr)
2091 					&& Warp_params[shipp->warpin_params_index].warp_type != WT_HYPERSPACE) {
2092 					warp_effect = shipp->warpin_effect;
2093 				}
2094 				else if ((shipp->flags[Ship::Ship_Flags::Depart_warp]) && (shipp->warpout_effect != nullptr)
2095 					&& Warp_params[shipp->warpout_params_index].warp_type != WT_HYPERSPACE) {
2096 					warp_effect = shipp->warpout_effect;
2097 				}
2098 
2099 				if (warp_effect != nullptr && point_is_clipped_by_warp(&world_pnt, warp_effect))
2100 					continue;
2101 			}
2102 
2103 			vm_vec_sub(&tempv, &View_position, &world_pnt);
2104 			vm_vec_normalize(&tempv);
2105 			vm_vec_unrotate(&world_norm, &loc_norm, orient);
2106 			d = vm_vec_dot(&tempv, &world_norm);
2107 
2108 			// ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
2109 #define NOISE_SCALE 0.5f
2110 #define MIN_SCALE 3.4f
2111 #define MAX_SCALE 4.7f
2112 
2113 			float magnitude;
2114 			vec3d scale_vec = { { { 1.0f, 0.0f, 0.0f } } };
2115 
2116 			// normalize banks, in case of incredibly big normals
2117 			if ( !IS_VEC_NULL_SQ_SAFE(&world_norm) )
2118 				vm_vec_copy_normalize(&scale_vec, &world_norm);
2119 
2120 			// adjust for thrust
2121 			(scale_vec.xyz.x *= thruster_info.length.xyz.x) -= 0.1f;
2122 			(scale_vec.xyz.y *= thruster_info.length.xyz.y) -= 0.1f;
2123 			(scale_vec.xyz.z *= thruster_info.length.xyz.z)   -= 0.1f;
2124 
2125 			// get magnitude, which we will use as the scaling reference
2126 			magnitude = vm_vec_normalize(&scale_vec);
2127 
2128 			// get absolute value
2129 			if (magnitude < 0.0f)
2130 				magnitude *= -1.0f;
2131 
2132 			float scale = magnitude * (MAX_SCALE - MIN_SCALE) + MIN_SCALE;
2133 
2134 			if (d > 0.0f){
2135 				// Make glow bitmap fade in/out quicker from sides.
2136 				d *= 3.0f;
2137 
2138 				if (d > 1.0f)
2139 					d = 1.0f;
2140 			}
2141 
2142 			float fog_int = 1.0f;
2143 
2144 			// fade them in the nebula as well
2145 			if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
2146 				vm_vec_unrotate(&npnt, &gpt->pnt, orient);
2147 				vm_vec_add2(&npnt, pos);
2148 
2149 				fog_int = neb2_get_fog_visibility(&npnt, NEB_FOG_VISIBILITY_MULT_THRUSTER);
2150 
2151 				if (fog_int > 1.0f)
2152 					fog_int = 1.0f;
2153 
2154 				d *= fog_int;
2155 
2156 				if (d > 1.0f)
2157 					d = 1.0f;
2158 			}
2159 
2160 			float w = gpt->radius * (scale + thruster_info.glow_noise * NOISE_SCALE);
2161 
2162 			// these lines are used by the tertiary glows, thus we will need to project this all of the time
2163 			g3_transfer_vertex( &p, &world_pnt );
2164 
2165 			// start primary thruster glows
2166 			if ( (thruster_info.primary_glow_bitmap >= 0) && (d > 0.0f) ) {
2167 				p.r = p.g = p.b = p.a = (ubyte)(255.0f * d);
2168 				batching_add_volume_bitmap(thruster_info.primary_glow_bitmap, &p, 0, (w * 0.5f * thruster_info.glow_rad_factor), d, w * 0.325f);
2169 			}
2170 
2171 			// start tertiary thruster glows
2172 			if (thruster_info.tertiary_glow_bitmap >= 0) {
2173 				p.screen.xyw.w -= w;
2174 				p.r = p.g = p.b = p.a = (ubyte)(255.0f * fog_int);
2175 				batching_add_volume_bitmap_rotated(thruster_info.tertiary_glow_bitmap, &p, magnitude * 4, w * 0.6f * thruster_info.tertiary_glow_rad_factor, fog_int, -w*0.5f);
2176 			}
2177 
2178 			// begin secondary glows
2179 			if (thruster_info.secondary_glow_bitmap >= 0) {
2180 				pnt = world_pnt;
2181 				scale = magnitude * (MAX_SCALE - (MIN_SCALE / 2)) + (MIN_SCALE / 2);
2182 				vm_vec_unrotate(&world_norm, &norm, orient);
2183 				d = vm_vec_dot(&tempv, &world_norm);
2184 				d += 0.75f;
2185 				d *= 3.0f;
2186 
2187 				if (d > 1.0f)
2188 					d = 1.0f;
2189 
2190 				if (d > 0.0f) {
2191 					vm_vec_add(&norm2, &world_norm, &pnt);
2192 					vm_vec_sub(&fvec, &norm2, &pnt);
2193 					vm_vec_normalize(&fvec);
2194 
2195 					float wVal = gpt->radius * scale * 2;
2196 
2197 					vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * thruster_info.glow_length_factor);
2198 
2199 					if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
2200 						vm_vec_add(&npnt, &pnt, pos);
2201 						d *= fog_int;
2202 					}
2203 
2204 					batching_add_beam(thruster_info.secondary_glow_bitmap, &pnt, &norm2, wVal*thruster_info.secondary_glow_rad_factor*0.5f, d);
2205 
2206 					if (Scene_framebuffer_in_frame && thruster_info.draw_distortion &&
2207 					    Gr_framebuffer_effects[FramebufferEffects::Thrusters]) {
2208 						vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * thruster_info.distortion_length_factor);
2209 						int dist_bitmap;
2210 						if (thruster_info.distortion_bitmap > 0) {
2211 							dist_bitmap = thruster_info.distortion_bitmap;
2212 						}
2213 						else {
2214 							dist_bitmap = thruster_info.secondary_glow_bitmap;
2215 						}
2216 
2217 						float mag = vm_vec_mag(&gpt->pnt);
2218 						mag -= (float)((int)mag);//Valathil - Get a fairly random but constant number to offset the distortion texture
2219 
2220 						batching_add_distortion_beam(dist_bitmap, &pnt, &norm2, wVal*thruster_info.distortion_rad_factor*0.5f, 1.0f, mag);
2221 					}
2222 				}
2223 			}
2224 
2225 			// begin particles
2226 			if (shipp) {
2227 				ship_info *sip = &Ship_info[shipp->ship_info_index];
2228 				particle::particle_emitter pe;
2229 				thruster_particles *tp;
2230 				size_t num_particles = 0;
2231 
2232 				if (thruster_info.use_ab)
2233 					num_particles = sip->afterburner_thruster_particles.size();
2234 				else
2235 					num_particles = sip->normal_thruster_particles.size();
2236 
2237 				for (k = 0; k < num_particles; k++) {
2238 					if (thruster_info.use_ab)
2239 						tp = &sip->afterburner_thruster_particles[k];
2240 					else
2241 						tp = &sip->normal_thruster_particles[k];
2242 
2243 					float v = vm_vec_mag_quick(&Objects[shipp->objnum].phys_info.desired_vel);
2244 
2245 					vm_vec_unrotate(&npnt, &gpt->pnt, orient);
2246 					vm_vec_add2(&npnt, pos);
2247 
2248 					// Where the particles emit from
2249 					pe.pos = npnt;
2250 					// Initial velocity of all the particles
2251 					pe.vel = Objects[shipp->objnum].phys_info.desired_vel;
2252 					pe.min_vel = v * 0.75f;
2253 					pe.max_vel =  v * 1.25f;
2254 					// What normal the particle emit around
2255 					pe.normal = orient->vec.fvec;
2256 					vm_vec_negate(&pe.normal);
2257 
2258 					// Lowest number of particles to create
2259 					pe.num_low = tp->n_low;
2260 					// Highest number of particles to create
2261 					pe.num_high = tp->n_high;
2262 					pe.min_rad = gpt->radius * tp->min_rad;
2263 					pe.max_rad = gpt->radius * tp->max_rad;
2264 					// How close they stick to that normal 0=on normal, 1=180, 2=360 degree
2265 					pe.normal_variance = tp->variance;
2266 					pe.min_life = 0.0f;
2267 					pe.max_life = 1.0f;
2268 
2269 					particle::emit( &pe, particle::PARTICLE_BITMAP, tp->thruster_bitmap.first_frame);
2270 				}
2271 			}
2272 		}
2273 	}
2274 }
2275 
model_render_insignias(insignia_draw_data * insignia_data)2276 void model_render_insignias(insignia_draw_data *insignia_data)
2277 {
2278 	polymodel *pm = insignia_data->pm;
2279 	int detail_level = insignia_data->detail_level;
2280 	int bitmap_num = insignia_data->bitmap_num;
2281 
2282 	// if the model has no insignias, or we don't have a texture, then bail
2283 	if ( (pm->num_ins <= 0) || (bitmap_num < 0) )
2284 		return;
2285 
2286 	int idx, s_idx;
2287 	vertex vecs[3];
2288 	vec3d t1, t2, t3;
2289 	int i1, i2, i3;
2290 
2291 	material insignia_material;
2292 	insignia_material.set_depth_bias(1);
2293 
2294 	// set the proper texture
2295 	material_set_unlit(&insignia_material, bitmap_num, 0.65f, true, true);
2296 
2297 	if ( insignia_data->clip ) {
2298 		insignia_material.set_clip_plane(insignia_data->clip_normal, insignia_data->clip_position);
2299 	}
2300 
2301 	// otherwise render them
2302 	for(idx=0; idx<pm->num_ins; idx++){
2303 		// skip insignias not on our detail level
2304 		if(pm->ins[idx].detail_level != detail_level){
2305 			continue;
2306 		}
2307 
2308 		for(s_idx=0; s_idx<pm->ins[idx].num_faces; s_idx++){
2309 			// get vertex indices
2310 			i1 = pm->ins[idx].faces[s_idx][0];
2311 			i2 = pm->ins[idx].faces[s_idx][1];
2312 			i3 = pm->ins[idx].faces[s_idx][2];
2313 
2314 			// transform vecs and setup vertices
2315 			vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset);
2316 			vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset);
2317 			vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset);
2318 
2319 			g3_transfer_vertex(&vecs[0], &t1);
2320 			g3_transfer_vertex(&vecs[1], &t2);
2321 			g3_transfer_vertex(&vecs[2], &t3);
2322 
2323 			// setup texture coords
2324 			vecs[0].texture_position.u = pm->ins[idx].u[s_idx][0];
2325 			vecs[0].texture_position.v = pm->ins[idx].v[s_idx][0];
2326 
2327 			vecs[1].texture_position.u = pm->ins[idx].u[s_idx][1];
2328 			vecs[1].texture_position.v = pm->ins[idx].v[s_idx][1];
2329 
2330 			vecs[2].texture_position.u = pm->ins[idx].u[s_idx][2];
2331 			vecs[2].texture_position.v = pm->ins[idx].v[s_idx][2];
2332 
2333 			light_apply_rgb( &vecs[0].r, &vecs[0].g, &vecs[0].b, &pm->ins[idx].vecs[i1], &pm->ins[idx].norm[i1], 1.5f );
2334 			light_apply_rgb( &vecs[1].r, &vecs[1].g, &vecs[1].b, &pm->ins[idx].vecs[i2], &pm->ins[idx].norm[i2], 1.5f );
2335 			light_apply_rgb( &vecs[2].r, &vecs[2].g, &vecs[2].b, &pm->ins[idx].vecs[i3], &pm->ins[idx].norm[i3], 1.5f );
2336 			vecs[0].a = vecs[1].a = vecs[2].a = 255;
2337 
2338 			// draw the polygon
2339 			g3_render_primitives_colored_textured(&insignia_material, vecs, 3, PRIM_TYPE_TRIFAN, false);
2340 		}
2341 	}
2342 }
2343 
model_render_arc(vec3d * v1,vec3d * v2,color * primary,color * secondary,float arc_width)2344 void model_render_arc(vec3d *v1, vec3d *v2, color *primary, color *secondary, float arc_width)
2345 {
2346 	Num_arc_segment_points = 0;
2347 
2348 	// need need to add the first point
2349 	memcpy( &Arc_segment_points[Num_arc_segment_points++], v1, sizeof(vec3d) );
2350 
2351 	// this should fill in all of the middle, and the last, points
2352 	interp_render_arc_segment(v1, v2, 0);
2353 
2354 	// use primary color for fist pass
2355 	Assert( primary );
2356 
2357 	g3_render_rod(primary, Num_arc_segment_points, Arc_segment_points, arc_width);
2358 
2359 	if (secondary) {
2360 		g3_render_rod(secondary, Num_arc_segment_points, Arc_segment_points, arc_width * 0.33f);
2361 	}
2362 }
2363 
model_render_debug_children(polymodel * pm,int mn,int detail_level,uint debug_flags)2364 void model_render_debug_children(polymodel *pm, int mn, int detail_level, uint debug_flags)
2365 {
2366 	int i;
2367 
2368 	if ( (mn < 0) || (mn >= pm->n_models) ) {
2369 		Int3();
2370 		return;
2371 	}
2372 
2373 	bsp_info *model = &pm->submodel[mn];
2374 
2375 	// Get submodel rotation data and use submodel orientation matrix
2376 	// to put together a matrix describing the final orientation of
2377 	// the submodel relative to its parent
2378 	// (Not needed here because we're not using model instances)
2379 	matrix submodel_orient = vmd_identity_matrix;
2380 
2381 	g3_start_instance_matrix(&model->offset, &submodel_orient, true);
2382 
2383 	if ( debug_flags & MR_DEBUG_PIVOTS ) {
2384 		model_draw_debug_points( pm, &pm->submodel[mn], debug_flags );
2385 	}
2386 
2387 	i = model->first_child;
2388 
2389 	while ( i >= 0 ) {
2390 		model_render_debug_children( pm, i, detail_level, debug_flags );
2391 
2392 		i = pm->submodel[i].next_sibling;
2393 	}
2394 
2395 	g3_done_instance(true);
2396 }
2397 
model_render_debug(int model_num,matrix * orient,vec3d * pos,uint flags,uint debug_flags,int objnum,int detail_level_locked)2398 void model_render_debug(int model_num, matrix *orient, vec3d * pos, uint flags, uint debug_flags, int objnum, int detail_level_locked )
2399 {
2400 	polymodel *pm = model_get(model_num);
2401 
2402 	g3_start_instance_matrix(pos, orient, true);
2403 
2404 	if ( debug_flags & MR_DEBUG_RADIUS ) {
2405 		if ( !( flags & MR_SHOW_OUTLINE_PRESET ) ) {
2406 			gr_set_color(0,64,0);
2407 			g3_draw_sphere_ez(&vmd_zero_vector,pm->rad);
2408 		}
2409 	}
2410 	float depth = model_render_determine_depth(objnum, model_num, orient, pos, detail_level_locked);
2411 	int detail_level = model_render_determine_detail(depth, objnum, model_num, orient, pos, flags, detail_level_locked);
2412 
2413 	vec3d auto_back = ZERO_VECTOR;
2414 	bool set_autocen = model_render_determine_autocenter(&auto_back, pm, detail_level, flags);
2415 
2416 	if ( set_autocen ) {
2417 		g3_start_instance_matrix(&auto_back, NULL, true);
2418 	}
2419 
2420 	uint save_gr_zbuffering_mode = gr_zbuffer_set(GR_ZBUFF_READ);
2421 
2422 	int i = pm->submodel[pm->detail[detail_level]].first_child;
2423 
2424 	while ( i >= 0 ) {
2425 		model_render_debug_children( pm, i, detail_level, flags );
2426 
2427 		i = pm->submodel[i].next_sibling;
2428 	}
2429 
2430 	if ( debug_flags & MR_DEBUG_PIVOTS ) {
2431 		model_draw_debug_points( pm, NULL, flags );
2432 		model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]], flags );
2433 
2434 		if ( pm->flags & PM_FLAG_AUTOCEN ) {
2435 			gr_set_color(255, 255, 255);
2436 			g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
2437 		}
2438 	}
2439 
2440 	if ( debug_flags & MR_DEBUG_SHIELDS )	{
2441 		model_render_shields(pm, flags);
2442 	}
2443 
2444 	if ( debug_flags & MR_DEBUG_PATHS ) {
2445 		model_draw_paths_htl(model_num, flags);
2446 	}
2447 
2448 	if ( debug_flags & MR_DEBUG_BAY_PATHS ) {
2449 		model_draw_bay_paths_htl(model_num);
2450 	}
2451 
2452 	if ( (flags & MR_AUTOCENTER) && (set_autocen) ) {
2453 		g3_done_instance(true);
2454 	}
2455 
2456 	g3_done_instance(true);
2457 
2458 	gr_zbuffer_set(save_gr_zbuffering_mode);
2459 }
2460 
model_render_immediate(model_render_params * render_info,int model_num,matrix * orient,vec3d * pos,int render,bool sort)2461 void model_render_immediate(model_render_params* render_info, int model_num, matrix* orient, vec3d* pos, int render, bool sort)
2462 {
2463 	model_render_immediate(render_info, model_num, -1, orient, pos, render, sort);
2464 }
2465 
model_render_immediate(model_render_params * render_info,int model_num,int model_instance_num,matrix * orient,vec3d * pos,int render,bool sort)2466 void model_render_immediate(model_render_params* render_info, int model_num, int model_instance_num, matrix* orient, vec3d* pos, int render, bool sort)
2467 {
2468 	model_draw_list model_list;
2469 
2470 	model_list.init();
2471 
2472 	model_render_queue(render_info, &model_list, model_num, model_instance_num, orient, pos);
2473 
2474 	model_list.init_render(sort);
2475 
2476 	switch ( render ) {
2477 	case MODEL_RENDER_OPAQUE:
2478 		model_list.render_all(ZBUFFER_TYPE_FULL);
2479 		break;
2480 	case MODEL_RENDER_TRANS:
2481 		model_list.render_all(ZBUFFER_TYPE_READ);
2482 		model_list.render_all(ZBUFFER_TYPE_NONE);
2483 		break;
2484 	case MODEL_RENDER_ALL:
2485 		model_list.render_all();
2486 		break;
2487 	}
2488 
2489 	model_list.render_outlines();
2490 	model_list.render_insignias();
2491 	model_list.render_arcs();
2492 
2493 	gr_zbias(0);
2494 	gr_set_cull(0);
2495 	gr_zbuffer_set(ZBUFFER_TYPE_READ);
2496 	gr_set_fill_mode(GR_FILL_MODE_SOLID);
2497 
2498 	gr_clear_states();
2499 
2500 	gr_reset_lighting();
2501 	gr_set_lighting(false, false);
2502 
2503 	if ( render_info->get_debug_flags() ) {
2504 		model_render_debug(model_num, orient, pos, render_info->get_model_flags(), render_info->get_debug_flags(), render_info->get_object_number(), render_info->get_detail_level_lock());
2505 	}
2506 }
2507 
model_render_queue(model_render_params * interp,model_draw_list * scene,int model_num,matrix * orient,vec3d * pos)2508 void model_render_queue(model_render_params* interp, model_draw_list* scene, int model_num, matrix* orient, vec3d* pos)
2509 {
2510 	model_render_queue(interp, scene, model_num, -1, orient, pos);
2511 }
2512 
model_render_queue(model_render_params * interp,model_draw_list * scene,int model_num,int model_instance_num,matrix * orient,vec3d * pos)2513 void model_render_queue(model_render_params* interp, model_draw_list* scene, int model_num, int model_instance_num, matrix* orient, vec3d* pos)
2514 {
2515 	int i;
2516 
2517 	const int objnum = interp->get_object_number();
2518 	const int model_flags = interp->get_model_flags();
2519 
2520 	model_material rendering_material;
2521 	polymodel *pm = model_get(model_num);
2522 	polymodel_instance *pmi = NULL;
2523 
2524 	float light_factor = model_render_determine_light_factor(interp, pos, model_flags);
2525 
2526 	if ( light_factor < (1.0f/32.0f) ) {
2527 		// If it is too far, exit
2528 		return;
2529 	}
2530 
2531 	rendering_material.set_light_factor(light_factor);
2532 
2533 	if ( interp->is_clip_plane_set() ) {
2534 		rendering_material.set_clip_plane(interp->get_clip_plane_normal(), interp->get_clip_plane_pos());
2535 	}
2536 
2537 	if ( interp->is_team_color_set() ) {
2538 		rendering_material.set_team_color(interp->get_team_color());
2539 	}
2540 
2541 	if ( model_flags & MR_FORCE_CLAMP ) {
2542 		rendering_material.set_texture_addressing(TMAP_ADDRESS_CLAMP);
2543 	} else {
2544 		rendering_material.set_texture_addressing(TMAP_ADDRESS_WRAP);
2545 	}
2546 
2547 	model_render_set_glow_points(pm, objnum);
2548 
2549 	if ( !(model_flags & MR_NO_LIGHTING) ) {
2550 		scene->set_light_filter(objnum, pos, pm->rad);
2551 	}
2552 
2553 	ship *shipp = NULL;
2554 	object *objp = NULL;
2555 
2556 	if (objnum >= 0) {
2557 		objp = &Objects[objnum];
2558 		int tentative_num = -1;
2559 
2560 		if (objp->type == OBJ_SHIP) {
2561 			shipp = &Ships[objp->instance];
2562 			tentative_num = shipp->model_instance_num;
2563 		}
2564 		else {
2565 			tentative_num = object_get_model_instance(objp);
2566 		}
2567 
2568 		if (tentative_num >= 0) {
2569 			model_instance_num = tentative_num;
2570 		}
2571 	}
2572 
2573 	// is this a skybox with a rotating submodel?
2574 	extern int Nmodel_num, Nmodel_instance_num;
2575 	if (model_num == Nmodel_num && Nmodel_instance_num >= 0) {
2576 		model_instance_num = Nmodel_instance_num;
2577 	}
2578 
2579 	if (model_instance_num >= 0) {
2580 		pmi = model_get_instance(model_instance_num);
2581 
2582 		// This can happen if we supply a HUD target model for a real ship.
2583 		// The passed parameter was -1 but it was assigned an instance from
2584 		// the actual object.  Set it back to -1 if there is a mismatch.
2585 		if (pmi->model_num != pm->id) {
2586 			model_instance_num = -1;
2587 			pmi = nullptr;
2588 		}
2589 	}
2590 
2591 	// Set the flags we will pass to the tmapper
2592 	uint tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
2593 
2594 	if ( !(model_flags & MR_NO_TEXTURING) )	{
2595 		tmap_flags |= TMAP_FLAG_TEXTURED;
2596 
2597 		if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
2598 			tmap_flags |= TMAP_FLAG_TILED;
2599 
2600 		if ( !(model_flags & MR_NO_CORRECT) )	{
2601 			tmap_flags |= TMAP_FLAG_CORRECT;
2602 		}
2603 	}
2604 
2605 	if ( model_flags & MR_DESATURATED ) {
2606 		rendering_material.set_desaturation(true);
2607 	}
2608 
2609 	if ( interp->get_animated_effect_num() >= 0 ) {
2610 		rendering_material.set_animated_effect(interp->get_animated_effect_num(), interp->get_animated_effect_timer());
2611 	}
2612 
2613 	bool is_outlines_only = (model_flags & MR_NO_POLYS) && ((model_flags & MR_SHOW_OUTLINE_PRESET) || (model_flags & MR_SHOW_OUTLINE));
2614 	bool is_outlines_only_htl = (model_flags & MR_NO_POLYS) && (model_flags & MR_SHOW_OUTLINE_HTL);
2615 
2616 	scene->push_transform(pos, orient);
2617 
2618 	float depth = model_render_determine_depth(objnum, model_num, orient, pos, interp->get_detail_level_lock());
2619 	int detail_level = model_render_determine_detail(depth, objnum, model_num, orient, pos, model_flags, interp->get_detail_level_lock());
2620 
2621 	// If we're rendering attached weapon models, check against the ships' tabled Weapon Model Draw Distance (which defaults to 200)
2622 	if ( model_flags & MR_ATTACHED_MODEL && shipp != NULL ) {
2623 		if (depth > Ship_info[shipp->ship_info_index].weapon_model_draw_distance) {
2624 			scene->pop_transform();
2625 			return;
2626 		}
2627 	}
2628 
2629 // #ifndef NDEBUG
2630 // 	if ( Interp_detail_level == 0 )	{
2631 // 		MONITOR_INC( NumHiModelsRend, 1 );
2632 // 	} else if ( Interp_detail_level == pm->n_detail_levels-1 ) {
2633 // 		MONITOR_INC( NumLowModelsRend, 1 );
2634 // 	}  else {
2635 // 		MONITOR_INC( NumMedModelsRend, 1 );
2636 // 	}
2637 // #endif
2638 
2639 	vec3d auto_back = ZERO_VECTOR;
2640 	bool set_autocen = model_render_determine_autocenter(&auto_back, pm, detail_level, model_flags);
2641 
2642 	if ( set_autocen ) {
2643 		scene->push_transform(&auto_back, NULL);
2644 	}
2645 
2646 	// if we're in nebula mode, fog everything except for the warp holes and other non-fogged models
2647 	if ( (The_mission.flags[Mission::Mission_Flags::Fullneb]) && (Neb2_render_mode != NEB2_RENDER_NONE) && !(model_flags & MR_NO_FOGGING) ) {
2648 		float fog_near = 10.0f, fog_far = 1000.0f;
2649 		neb2_get_adjusted_fog_values(&fog_near, &fog_far, nullptr, objp);
2650 
2651 		unsigned char r, g, b;
2652 		neb2_get_fog_color(&r, &g, &b);
2653 
2654 		rendering_material.set_fog(r, g, b, fog_near, fog_far);
2655 	} else {
2656 		rendering_material.set_fog();
2657 	}
2658 
2659 	if ( is_outlines_only_htl ) {
2660 		rendering_material.set_fill_mode(GR_FILL_MODE_WIRE);
2661 
2662 		tmap_flags &= ~TMAP_FLAG_RGB;
2663 	} else {
2664 		rendering_material.set_fill_mode(GR_FILL_MODE_SOLID);
2665 	}
2666 
2667 	color clr = interp->get_color();
2668 	rendering_material.set_color(clr);
2669 
2670 	if ( model_flags & MR_EDGE_ALPHA ) {
2671 		rendering_material.set_center_alpha(-1);
2672 	} else if ( model_flags & MR_CENTER_ALPHA ) {
2673 		rendering_material.set_center_alpha(1);
2674 	} else {
2675 		rendering_material.set_center_alpha(0);
2676 	}
2677 
2678 	if ( interp->is_normal_alpha_set() ) {
2679 		rendering_material.set_normal_alpha(interp->get_normal_alpha_min(), interp->get_normal_alpha_max());
2680 	}
2681 
2682 	if ( interp->uses_thick_outlines() ) {
2683 		rendering_material.set_outline_thickness(interp->get_outline_thickness());
2684 	}
2685 
2686 	if ( ( model_flags & MR_NO_CULL ) || ( model_flags & MR_ALL_XPARENT ) || ( interp->get_warp_bitmap() >= 0 ) ) {
2687 		rendering_material.set_cull_mode(false);
2688 	} else {
2689 		rendering_material.set_cull_mode(true);
2690 	}
2691 
2692 	if ( !(model_flags & MR_NO_LIGHTING) ) {
2693 		rendering_material.set_lighting(true);
2694 	} else {
2695 		rendering_material.set_lighting(false);
2696 	}
2697 
2698 	Assertion(!(model_flags & MR_STENCIL_READ && model_flags & MR_STENCIL_WRITE),
2699 			  "Enabling stencil read and write at the same time is not supported!");
2700 
2701 	if (model_flags & MR_STENCIL_READ) {
2702 		rendering_material.set_stencil_test(true);
2703 		rendering_material.set_stencil_func(ComparisionFunction::NotEqual, 1, 0xFFFF);
2704 		rendering_material.set_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Keep);
2705 	} else if (model_flags & MR_STENCIL_WRITE) {
2706 		rendering_material.set_stencil_test(true);
2707 		rendering_material.set_stencil_func(ComparisionFunction::Always, 1, 0xFFFF);
2708 		rendering_material.set_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Replace);
2709 	} else {
2710 		rendering_material.set_stencil_test(false);
2711 		rendering_material.set_stencil_func(ComparisionFunction::Never, 1, 0xFFFF);
2712 		rendering_material.set_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Keep);
2713 	}
2714 
2715 	if (model_flags & MR_NO_COLOR_WRITES) {
2716 		rendering_material.set_color_mask(false, false, false, false);
2717 	} else {
2718 		rendering_material.set_color_mask(true, true, true, true);
2719 	}
2720 
2721 	if ( Rendering_to_shadow_map ) {
2722 		rendering_material.set_depth_bias(-1024);
2723 	} else {
2724 		rendering_material.set_depth_bias(0);
2725 	}
2726 
2727 	if ( !(model_flags & MR_NO_BATCH) && pm->flags & PM_FLAG_BATCHED
2728 		&& !(is_outlines_only || is_outlines_only_htl) ) {
2729 		// always set batched rendering on if supported
2730 		tmap_flags |= TMAP_FLAG_BATCH_TRANSFORMS;
2731 	}
2732 
2733 	if ( (tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS) ) {
2734 		scene->start_model_batch(pm->n_models);
2735 		model_render_buffers(scene, &rendering_material, interp, &pm->detail_buffers[detail_level], pm, -1, detail_level, tmap_flags);
2736 	}
2737 
2738 	// Draw the subobjects
2739 	bool draw_thrusters = false;
2740 	bool trans_buffer = false;
2741 	i = pm->submodel[pm->detail[detail_level]].first_child;
2742 
2743 	while( i >= 0 )	{
2744 		if ( !pm->submodel[i].is_thruster ) {
2745 			model_render_children_buffers( scene, &rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
2746 		} else {
2747 			draw_thrusters = true;
2748 		}
2749 
2750 		i = pm->submodel[i].next_sibling;
2751 	}
2752 
2753 	model_radius = pm->submodel[pm->detail[detail_level]].rad;
2754 
2755 	//*************************** draw the hull of the ship *********************************************
2756 	vec3d view_pos = scene->get_view_position();
2757 
2758 	if ( model_render_check_detail_box(&view_pos, pm, pm->detail[detail_level], model_flags) ) {
2759 		int detail_model_num = pm->detail[detail_level];
2760 
2761 		if ( (is_outlines_only || is_outlines_only_htl) && pm->submodel[detail_model_num].outline_buffer != NULL ) {
2762 			color outline_color = interp->get_color();
2763 			scene->add_outline(pm->submodel[detail_model_num].outline_buffer, pm->submodel[detail_model_num].n_verts_outline, &outline_color);
2764 		} else {
2765 			model_render_buffers(scene, &rendering_material, interp, &pm->submodel[detail_model_num].buffer, pm, detail_model_num, detail_level, tmap_flags);
2766 
2767 			if ( pmi != nullptr && pmi->submodel[detail_model_num].num_arcs > 0 ) {
2768 				model_render_add_lightning( scene, interp, pm, &pmi->submodel[detail_model_num] );
2769 			}
2770 		}
2771 	}
2772 
2773 	// make sure batch rendering is unconditionally off.
2774 	tmap_flags &= ~TMAP_FLAG_BATCH_TRANSFORMS;
2775 
2776 	if ( pm->flags & PM_FLAG_TRANS_BUFFER && !(is_outlines_only || is_outlines_only_htl) ) {
2777 		trans_buffer = true;
2778 		i = pm->submodel[pm->detail[detail_level]].first_child;
2779 
2780 		while( i >= 0 )	{
2781 			if ( !pm->submodel[i].is_thruster ) {
2782 				model_render_children_buffers( scene, &rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
2783 			}
2784 
2785 			i = pm->submodel[i].next_sibling;
2786 		}
2787 
2788 		view_pos = scene->get_view_position();
2789 
2790 		if ( model_render_check_detail_box(&view_pos, pm, pm->detail[detail_level], model_flags) ) {
2791 			int detail_model_num = pm->detail[detail_level];
2792 			model_render_buffers(scene, &rendering_material, interp, &pm->submodel[detail_model_num].trans_buffer, pm, detail_model_num, detail_level, tmap_flags);
2793 		}
2794 	}
2795 
2796 	// Draw the thruster subobjects
2797 	if ( draw_thrusters && !(is_outlines_only || is_outlines_only_htl) ) {
2798 		i = pm->submodel[pm->detail[detail_level]].first_child;
2799 		trans_buffer = false;
2800 
2801 		while( i >= 0 ) {
2802 			if (pm->submodel[i].is_thruster) {
2803 				model_render_children_buffers( scene, &rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
2804 			}
2805 			i = pm->submodel[i].next_sibling;
2806 		}
2807 	}
2808 
2809 	if ( !( model_flags & MR_NO_TEXTURING ) ) {
2810 		scene->add_insignia(interp, pm, detail_level, interp->get_insignia_bitmap());
2811 	}
2812 
2813 	if ( (model_flags & MR_AUTOCENTER) && (set_autocen) ) {
2814 		scene->pop_transform();
2815 	}
2816 
2817 	scene->pop_transform();
2818 
2819 	// start rendering glow points -Bobboau
2820 	if ( (pm->n_glow_point_banks) && !is_outlines_only && !is_outlines_only_htl && !Glowpoint_override ) {
2821 		model_render_glow_points(pm, pmi, shipp, orient, pos, Glowpoint_use_depth_buffer);
2822 	}
2823 
2824 	// Draw the thruster glow
2825 	if ( !is_outlines_only && !is_outlines_only_htl ) {
2826 		if ( ( model_flags & MR_AUTOCENTER ) && set_autocen ) {
2827 			vec3d autoback_rotated;
2828 
2829 			vm_vec_unrotate(&autoback_rotated, &auto_back, orient);
2830 			vm_vec_add2(&autoback_rotated, pos);
2831 
2832 			model_queue_render_thrusters( interp, pm, objnum, shipp, orient, &autoback_rotated );
2833 		} else {
2834 			model_queue_render_thrusters( interp, pm, objnum, shipp, orient, pos );
2835 		}
2836 	}
2837 }
2838 
model_render_set_wireframe_color(color * clr)2839 void model_render_set_wireframe_color(color* clr)
2840 {
2841 	Wireframe_color = *clr;
2842 }
2843