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