Files
fs2open.github.com/code/model/modelrender.cpp
T

2862 lines
82 KiB
C++

/*
* Copyright (C) Freespace Open 2013. All rights reserved.
*
* All source code herein is the property of Freespace Open. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include <algorithm>
#include "asteroid/asteroid.h"
#include "cmdline/cmdline.h"
#include "gamesequence/gamesequence.h"
#include "graphics/gropengldraw.h"
#include "graphics/gropenglshader.h"
#include "graphics/tmapper.h"
#include "io/timer.h"
#include "math/staticrand.h"
#include "model/modelrender.h"
#include "nebula/neb.h"
#include "particle/particle.h"
#include "render/3dinternal.h"
#include "render/batching.h"
#include "math/staticrand.h"
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "weapon/weapon.h"
extern int Model_texturing;
extern int Model_polys;
extern int tiling;
extern float model_radius;
extern const int MAX_ARC_SEGMENT_POINTS;
extern int Num_arc_segment_points;
extern vec3d Arc_segment_points[];
extern bool Scene_framebuffer_in_frame;
extern void interp_render_arc_segment( vec3d *v1, vec3d *v2, int depth );
draw_list *draw_list::Target = NULL;
model_batch_buffer TransformBufferHandler;
model_render_params::model_render_params():
Model_flags(MR_NORMAL),
Debug_flags(0),
Objnum(-1),
Detail_level_locked(-1),
Depth_scale(1500.0f),
Warp_bitmap(-1),
Warp_alpha(-1.0f),
Xparent_alpha(1.0f),
Forced_bitmap(-1),
Insignia_bitmap(-1),
Replacement_textures(NULL),
Team_color_set(false),
Clip_plane_set(false),
Animated_effect(-1),
Animated_timer(0.0f),
Thruster_info(),
Normal_alpha(false),
Normal_extrude(false)
{
Warp_scale.xyz.x = 1.0f;
Warp_scale.xyz.y = 1.0f;
Warp_scale.xyz.z = 1.0f;
Clip_normal = vmd_zero_vector;
Clip_pos = vmd_zero_vector;
if ( !Model_texturing )
Model_flags |= MR_NO_TEXTURING;
if ( !Model_polys ) {
Model_flags |= MR_NO_POLYS;
}
gr_init_color(&Color, 0, 0, 0);
}
uint model_render_params::get_model_flags()
{
return Model_flags;
}
uint model_render_params::get_debug_flags()
{
return Debug_flags;
}
int model_render_params::get_object_number()
{
return Objnum;
}
int model_render_params::get_detail_level_lock()
{
return Detail_level_locked;
}
float model_render_params::get_depth_scale()
{
return Depth_scale;
}
int model_render_params::get_warp_bitmap()
{
return Warp_bitmap;
}
float model_render_params::get_warp_alpha()
{
return Warp_alpha;
}
const vec3d& model_render_params::get_warp_scale()
{
return Warp_scale;
}
const color& model_render_params::get_color()
{
return Color;
}
float model_render_params::get_alpha()
{
return Xparent_alpha;
}
int model_render_params::get_forced_bitmap()
{
return Forced_bitmap;
}
int model_render_params::get_insignia_bitmap()
{
return Insignia_bitmap;
}
const int* model_render_params::get_replacement_textures()
{
return Replacement_textures;
}
const team_color& model_render_params::get_team_color()
{
return Current_team_color;
}
const vec3d& model_render_params::get_clip_plane_pos()
{
return Clip_pos;
}
const vec3d& model_render_params::get_clip_plane_normal()
{
return Clip_normal;
}
int model_render_params::get_animated_effect_num()
{
return Animated_effect;
}
float model_render_params::get_animated_effect_timer()
{
return Animated_timer;
}
void model_render_params::set_animated_effect(int effect_num, float timer)
{
Animated_effect = effect_num;
Animated_timer = timer;
}
void model_render_params::set_clip_plane(vec3d &pos, vec3d &normal)
{
Clip_plane_set = true;
Clip_normal = normal;
Clip_pos = pos;
}
bool model_render_params::is_clip_plane_set()
{
return Clip_plane_set;
}
void model_render_params::set_team_color(team_color &clr)
{
Team_color_set = true;
Current_team_color = clr;
}
void model_render_params::set_team_color(const SCP_string &team, const SCP_string &secondaryteam, fix timestamp, int fadetime)
{
Team_color_set = model_get_team_color(&Current_team_color, team, secondaryteam, timestamp, fadetime);
}
bool model_render_params::is_team_color_set()
{
return Team_color_set;
}
void model_render_params::set_replacement_textures(int *textures)
{
Replacement_textures = textures;
}
void model_render_params::set_insignia_bitmap(int bitmap)
{
Insignia_bitmap = bitmap;
}
void model_render_params::set_forced_bitmap(int bitmap)
{
Forced_bitmap = bitmap;
}
void model_render_params::set_alpha(float alpha)
{
Xparent_alpha = alpha;
}
void model_render_params::set_color(color &clr)
{
Color = clr;
}
void model_render_params::set_color(int r, int g, int b)
{
gr_init_color( &Color, r, g, b );
}
void model_render_params::set_warp_params(int bitmap, float alpha, vec3d &scale)
{
Warp_bitmap = bitmap;
Warp_alpha = alpha;
Warp_scale = scale;
}
void model_render_params::set_depth_scale(float scale)
{
Depth_scale = scale;
}
void model_render_params::set_debug_flags(uint flags)
{
Debug_flags = flags;
}
void model_render_params::set_object_number(int num)
{
Objnum = num;
}
void model_render_params::set_flags(uint flags)
{
Model_flags = flags;
}
void model_render_params::set_detail_level_lock(int detail_level_lock)
{
Detail_level_locked = detail_level_lock;
}
void model_render_params::set_thruster_info(mst_info &info)
{
Thruster_info = info;
CLAMP(Thruster_info.length.xyz.z, 0.1f, 1.0f);
}
const mst_info& model_render_params::get_thruster_info()
{
return Thruster_info;
}
void model_render_params::set_normal_alpha(float min, float max)
{
Normal_alpha = true;
Normal_alpha_min = min;
Normal_alpha_max = max;
}
bool model_render_params::is_normal_alpha_set()
{
return Normal_alpha;
}
float model_render_params::get_normal_alpha_min()
{
return Normal_alpha_min;
}
float model_render_params::get_normal_alpha_max()
{
return Normal_alpha_max;
}
void model_render_params::set_normal_extrude_width(float width)
{
Normal_extrude = true;
Normal_extrude_width = width;
}
bool model_render_params::is_normal_extrude_set()
{
return Normal_extrude;
}
float model_render_params::get_normal_extrude_width()
{
return Normal_extrude_width;
}
void model_batch_buffer::reset()
{
Submodel_matrices.clear();
Current_offset = 0;
}
void model_batch_buffer::set_num_models(int n_models)
{
matrix4 init_mat;
vm_matrix4_set_identity(&init_mat);
Current_offset = Submodel_matrices.size();
for ( int i = 0; i < n_models; ++i ) {
Submodel_matrices.push_back(init_mat);
}
}
void model_batch_buffer::set_model_transform(matrix4 &transform, int model_id)
{
Submodel_matrices[Current_offset + model_id] = transform;
}
void model_batch_buffer::add_matrix(matrix4 &mat)
{
Submodel_matrices.push_back(mat);
}
size_t model_batch_buffer::get_buffer_offset()
{
return Current_offset;
}
void model_batch_buffer::allocate_memory()
{
auto size = Submodel_matrices.size() * sizeof(matrix4);
if ( Mem_alloc == NULL || Mem_alloc_size < size ) {
if ( Mem_alloc != NULL ) {
vm_free(Mem_alloc);
}
Mem_alloc = vm_malloc(size);
}
Mem_alloc_size = size;
memcpy(Mem_alloc, &Submodel_matrices[0], size);
}
void model_batch_buffer::submit_buffer_data()
{
if ( Submodel_matrices.size() == 0 ) {
return;
}
allocate_memory();
gr_update_transform_buffer(Mem_alloc, Mem_alloc_size);
}
draw_list::draw_list()
{
reset();
}
void draw_list::reset()
{
Render_elements.clear();
Render_keys.clear();
clear_transforms();
Current_scale.xyz.x = 1.0f;
Current_scale.xyz.y = 1.0f;
Current_scale.xyz.z = 1.0f;
}
void draw_list::sort_draws()
{
Target = this;
std::sort(Target->Render_keys.begin(), Target->Render_keys.end(), draw_list::sort_draw_pair);
}
void draw_list::start_model_batch(int n_models)
{
TransformBufferHandler.set_num_models(n_models);
}
void draw_list::add_submodel_to_batch(int model_num)
{
matrix4 transform;
vm_matrix4_set_identity(&transform);
// set basis
transform.a1d[0] = Current_transform.basis.a1d[0] * Current_scale.xyz.x;
transform.a1d[1] = Current_transform.basis.a1d[1];
transform.a1d[2] = Current_transform.basis.a1d[2];
transform.a1d[4] = Current_transform.basis.a1d[3];
transform.a1d[5] = Current_transform.basis.a1d[4] * Current_scale.xyz.y;
transform.a1d[6] = Current_transform.basis.a1d[5];
transform.a1d[8] = Current_transform.basis.a1d[6];
transform.a1d[9] = Current_transform.basis.a1d[7];
transform.a1d[10] = Current_transform.basis.a1d[8] * Current_scale.xyz.z;
// set position
transform.a1d[12] = Current_transform.origin.a1d[0];
transform.a1d[13] = Current_transform.origin.a1d[1];
transform.a1d[14] = Current_transform.origin.a1d[2];
// set visibility
transform.a1d[15] = 0.0f;
TransformBufferHandler.set_model_transform(transform, model_num);
}
void draw_list::add_arc(vec3d *v1, vec3d *v2, color *primary, color *secondary, float arc_width)
{
arc_effect new_arc;
new_arc.transformation = Current_transform;
new_arc.v1 = *v1;
new_arc.v2 = *v2;
new_arc.primary = *primary;
new_arc.secondary = *secondary;
new_arc.width = arc_width;
Arcs.push_back(new_arc);
}
void draw_list::set_light_filter(int objnum, vec3d *pos, float rad)
{
Scene_light_handler.setLightFilter(objnum, pos, rad);
Current_lights_set = Scene_light_handler.bufferLights();
}
void draw_list::add_buffer_draw(model_material *render_material, indexed_vertex_source *vert_src, vertex_buffer *buffer, size_t texi, uint tmap_flags)
{
queued_buffer_draw draw_data;
render_material->set_deferred_lighting(Deferred_lighting);
render_material->set_high_dynamic_range(High_dynamic_range);
render_material->set_shadow_casting(Rendering_to_shadow_map ? true : false);
if (tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS && buffer->flags & VB_FLAG_MODEL_ID) {
draw_data.transformation = transform();
draw_data.scale.xyz.x = 1.0f;
draw_data.scale.xyz.y = 1.0f;
draw_data.scale.xyz.z = 1.0f;
draw_data.transform_buffer_offset = TransformBufferHandler.get_buffer_offset();
render_material->set_batching(true);
} else {
draw_data.transformation = Current_transform;
draw_data.scale = Current_scale;
draw_data.transform_buffer_offset = INVALID_SIZE;
render_material->set_batching(false);
}
draw_data.sdr_flags = render_material->get_shader_flags();
draw_data.vert_src = vert_src;
draw_data.buffer = buffer;
draw_data.texi = texi;
draw_data.flags = tmap_flags;
draw_data.render_material = *render_material;
draw_data.lights = Current_lights_set;
Render_elements.push_back(draw_data);
Render_keys.push_back(Render_elements.size() - 1);
}
void draw_list::render_buffer(queued_buffer_draw &render_elements)
{
gr_set_transform_buffer_offset(render_elements.transform_buffer_offset);
if ( render_elements.render_material.is_lit() ) {
gr_set_light_factor(render_elements.render_material.get_light_factor());
Scene_light_handler.setLights(&render_elements.lights);
} else {
gr_set_lighting(false, false);
Scene_light_handler.resetLightState();
}
g3_start_instance_matrix(&render_elements.transformation.origin, &render_elements.transformation.basis);
gr_push_scale_matrix(&render_elements.scale);
gr_render_model(&render_elements.render_material, render_elements.vert_src, render_elements.buffer, render_elements.texi);
gr_pop_scale_matrix();
g3_done_instance(true);
}
vec3d draw_list::get_view_position()
{
matrix basis_world;
// get the world basis of our current local space.
vm_matrix_x_matrix(&basis_world, &Object_matrix, &Current_transform.basis);
vec3d eye_pos_local;
vm_vec_sub(&eye_pos_local, &Eye_position, &Current_transform.origin);
vec3d return_val;
vm_vec_rotate(&return_val, &eye_pos_local, &basis_world);
return return_val;
}
void draw_list::clear_transforms()
{
Current_transform = transform();
Transform_stack.clear();
}
void draw_list::push_transform(vec3d *pos, matrix *orient)
{
matrix basis;
vec3d origin;
if ( orient == NULL ) {
basis = vmd_identity_matrix;
} else {
basis = *orient;
}
if ( pos == NULL ) {
origin = vmd_zero_vector;
} else {
origin = *pos;
}
if ( Transform_stack.size() == 0 ) {
Current_transform.basis = basis;
Current_transform.origin = origin;
Transform_stack.push_back(Current_transform);
return;
}
vec3d tempv;
transform newTransform = Current_transform;
vm_vec_unrotate(&tempv, &origin, &Current_transform.basis);
vm_vec_add2(&newTransform.origin, &tempv);
vm_matrix_x_matrix(&newTransform.basis, &Current_transform.basis, &basis);
Current_transform = newTransform;
Transform_stack.push_back(Current_transform);
}
void draw_list::pop_transform()
{
Assert( Transform_stack.size() > 0 );
Transform_stack.pop_back();
if ( Transform_stack.size() > 0 ) {
Current_transform = Transform_stack.back();
} else {
Current_transform = transform();
}
}
void draw_list::set_scale(vec3d *scale)
{
if ( scale == NULL ) {
Current_scale.xyz.x = 1.0f;
Current_scale.xyz.y = 1.0f;
Current_scale.xyz.z = 1.0f;
return;
}
Current_scale = *scale;
}
void draw_list::init()
{
reset();
for ( int i = 0; i < Num_lights; ++i ) {
if ( Lights[i].type == LT_DIRECTIONAL || !Deferred_lighting ) {
Scene_light_handler.addLight(&Lights[i]);
}
}
TransformBufferHandler.reset();
}
void draw_list::init_render(bool sort)
{
if ( sort ) {
sort_draws();
}
TransformBufferHandler.submit_buffer_data();
}
void draw_list::render_all(gr_zbuffer_type depth_mode)
{
Scene_light_handler.resetLightState();
for ( size_t i = 0; i < Render_keys.size(); ++i ) {
int render_index = Render_keys[i];
if ( depth_mode == ZBUFFER_TYPE_DEFAULT || Render_elements[render_index].render_material.get_depth_mode() == depth_mode ) {
render_buffer(Render_elements[render_index]);
}
}
gr_alpha_mask_set(0, 1.0f);
}
void draw_list::render_arc(arc_effect &arc)
{
g3_start_instance_matrix(&arc.transformation.origin, &arc.transformation.basis);
model_render_arc(&arc.v1, &arc.v2, &arc.primary, &arc.secondary, arc.width);
//interp_render_arc(&arc.v1, &arc.v2, &arc.primary, &arc.secondary, arc.width);
g3_done_instance(true);
}
void draw_list::render_arcs()
{
int mode = gr_zbuffer_set(GR_ZBUFF_READ);
for ( size_t i = 0; i < Arcs.size(); ++i ) {
render_arc(Arcs[i]);
}
gr_zbuffer_set(mode);
}
void draw_list::add_insignia(model_render_params *params, polymodel *pm, int detail_level, int bitmap_num)
{
insignia_draw_data new_insignia;
new_insignia.transformation = Current_transform;
new_insignia.pm = pm;
new_insignia.detail_level = detail_level;
new_insignia.bitmap_num = bitmap_num;
new_insignia.clip = params->is_clip_plane_set();
new_insignia.clip_normal = params->get_clip_plane_normal();
new_insignia.clip_position = params->get_clip_plane_pos();
Insignias.push_back(new_insignia);
}
void draw_list::render_insignia(insignia_draw_data &insignia_info)
{
if ( insignia_info.clip ) {
vec3d tmp;
vm_vec_sub(&tmp, &insignia_info.transformation.origin, &insignia_info.clip_position);
vm_vec_normalize(&tmp);
if ( vm_vec_dot(&tmp, &insignia_info.clip_normal) < 0.0f) {
return;
}
}
g3_start_instance_matrix(&insignia_info.transformation.origin, &insignia_info.transformation.basis);
model_render_insignias(&insignia_info);
g3_done_instance(true);
}
void draw_list::render_insignias()
{
for ( size_t i = 0; i < Insignias.size(); ++i ) {
render_insignia(Insignias[i]);
}
}
void draw_list::add_outline(vertex* vert_array, int n_verts, color *clr)
{
outline_draw draw_info;
draw_info.vert_array = vert_array;
draw_info.n_verts = n_verts;
draw_info.clr = *clr;
draw_info.transformation = Current_transform;
Outlines.push_back(draw_info);
}
void draw_list::render_outlines()
{
gr_clear_states();
for ( size_t i = 0; i < Outlines.size(); ++i ) {
render_outline(Outlines[i]);
}
}
void draw_list::render_outline(outline_draw &outline_info)
{
g3_start_instance_matrix(&outline_info.transformation.origin, &outline_info.transformation.basis);
material material_instance;
material_instance.set_depth_mode(ZBUFFER_TYPE_READ);
material_instance.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA);
material_instance.set_color(outline_info.clr);
g3_render_primitives_colored(&material_instance, outline_info.vert_array, outline_info.n_verts, PRIM_TYPE_LINES, false);
g3_done_instance(true);
}
bool draw_list::sort_draw_pair(const int a, const int b)
{
queued_buffer_draw *draw_call_a = &Target->Render_elements[a];
queued_buffer_draw *draw_call_b = &Target->Render_elements[b];
if ( draw_call_a->sdr_flags != draw_call_b->sdr_flags ) {
return draw_call_a->sdr_flags < draw_call_b->sdr_flags;
}
if ( draw_call_a->vert_src->Vbuffer_handle != draw_call_b->vert_src->Vbuffer_handle ) {
return draw_call_a->vert_src->Vbuffer_handle < draw_call_b->vert_src->Vbuffer_handle;
}
if ( draw_call_a->vert_src->Ibuffer_handle != draw_call_b->vert_src->Ibuffer_handle ) {
return draw_call_a->vert_src->Ibuffer_handle < draw_call_b->vert_src->Ibuffer_handle;
}
if ( draw_call_a->render_material.get_texture_map(TM_BASE_TYPE) != draw_call_b->render_material.get_texture_map(TM_BASE_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_BASE_TYPE) < draw_call_b->render_material.get_texture_map(TM_BASE_TYPE);
}
if ( draw_call_a->render_material.get_texture_map(TM_SPECULAR_TYPE) != draw_call_b->render_material.get_texture_map(TM_SPECULAR_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_SPECULAR_TYPE) < draw_call_b->render_material.get_texture_map(TM_SPECULAR_TYPE);
}
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) ) {
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);
}
if ( draw_call_a->render_material.get_texture_map(TM_GLOW_TYPE) != draw_call_b->render_material.get_texture_map(TM_GLOW_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_GLOW_TYPE) < draw_call_b->render_material.get_texture_map(TM_GLOW_TYPE);
}
if ( draw_call_a->render_material.get_texture_map(TM_NORMAL_TYPE) != draw_call_b->render_material.get_texture_map(TM_NORMAL_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_NORMAL_TYPE) < draw_call_b->render_material.get_texture_map(TM_NORMAL_TYPE);
}
if ( draw_call_a->render_material.get_texture_map(TM_HEIGHT_TYPE) != draw_call_b->render_material.get_texture_map(TM_HEIGHT_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_HEIGHT_TYPE) < draw_call_b->render_material.get_texture_map(TM_HEIGHT_TYPE);
}
if ( draw_call_a->render_material.get_texture_map(TM_AMBIENT_TYPE) != draw_call_b->render_material.get_texture_map(TM_AMBIENT_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_AMBIENT_TYPE) < draw_call_b->render_material.get_texture_map(TM_AMBIENT_TYPE);
}
if ( draw_call_a->render_material.get_texture_map(TM_MISC_TYPE) != draw_call_b->render_material.get_texture_map(TM_MISC_TYPE) ) {
return draw_call_a->render_material.get_texture_map(TM_MISC_TYPE) < draw_call_b->render_material.get_texture_map(TM_MISC_TYPE);
}
return draw_call_a->lights.index_start < draw_call_b->lights.index_start;
}
void model_render_add_lightning( draw_list *scene, model_render_params* interp, polymodel *pm, bsp_info * sm )
{
int i;
float width = 0.9f;
color primary, secondary;
const int AR = 64;
const int AG = 64;
const int AB = 5;
const int AR2 = 128;
const int AG2 = 128;
const int AB2 = 10;
Assert( sm->num_arcs > 0 );
if ( interp->get_model_flags() & MR_SHOW_OUTLINE_PRESET ) {
return;
}
extern int Interp_lightning;
if ( !Interp_lightning ) {
return;
}
// try and scale the size a bit so that it looks equally well on smaller vessels
if ( pm->rad < 500.0f ) {
width *= (pm->rad * 0.01f);
if ( width < 0.2f ) {
width = 0.2f;
}
}
for ( i = 0; i < sm->num_arcs; i++ ) {
// pick a color based upon arc type
switch ( sm->arc_type[i] ) {
// "normal", FreeSpace 1 style arcs
case MARC_TYPE_NORMAL:
if ( (rand()>>4) & 1 ) {
gr_init_color(&primary, 64, 64, 255);
} else {
gr_init_color(&primary, 128, 128, 255);
}
gr_init_color(&secondary, 200, 200, 255);
break;
// "EMP" style arcs
case MARC_TYPE_EMP:
if ( (rand()>>4) & 1 ) {
gr_init_color(&primary, AR, AG, AB);
} else {
gr_init_color(&primary, AR2, AG2, AB2);
}
gr_init_color(&secondary, 255, 255, 10);
break;
default:
Int3();
}
// render the actual arc segment
scene->add_arc(&sm->arc_pts[i][0], &sm->arc_pts[i][1], &primary, &secondary, width);
}
}
float model_render_determine_depth(int obj_num, int model_num, matrix* orient, vec3d* pos, int detail_level_locked)
{
vec3d closest_pos;
float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
if ( detail_level_locked < 0 ) {
switch (Detail.detail_distance) {
case 0: // lowest
depth /= The_mission.ai_profile->detail_distance_mult[0];
break;
case 1: // lower than normal
depth /= The_mission.ai_profile->detail_distance_mult[1];
break;
case 2: // default
depth /= The_mission.ai_profile->detail_distance_mult[2];
break;
case 3: // above normal
depth /= The_mission.ai_profile->detail_distance_mult[3];
break;
case 4: // even more normal
depth /= The_mission.ai_profile->detail_distance_mult[4];
break;
}
// nebula ?
if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
depth *= neb2_get_lod_scale(obj_num);
}
}
return depth;
}
int model_render_determine_detail(float depth, int obj_num, int model_num, matrix* orient, vec3d* pos, int flags, int detail_level_locked)
{
int tmp_detail_level = Game_detail_level;
polymodel *pm = model_get(model_num);
Assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS );
int i;
if ( pm->n_detail_levels > 1 ) {
if ( detail_level_locked >= 0 ) {
i = detail_level_locked+1;
} else {
#if MAX_DETAIL_LEVEL != 4
#error Code in modelrender.cpp assumes MAX_DETAIL_LEVEL == 4
#endif
for ( i = 0; i < pm->n_detail_levels; i++ ) {
if ( depth <= pm->detail_depth[i] ) {
break;
}
}
// If no valid detail depths specified, use highest.
if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f) ) {
i = 1;
}
}
int detail_level = i - 1 - tmp_detail_level;
if ( detail_level < 0 ) {
return 0;
} else if ( detail_level >= pm->n_detail_levels ) {
return pm->n_detail_levels - 1;
}
return detail_level;
} else {
return 0;
}
}
void model_render_buffers(draw_list* scene, model_material *rendering_material, model_render_params* interp, vertex_buffer *buffer, polymodel *pm, int mn, int detail_level, uint tmap_flags)
{
if ( pm->vertex_buffer_id < 0 ) {
return;
}
bsp_info *model = NULL;
const uint model_flags = interp->get_model_flags();
const int obj_num = interp->get_object_number();
Assert(buffer != NULL);
Assert(detail_level >= 0);
if ( (mn >= 0) && (mn < pm->n_models) ) {
model = &pm->submodel[mn];
}
bool render_as_thruster = (model != NULL) && model->is_thruster && (model_flags & MR_SHOW_THRUSTERS);
vec3d scale;
if ( render_as_thruster ) {
scale.xyz.x = 1.0f;
scale.xyz.y = 1.0f;
if ( is_minimum_GLSL_version() ) {
scale.xyz.z = 1.0f;
rendering_material->set_thrust_scale(interp->get_thruster_info().length.xyz.z);
} else {
scale.xyz.z = interp->get_thruster_info().length.xyz.z;
rendering_material->set_thrust_scale();
}
} else {
scale = interp->get_warp_scale();
rendering_material->set_thrust_scale();
}
scene->set_scale(&scale);
if ( tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS && (mn >= 0) && (mn < pm->n_models) ) {
scene->add_submodel_to_batch(mn);
return;
}
fix base_frametime = model_render_determine_base_frametime(obj_num, model_flags);
texture_info tex_replace[TM_NUM_TYPES];
int no_texturing = model_flags & MR_NO_TEXTURING;
int forced_texture = -2;
float forced_alpha = 1.0f;
int forced_blend_filter = GR_ALPHABLEND_NONE;
if ( interp->get_forced_bitmap() >= 0 ) {
forced_texture = interp->get_forced_bitmap();
} else if ( interp->get_warp_bitmap() >= 0 ) {
forced_texture = interp->get_warp_bitmap();
forced_alpha = interp->get_warp_alpha();
forced_blend_filter = GR_ALPHABLEND_FILTER;
} else if ( render_as_thruster ) {
if ( ( interp->get_thruster_info().primary_bitmap >= 0 ) && ( interp->get_thruster_info().length.xyz.z > 0.0f ) ) {
forced_texture = interp->get_thruster_info().primary_bitmap;
} else {
forced_texture = -1;
}
forced_alpha = 1.2f;
forced_blend_filter = GR_ALPHABLEND_FILTER;
} else if ( model_flags & MR_ALL_XPARENT ) {
forced_alpha = interp->get_alpha();
forced_blend_filter = GR_ALPHABLEND_FILTER;
}
int texture_maps[TM_NUM_TYPES] = { -1 };
size_t buffer_size = buffer->tex_buf.size();
const int *replacement_textures = interp->get_replacement_textures();
for ( size_t i = 0; i < buffer_size; i++ ) {
int tmap_num = buffer->tex_buf[i].texture;
texture_map *tmap = &pm->maps[tmap_num];
int rt_begin_index = tmap_num*TM_NUM_TYPES;
float alpha = 1.0f;
int blend_filter = GR_ALPHABLEND_NONE;
texture_maps[TM_BASE_TYPE] = -1;
texture_maps[TM_GLOW_TYPE] = -1;
texture_maps[TM_SPECULAR_TYPE] = -1;
texture_maps[TM_NORMAL_TYPE] = -1;
texture_maps[TM_HEIGHT_TYPE] = -1;
texture_maps[TM_MISC_TYPE] = -1;
texture_maps[TM_SPEC_GLOSS_TYPE] = -1;
texture_maps[TM_UNLIT_TYPE] = -1;
texture_maps[TM_AMBIENT_TYPE] = -1;
if (forced_texture != -2) {
texture_maps[TM_BASE_TYPE] = forced_texture;
alpha = forced_alpha;
if (interp->get_warp_bitmap() >= 0) {
texture_maps[TM_GLOW_TYPE] = forced_texture;
}
} else if ( !no_texturing ) {
// pick the texture, animating it if necessary
if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_BASE_TYPE] == REPLACE_WITH_INVISIBLE) ) {
// invisible textures aren't rendered, but we still have to skip assigning the underlying model texture
texture_maps[TM_BASE_TYPE] = -1;
} else if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0) ) {
// an underlying texture is replaced with a real new texture
tex_replace[TM_BASE_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_BASE_TYPE]);
texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], base_frametime);
} else {
// we just use the underlying texture
texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], base_frametime);
}
if ( texture_maps[TM_BASE_TYPE] < 0 ) {
continue;
}
if (replacement_textures != NULL && replacement_textures[rt_begin_index + TM_UNLIT_TYPE] >= 0) {
tex_replace[TM_UNLIT_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_UNLIT_TYPE]);
texture_maps[TM_UNLIT_TYPE] = model_interp_get_texture(&tex_replace[TM_UNLIT_TYPE], base_frametime);
} else {
texture_maps[TM_UNLIT_TYPE] = model_interp_get_texture(&tmap->textures[TM_UNLIT_TYPE], base_frametime);
}
if ( (texture_maps[TM_UNLIT_TYPE] >= 0) && (model_flags & MR_NO_LIGHTING) && (buffer->flags & VB_FLAG_TRANS) ) {
// don't render transparent buffers for unlit textures in no lighting mode.
continue;
}
// doing glow maps?
if ( !(model_flags & MR_NO_GLOWMAPS) ) {
texture_info *tglow = &tmap->textures[TM_GLOW_TYPE];
if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_GLOW_TYPE] >= 0) ) {
tex_replace[TM_GLOW_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_GLOW_TYPE]);
texture_maps[TM_GLOW_TYPE] = model_interp_get_texture(&tex_replace[TM_GLOW_TYPE], base_frametime);
} else if (tglow->GetTexture() >= 0) {
// shockwaves are special, their current frame has to come out of the shockwave code to get the timing correct
if ( (obj_num >= 0) && (Objects[obj_num].type == OBJ_SHOCKWAVE) && (tglow->GetNumFrames() > 1) ) {
texture_maps[TM_GLOW_TYPE] = tglow->GetTexture() + shockwave_get_framenum(Objects[obj_num].instance, tglow->GetTexture());
} else {
texture_maps[TM_GLOW_TYPE] = model_interp_get_texture(tglow, base_frametime);
}
}
}
if ( replacement_textures != NULL && replacement_textures[rt_begin_index + TM_SPECULAR_TYPE] >= 0 ) {
tex_replace[TM_SPECULAR_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_SPECULAR_TYPE]);
texture_maps[TM_SPECULAR_TYPE] = model_interp_get_texture(&tex_replace[TM_SPECULAR_TYPE], base_frametime);
} else {
texture_maps[TM_SPECULAR_TYPE] = model_interp_get_texture(&tmap->textures[TM_SPECULAR_TYPE], base_frametime);
}
if ( replacement_textures != NULL && replacement_textures[rt_begin_index + TM_SPEC_GLOSS_TYPE] >= 0 ) {
tex_replace[TM_SPEC_GLOSS_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_SPEC_GLOSS_TYPE]);
texture_maps[TM_SPEC_GLOSS_TYPE] = model_interp_get_texture(&tex_replace[TM_SPEC_GLOSS_TYPE], base_frametime);
} else {
texture_maps[TM_SPEC_GLOSS_TYPE] = model_interp_get_texture(&tmap->textures[TM_SPEC_GLOSS_TYPE], base_frametime);
}
if ( (Detail.lighting > 2) && (detail_level < 2) ) {
// likewise, etc.
texture_info *norm_map = &tmap->textures[TM_NORMAL_TYPE];
texture_info *height_map = &tmap->textures[TM_HEIGHT_TYPE];
texture_info *ambient_map = &tmap->textures[TM_AMBIENT_TYPE];
texture_info *misc_map = &tmap->textures[TM_MISC_TYPE];
if (replacement_textures != NULL) {
if (replacement_textures[rt_begin_index + TM_NORMAL_TYPE] >= 0) {
tex_replace[TM_NORMAL_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_NORMAL_TYPE]);
norm_map = &tex_replace[TM_NORMAL_TYPE];
}
if (replacement_textures[rt_begin_index + TM_HEIGHT_TYPE] >= 0) {
tex_replace[TM_HEIGHT_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_HEIGHT_TYPE]);
height_map = &tex_replace[TM_HEIGHT_TYPE];
}
if ( replacement_textures[rt_begin_index + TM_AMBIENT_TYPE] >= 0 ) {
tex_replace[TM_AMBIENT_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_AMBIENT_TYPE]);
ambient_map = &tex_replace[TM_AMBIENT_TYPE];
}
if (replacement_textures[rt_begin_index + TM_MISC_TYPE] >= 0) {
tex_replace[TM_MISC_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_MISC_TYPE]);
misc_map = &tex_replace[TM_MISC_TYPE];
}
}
texture_maps[TM_NORMAL_TYPE] = model_interp_get_texture(norm_map, base_frametime);
texture_maps[TM_HEIGHT_TYPE] = model_interp_get_texture(height_map, base_frametime);
texture_maps[TM_AMBIENT_TYPE] = model_interp_get_texture(ambient_map, base_frametime);
texture_maps[TM_MISC_TYPE] = model_interp_get_texture(misc_map, base_frametime);
}
} else {
alpha = forced_alpha;
//Check for invisible or transparent textures so they don't show up in the shadow maps - Valathil
if ( Rendering_to_shadow_map ) {
if ( (replacement_textures != NULL) && (replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0) ) {
tex_replace[TM_BASE_TYPE] = texture_info(replacement_textures[rt_begin_index + TM_BASE_TYPE]);
texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], base_frametime);
} else {
texture_maps[TM_BASE_TYPE] = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], base_frametime);
}
if ( texture_maps[TM_BASE_TYPE] <= 0 ) {
continue;
}
}
}
if ( (texture_maps[TM_BASE_TYPE] == -1) && !no_texturing ) {
continue;
}
bool use_blending = false;
// trying to get transparent textures-Bobboau
if (tmap->is_transparent) {
// for special shockwave/warp map usage
alpha = (interp->get_warp_alpha() != -1.0f) ? interp->get_warp_alpha() : 0.8f;
use_blending = true;
} else if ( buffer->flags & VB_FLAG_TRANS ) {
use_blending = true;
}
if (forced_blend_filter != GR_ALPHABLEND_NONE) {
use_blending = true;
}
bool use_depth_test;
if ( use_blending ) {
use_depth_test = true;
} else {
if ( (model_flags & MR_NO_ZBUFFER) || (model_flags & MR_ALL_XPARENT) ) {
use_depth_test = false;
} else {
use_depth_test = true;
}
}
gr_alpha_blend blend_mode = model_render_determine_blend_mode(texture_maps[TM_BASE_TYPE], use_blending);
gr_zbuffer_type depth_mode = material_determine_depth_mode(use_depth_test, use_blending);
rendering_material->set_depth_mode(depth_mode);
rendering_material->set_blend_mode(blend_mode);
color clr = interp->get_color();
model_render_determine_color(&clr, alpha, blend_mode, no_texturing ? true : false, rendering_material->is_desaturated());
rendering_material->set_color(clr);
if ( (tmap_flags & TMAP_FLAG_TEXTURED) && (buffer->flags & VB_FLAG_UV1) ) {
rendering_material->set_texture_map(TM_BASE_TYPE, texture_maps[TM_BASE_TYPE]);
rendering_material->set_texture_map(TM_UNLIT_TYPE, texture_maps[TM_UNLIT_TYPE]);
if ( ( texture_maps[TM_BASE_TYPE] >= 0 && bm_has_alpha_channel(texture_maps[TM_BASE_TYPE]) ) ||
(texture_maps[TM_UNLIT_TYPE] >= 0 && bm_has_alpha_channel(texture_maps[TM_UNLIT_TYPE])) ) {
rendering_material->set_texture_type(material::TEX_TYPE_XPARENT);
}
rendering_material->set_texture_map(TM_GLOW_TYPE, texture_maps[TM_GLOW_TYPE]);
rendering_material->set_texture_map(TM_SPECULAR_TYPE, texture_maps[TM_SPECULAR_TYPE]);
rendering_material->set_texture_map(TM_SPEC_GLOSS_TYPE, texture_maps[TM_SPEC_GLOSS_TYPE]);
rendering_material->set_texture_map(TM_NORMAL_TYPE, texture_maps[TM_NORMAL_TYPE]);
rendering_material->set_texture_map(TM_HEIGHT_TYPE, texture_maps[TM_HEIGHT_TYPE]);
rendering_material->set_texture_map(TM_AMBIENT_TYPE, texture_maps[TM_AMBIENT_TYPE]);
rendering_material->set_texture_map(TM_MISC_TYPE, texture_maps[TM_MISC_TYPE]);
}
scene->add_buffer_draw(rendering_material, &pm->vert_source, buffer, i, tmap_flags);
}
}
void model_render_children_buffers(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)
{
int i;
if ( (mn < 0) || (mn >= pm->n_models) ) {
Int3();
return;
}
bsp_info *model = &pm->submodel[mn];
submodel_instance *smi = NULL;
if ( pmi != NULL ) {
smi = &pmi->submodel[mn];
}
if ( smi != NULL ) {
if ( smi->blown_off ) {
return;
}
} else if ( model->blown_off ) {
return;
}
const uint model_flags = interp->get_model_flags();
if (model->is_thruster) {
if ( !( model_flags & MR_SHOW_THRUSTERS ) ) {
return;
}
rendering_material->set_lighting(false);
}
vec3d view_pos = scene->get_view_position();
if ( !model_render_check_detail_box(&view_pos, pm, mn, model_flags) ) {
return;
}
// Get submodel rotation data and use submodel orientation matrix
// to put together a matrix describing the final orientation of
// the submodel relative to its parent
angles ang = model->angs;
if ( smi != NULL ) {
ang = smi->angs;
}
// Add barrel rotation if needed
if ( model->gun_rotation ) {
if ( pm->gun_submodel_rotation > PI2 ) {
pm->gun_submodel_rotation -= PI2;
} else if ( pm->gun_submodel_rotation < 0.0f ) {
pm->gun_submodel_rotation += PI2;
}
ang.b += pm->gun_submodel_rotation;
}
// Compute final submodel orientation by using the orientation matrix
// and the rotation angles.
// By using this kind of computation, the rotational angles can always
// be computed relative to the submodel itself, instead of relative
// to the parent
matrix rotation_matrix = model->orientation;
vm_rotate_matrix_by_angles(&rotation_matrix, &ang);
matrix inv_orientation;
vm_copy_transpose(&inv_orientation, &model->orientation);
matrix submodel_matrix;
vm_matrix_x_matrix(&submodel_matrix, &rotation_matrix, &inv_orientation);
scene->push_transform(&model->offset, &submodel_matrix);
if ( (model_flags & MR_SHOW_OUTLINE || model_flags & MR_SHOW_OUTLINE_HTL || model_flags & MR_SHOW_OUTLINE_PRESET) &&
pm->submodel[mn].outline_buffer != NULL ) {
color outline_color = interp->get_color();
scene->add_outline(pm->submodel[mn].outline_buffer, pm->submodel[mn].n_verts_outline, &outline_color);
} else {
if ( trans_buffer && pm->submodel[mn].trans_buffer.flags & VB_FLAG_TRANS ) {
model_render_buffers(scene, rendering_material, interp, &pm->submodel[mn].trans_buffer, pm, mn, detail_level, tmap_flags);
} else {
model_render_buffers(scene, rendering_material, interp, &pm->submodel[mn].buffer, pm, mn, detail_level, tmap_flags);
}
}
if ( model->num_arcs ) {
model_render_add_lightning( scene, interp, pm, &pm->submodel[mn] );
}
i = model->first_child;
while ( i >= 0 ) {
if ( !pm->submodel[i].is_thruster ) {
model_render_children_buffers( scene, rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
}
i = pm->submodel[i].next_sibling;
}
if ( model->is_thruster ) {
rendering_material->set_lighting(true);
}
scene->pop_transform();
}
float model_render_determine_light_factor(model_render_params* interp, vec3d *pos, uint flags)
{
if ( flags & MR_IS_ASTEROID ) {
// Dim it based on distance
float depth = vm_vec_dist_quick( pos, &Eye_position );
if ( depth > interp->get_depth_scale() ) {
float temp_light = interp->get_depth_scale()/depth;
if ( temp_light > 1.0f ) {
return 1.0f;
}
return temp_light;
}
}
return 1.0f;
}
float model_render_determine_box_scale()
{
float box_scale = 1.2f;
// scale the render box settings based on the "Model Detail" slider
switch ( Detail.detail_distance ) {
case 0: // 1st dot is 20%
box_scale = 0.2f;
break;
case 1: // 2nd dot is 50%
box_scale = 0.5f;
break;
case 2: // 3rd dot is 80%
box_scale = 0.8f;
break;
case 3: // 4th dot is 100% (this is the default setting for "High" and "Very High" settings)
box_scale = 1.0f;
break;
case 4: // 5th dot (max) is 120%
default:
box_scale = 1.2f;
break;
}
return box_scale;
}
fix model_render_determine_base_frametime(int objnum, uint flags)
{
// Goober5000
fix base_frametime = 0;
if ( objnum >= 0 ) {
object *objp = &Objects[objnum];
if ( objp->type == OBJ_SHIP ) {
base_frametime = Ships[objp->instance].base_texture_anim_frametime;
}
} else if ( flags & MR_SKYBOX ) {
base_frametime = Skybox_timestamp;
}
return base_frametime;
}
bool model_render_determine_autocenter(vec3d *auto_back, polymodel *pm, int detail_level, uint flags)
{
if ( flags & MR_AUTOCENTER ) {
// standard autocenter using data in model
if ( pm->flags & PM_FLAG_AUTOCEN ) {
*auto_back = pm->autocenter;
vm_vec_scale(auto_back, -1.0f);
return true;
} else if ( flags & MR_IS_MISSILE ) {
// fake autocenter if we are a missile and don't already have autocen info
auto_back->xyz.x = -( (pm->submodel[pm->detail[detail_level]].max.xyz.x + pm->submodel[pm->detail[detail_level]].min.xyz.x) / 2.0f );
auto_back->xyz.y = -( (pm->submodel[pm->detail[detail_level]].max.xyz.y + pm->submodel[pm->detail[detail_level]].min.xyz.y) / 2.0f );
auto_back->xyz.z = -( (pm->submodel[pm->detail[detail_level]].max.xyz.z + pm->submodel[pm->detail[detail_level]].min.xyz.z) / 2.0f );
return true;
}
}
return false;
}
void model_render_determine_color(color *clr, float alpha, gr_alpha_blend blend_mode, bool no_texturing, bool desaturate)
{
clr->alpha = fl2i(alpha * 255.0f);
if ( no_texturing || desaturate ) {
// don't override the given color if we're not texturing or we're desaturating
return;
}
if ( blend_mode == ALPHA_BLEND_ADDITIVE ) {
clr->red = clr->green = clr->blue = clr->alpha;
clr->alpha = 255;
} else {
clr->red = clr->green = clr->blue = 255;
}
}
gr_alpha_blend model_render_determine_blend_mode(int base_bitmap, bool blending)
{
if ( blending ) {
if ( base_bitmap >= 0 && bm_has_alpha_channel(base_bitmap) ) {
return ALPHA_BLEND_PREMULTIPLIED;
}
return ALPHA_BLEND_ADDITIVE;
}
return ALPHA_BLEND_ALPHA_BLEND_ALPHA;
}
bool model_render_check_detail_box(vec3d *view_pos, polymodel *pm, int submodel_num, uint flags)
{
Assert(pm != NULL);
bsp_info *model = &pm->submodel[submodel_num];
float box_scale = model_render_determine_box_scale();
if ( !( flags & MR_FULL_DETAIL ) && model->use_render_box ) {
vec3d box_min, box_max, offset;
if (model->use_render_box_offset) {
offset = model->render_box_offset;
} else {
model_find_submodel_offset(&offset, pm->id, submodel_num);
}
vm_vec_copy_scale(&box_min, &model->render_box_min, box_scale);
vm_vec_copy_scale(&box_max, &model->render_box_max, box_scale);
if ( (-model->use_render_box + in_box(&box_min, &box_max, &offset, view_pos)) ) {
return false;
}
}
if ( !(flags & MR_FULL_DETAIL) && model->use_render_sphere ) {
float sphere_radius = model->render_sphere_radius * box_scale;
// TODO: doesn't consider submodel rotations yet -zookeeper
vec3d offset;
if (model->use_render_sphere_offset) {
offset = model->render_sphere_offset;
} else {
model_find_submodel_offset(&offset, pm->id, submodel_num);
}
if ( (-model->use_render_sphere + in_sphere(&offset, sphere_radius, view_pos)) ) {
return false;
}
}
return true;
}
void submodel_render_immediate(model_render_params *render_info, int model_num, int submodel_num, matrix *orient, vec3d * pos)
{
draw_list model_list;
model_list.init();
submodel_render_queue(render_info, &model_list, model_num, submodel_num, orient, pos);
model_list.render_all();
gr_zbias(0);
gr_zbuffer_set(ZBUFFER_TYPE_READ);
gr_set_cull(0);
gr_set_fill_mode(GR_FILL_MODE_SOLID);
gr_clear_states();
gr_set_buffer(-1);
gr_reset_lighting();
gr_set_lighting(false, false);
}
void submodel_render_queue(model_render_params *render_info, draw_list *scene, int model_num, int submodel_num, matrix *orient, vec3d * pos)
{
polymodel * pm;
model_material rendering_material;
//MONITOR_INC( NumModelsRend, 1 );
if ( !( Game_detail_flags & DETAIL_FLAG_MODELS ) ) return;
if ( render_info->is_clip_plane_set() ) {
rendering_material.set_clip_plane(render_info->get_clip_plane_normal(), render_info->get_clip_plane_pos());
}
if ( render_info->is_team_color_set() ) {
rendering_material.set_team_color(render_info->get_team_color());
}
uint flags = render_info->get_model_flags();
int objnum = render_info->get_object_number();
pm = model_get(model_num);
// Set the flags we will pass to the tmapper
uint tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
// if we're in nebula mode
if( ( The_mission.flags[Mission::Mission_Flags::Fullneb] ) && ( Neb2_render_mode != NEB2_RENDER_NONE ) ) {
tmap_flags |= TMAP_FLAG_PIXEL_FOG;
}
if ( !( flags & MR_NO_TEXTURING ) ) {
tmap_flags |= TMAP_FLAG_TEXTURED;
if ( ( pm->flags & PM_FLAG_ALLOW_TILING ) && tiling )
tmap_flags |= TMAP_FLAG_TILED;
if ( !( flags & MR_NO_CORRECT ) ) {
tmap_flags |= TMAP_FLAG_CORRECT;
}
}
if ( render_info->get_animated_effect_num() >= 0 ) {
tmap_flags |= TMAP_ANIMATED_SHADER;
rendering_material.set_animated_effect(render_info->get_animated_effect_num(), render_info->get_animated_effect_timer());
}
bool is_outlines_only_htl = (flags & MR_NO_POLYS) && (flags & MR_SHOW_OUTLINE_HTL);
//set to true since D3d and OGL need the api matrices set
scene->push_transform(pos, orient);
vec3d auto_back = ZERO_VECTOR;
bool set_autocen = model_render_determine_autocenter(&auto_back, pm, render_info->get_detail_level_lock(), flags);
if ( set_autocen ) {
scene->push_transform(&auto_back, NULL);
}
if (is_outlines_only_htl) {
rendering_material.set_fill_mode(GR_FILL_MODE_WIRE);
color outline_color = render_info->get_color();
rendering_material.set_color(outline_color);
tmap_flags &= ~TMAP_FLAG_RGB;
} else {
rendering_material.set_fill_mode(GR_FILL_MODE_SOLID);
}
rendering_material.set_light_factor(1.0f);
if ( !( flags & MR_NO_LIGHTING ) ) {
scene->set_light_filter(-1, pos, pm->submodel[submodel_num].rad);
rendering_material.set_lighting(true);
} else {
rendering_material.set_lighting(false);
}
// fixes disappearing HUD in OGL - taylor
rendering_material.set_cull_mode(true);
// RT - Put this here to fog debris
if( tmap_flags & TMAP_FLAG_PIXEL_FOG ) {
float fog_near, fog_far;
object *obj = NULL;
if (objnum >= 0)
obj = &Objects[objnum];
neb2_get_adjusted_fog_values(&fog_near, &fog_far, obj);
unsigned char r, g, b;
neb2_get_fog_color(&r, &g, &b);
rendering_material.set_fog(r, g, b, fog_near, fog_far);
} else {
rendering_material.set_fog();
}
if(Rendering_to_shadow_map) {
rendering_material.set_depth_bias(-1024);
} else {
rendering_material.set_depth_bias(0);
}
vec3d view_pos = scene->get_view_position();
if ( model_render_check_detail_box(&view_pos, pm, submodel_num, flags) ) {
model_render_buffers(scene, &rendering_material, render_info, &pm->submodel[submodel_num].buffer, pm, submodel_num, 0, tmap_flags);
if ( pm->flags & PM_FLAG_TRANS_BUFFER && pm->submodel[submodel_num].trans_buffer.flags & VB_FLAG_TRANS ) {
model_render_buffers(scene, &rendering_material, render_info, &pm->submodel[submodel_num].trans_buffer, pm, submodel_num, 0, tmap_flags);
}
}
if ( pm->submodel[submodel_num].num_arcs ) {
model_render_add_lightning( scene, render_info, pm, &pm->submodel[submodel_num] );
}
if ( set_autocen ) {
scene->pop_transform();
}
scene->pop_transform();
}
void model_render_glowpoint(int point_num, vec3d *pos, matrix *orient, glow_point_bank *bank, glow_point_bank_override *gpo, polymodel *pm, ship* shipp, bool use_depth_buffer)
{
glow_point *gpt = &bank->points[point_num];
vec3d loc_offset = gpt->pnt;
vec3d loc_norm = gpt->norm;
vec3d world_pnt;
vec3d world_norm;
vec3d tempv;
vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
bool submodel_rotation = false;
if ( bank->submodel_parent > 0 && pm->submodel[bank->submodel_parent].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) && shipp != NULL ) {
model_find_submodel_offset(&submodel_static_offset, Ship_info[shipp->ship_info_index].model_num, bank->submodel_parent);
submodel_rotation = true;
}
if ( submodel_rotation ) {
vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
tempv = loc_offset;
find_submodel_instance_point_normal(&loc_offset, &loc_norm, shipp->model_instance_num, bank->submodel_parent, &tempv, &loc_norm);
}
vm_vec_unrotate(&world_pnt, &loc_offset, orient);
vm_vec_add2(&world_pnt, pos);
vm_vec_unrotate(&world_norm, &loc_norm, orient);
if ( shipp != NULL ) {
if ( (shipp->is_arriving() ) && (shipp->warpin_effect) && Ship_info[shipp->ship_info_index].warpin_type != WT_HYPERSPACE) {
vec3d warp_pnt, tmp;
matrix warp_orient;
shipp->warpin_effect->getWarpPosition(&warp_pnt);
shipp->warpin_effect->getWarpOrientation(&warp_orient);
vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) < 0.0f ) {
return;
}
}
if ( (shipp->flags[Ship::Ship_Flags::Depart_warp] ) && (shipp->warpout_effect) && Ship_info[shipp->ship_info_index].warpout_type != WT_HYPERSPACE) {
vec3d warp_pnt, tmp;
matrix warp_orient;
shipp->warpout_effect->getWarpPosition(&warp_pnt);
shipp->warpout_effect->getWarpOrientation(&warp_orient);
vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) > 0.0f ) {
return;
}
}
}
switch ((gpo && gpo->type_override)?gpo->type:bank->type)
{
case 0:
{
float d,pulse = 1.0f;
if ( IS_VEC_NULL(&world_norm) ) {
d = 1.0f; //if given a nul vector then always show it
} else {
vm_vec_sub(&tempv,&View_position,&world_pnt);
vm_vec_normalize(&tempv);
d = vm_vec_dot(&tempv,&world_norm);
d -= 0.25;
}
float w = gpt->radius;
if (d > 0.0f) {
vertex p;
d *= 3.0f;
if (d > 1.0f)
d = 1.0f;
// fade them in the nebula as well
if ( The_mission.flags[Mission::Mission_Flags::Fullneb] ) {
//vec3d npnt;
//vm_vec_add(&npnt, &loc_offset, pos);
d *= (1.0f - neb2_get_fog_intensity(&world_pnt));
w *= 1.5; //make it bigger in a nebula
}
g3_transfer_vertex(&p, &world_pnt);
p.r = p.g = p.b = p.a = (ubyte)(255.0f * MAX(d,0.0f));
if((gpo && gpo->glow_bitmap_override)?(gpo->glow_bitmap > -1):(bank->glow_bitmap > -1)) {
int gpflags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_EMISSIVE;
if (use_depth_buffer)
gpflags |= TMAP_FLAG_SOFT_QUAD;
int bitmap_id = (gpo && gpo->glow_bitmap_override) ? gpo->glow_bitmap : bank->glow_bitmap;
if ( use_depth_buffer ) {
batching_add_volume_bitmap(bitmap_id, &p, 0, (w * 0.5f), d * pulse, w);
} else {
batching_add_bitmap(bitmap_id, &p, 0, (w * 0.5f), d * pulse, w);
}
}
} //d>0.0f
if ( gpo && gpo->pulse_type ) {
int period;
if(gpo->pulse_period_override) {
period = gpo->pulse_period;
} else {
if(gpo->on_time_override) {
period = 2 * gpo->on_time;
} else {
period = 2 * bank->on_time;
}
}
int x = 0;
if ( (gpo && gpo->off_time_override) ? gpo->off_time : bank->off_time ) {
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);
} else {
x = (timestamp() - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % gpo->pulse_period;
}
switch ( gpo->pulse_type ) {
case PULSE_SIN:
pulse = gpo->pulse_bias + gpo->pulse_amplitude * pow(sin( PI2 / period * x),gpo->pulse_exponent);
break;
case PULSE_COS:
pulse = gpo->pulse_bias + gpo->pulse_amplitude * pow(cos( PI2 / period * x),gpo->pulse_exponent);
break;
case PULSE_SHIFTTRI:
x += period / 4;
if((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) {
x %= ( ((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) );
} else {
x %= gpo->pulse_period;
}
case PULSE_TRI:
float inv;
if( x > period / 2) {
inv = -1;
} else {
inv = 1;
}
if( x > period / 4) {
pulse = gpo->pulse_bias + gpo->pulse_amplitude * inv * pow( 1.0f - ((x - period / 4.0f) * 4 / period) ,gpo->pulse_exponent);
} else {
pulse = gpo->pulse_bias + gpo->pulse_amplitude * inv * pow( (x * 4.0f / period) ,gpo->pulse_exponent);
}
break;
}
}
if ( Deferred_lighting && gpo && gpo->is_lightsource ) {
if ( gpo->lightcone ) {
vec3d cone_dir_rot;
vec3d cone_dir_model;
vec3d cone_dir_world;
vec3d cone_dir_screen;
vec3d unused;
if ( gpo->rotating ) {
vm_rot_point_around_line(&cone_dir_rot, &gpo->cone_direction, PI * timestamp() * 0.000033333f * gpo->rotation_speed, &vmd_zero_vector, &gpo->rotation_axis);
} else {
cone_dir_rot = gpo->cone_direction;
}
find_submodel_instance_point_normal(&unused, &cone_dir_model, shipp->model_instance_num, bank->submodel_parent, &unused, &cone_dir_rot);
vm_vec_unrotate(&cone_dir_world, &cone_dir_model, orient);
vm_vec_rotate(&cone_dir_screen, &cone_dir_world, &Eye_matrix);
cone_dir_screen.xyz.z = -cone_dir_screen.xyz.z;
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);
} else {
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);
}
}
break;
}
case 1:
{
vertex verts[4];
vec3d fvec, top1, bottom1, top2, bottom2, start, end;
vm_vec_add2(&loc_norm, &loc_offset);
vm_vec_rotate(&start, &loc_offset, orient);
vm_vec_rotate(&end, &loc_norm, orient);
vm_vec_sub(&fvec, &end, &start);
vm_vec_normalize(&fvec);
moldel_calc_facing_pts(&top1, &bottom1, &fvec, &loc_offset, gpt->radius, 1.0f, &View_position);
moldel_calc_facing_pts(&top2, &bottom2, &fvec, &loc_norm, gpt->radius, 1.0f, &View_position);
g3_transfer_vertex(&verts[0], &bottom1);
g3_transfer_vertex(&verts[1], &bottom2);
g3_transfer_vertex(&verts[2], &top2);
g3_transfer_vertex(&verts[3], &top1);
verts[0].texture_position.u = 0.0f;
verts[0].texture_position.v = 0.0f;
verts[1].texture_position.u = 1.0f;
verts[1].texture_position.v = 0.0f;
verts[2].texture_position.u = 1.0f;
verts[2].texture_position.v = 1.0f;
verts[3].texture_position.u = 0.0f;
verts[3].texture_position.v = 1.0f;
vm_vec_sub(&tempv,&View_position,&loc_offset);
vm_vec_normalize(&tempv);
if ( The_mission.flags[Mission::Mission_Flags::Fullneb] ) {
batching_add_quad(bank->glow_neb_bitmap, verts);
} else {
batching_add_quad(bank->glow_bitmap, verts);
}
break;
}
}
}
void model_render_set_glow_points(polymodel *pm, int objnum)
{
int time = timestamp();
glow_point_bank_override *gpo = NULL;
bool override_all = false;
SCP_unordered_map<int, void*>::iterator gpoi;
ship_info *sip = NULL;
ship *shipp = NULL;
if ( Glowpoint_override ) {
return;
}
if ( objnum > -1 ) {
object *objp = &Objects[objnum];
if ( objp != NULL && objp->type == OBJ_SHIP ) {
shipp = &Ships[Objects[objnum].instance];
sip = &Ship_info[shipp->ship_info_index];
gpoi = sip->glowpoint_bank_override_map.find(-1);
if (gpoi != sip->glowpoint_bank_override_map.end()) {
override_all = true;
gpo = (glow_point_bank_override*)sip->glowpoint_bank_override_map[-1];
}
}
}
for ( int i = 0; i < pm->n_glow_point_banks; i++ ) { //glow point blink code -Bobboau
glow_point_bank *bank = &pm->glow_point_banks[i];
if ( !override_all && sip ) {
gpoi = sip->glowpoint_bank_override_map.find(i);
if ( gpoi != sip->glowpoint_bank_override_map.end() ) {
gpo = (glow_point_bank_override*) sip->glowpoint_bank_override_map[i];
} else {
gpo = NULL;
}
}
if ( bank->glow_timestamp == 0 ) {
bank->glow_timestamp=time;
}
if ( ( gpo && gpo->off_time_override ) ? gpo->off_time : bank->off_time ) {
if ( bank->is_on ) {
if( ((gpo && gpo->on_time_override) ? gpo->on_time : bank->on_time) > ((time - ((gpo && gpo->disp_time_override) ? gpo->disp_time : bank->disp_time)) % (((gpo && gpo->on_time_override) ? gpo->on_time : bank->on_time) + ((gpo && gpo->off_time_override) ? gpo->off_time : bank->off_time))) ){
bank->glow_timestamp = time;
bank->is_on = 0;
}
} else {
if( ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) < ((time - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % (((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time))) ){
bank->glow_timestamp = time;
bank->is_on = 1;
}
}
}
}
}
void model_render_glow_points(polymodel *pm, ship *shipp, matrix *orient, vec3d *pos, bool use_depth_buffer = true)
{
if ( Rendering_to_shadow_map ) {
return;
}
int i, j;
int cull = gr_set_cull(0);
glow_point_bank_override *gpo = NULL;
bool override_all = false;
SCP_unordered_map<int, void*>::iterator gpoi;
ship_info *sip = NULL;
if ( shipp ) {
sip = &Ship_info[shipp->ship_info_index];
gpoi = sip->glowpoint_bank_override_map.find(-1);
if(gpoi != sip->glowpoint_bank_override_map.end()) {
override_all = true;
gpo = (glow_point_bank_override*) sip->glowpoint_bank_override_map[-1];
}
}
for (i = 0; i < pm->n_glow_point_banks; i++ ) {
glow_point_bank *bank = &pm->glow_point_banks[i];
if(!override_all && sip) {
gpoi = sip->glowpoint_bank_override_map.find(i);
if(gpoi != sip->glowpoint_bank_override_map.end()) {
gpo = (glow_point_bank_override*) sip->glowpoint_bank_override_map[i];
} else {
gpo = NULL;
}
}
//Only continue if there actually is a glowpoint bitmap available
if (bank->glow_bitmap == -1)
continue;
if (pm->submodel[bank->submodel_parent].blown_off)
continue;
if ((gpo && gpo->off_time_override && !gpo->off_time)?gpo->is_on:bank->is_on) {
if ( (shipp != NULL) && !(shipp->glow_point_bank_active[i]) )
continue;
for (j = 0; j < bank->num_points; j++) {
Assert( bank->points != NULL );
int flick;
if (pm->submodel[pm->detail[0]].num_arcs) {
flick = static_rand( timestamp() % 20 ) % (pm->submodel[pm->detail[0]].num_arcs + j); //the more damage, the more arcs, the more likely the lights will fail
} else {
flick = 1;
}
if (flick == 1) {
model_render_glowpoint(j, pos, orient, bank, gpo, pm, shipp, use_depth_buffer);
} // flick
} // for slot
} // bank is on
} // for bank
gr_set_cull(cull);
}
void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, int objnum, ship *shipp, matrix *orient, vec3d *pos)
{
int i, j;
int n_q = 0;
size_t k;
vec3d norm, norm2, fvec, pnt, npnt;
thruster_bank *bank = NULL;
vertex p;
bool do_render = false;
if ( Rendering_to_shadow_map ) {
return;
}
if ( pm == NULL ) {
Int3();
return;
}
if ( !(interp->get_model_flags() & MR_SHOW_THRUSTERS) ) {
return;
}
// get an initial count to figure out how man geo batchers we need allocated
for (i = 0; i < pm->n_thrusters; i++ ) {
bank = &pm->thrusters[i];
n_q += bank->num_points;
}
if (n_q <= 0) {
return;
}
const mst_info& thruster_info = interp->get_thruster_info();
// primary_thruster_batcher
if (thruster_info.primary_glow_bitmap >= 0) {
do_render = true;
}
// secondary_thruster_batcher
if (thruster_info.secondary_glow_bitmap >= 0) {
do_render = true;
}
// tertiary_thruster_batcher
if (thruster_info.tertiary_glow_bitmap >= 0) {
do_render = true;
}
if (do_render == false) {
return;
}
// this is used for the secondary thruster glows
// it only needs to be calculated once so I'm doing it here -Bobboau
norm.xyz.z = -1.0f;
norm.xyz.x = 1.0f;
norm.xyz.y = -1.0f;
norm.xyz.x *= thruster_info.rotvel.xyz.y/2;
norm.xyz.y *= thruster_info.rotvel.xyz.x/2;
vm_vec_normalize(&norm);
for (i = 0; i < pm->n_thrusters; i++ ) {
vec3d submodel_static_offset; // The associated submodel's static offset in the ship's frame of reference
bool submodel_rotation = false;
bank = &pm->thrusters[i];
// don't draw this thruster if the engine is destroyed or just not on
if ( !model_should_render_engine_glow(objnum, bank->obj_num) )
continue;
// If bank is attached to a submodel, prepare to account for rotations
//
// TODO: This won't work in the ship lab, because the lab code doesn't
// set the the necessary submodel instance info needed here. The second
// condition is thus a hack to disable the feature while in the lab, and
// can be removed if the lab is re-structured accordingly. -zookeeper
if ( bank->submodel_num > -1 && pm->submodel[bank->submodel_num].can_move && (gameseq_get_state_idx(GS_STATE_LAB) == -1) ) {
model_find_submodel_offset(&submodel_static_offset, Ship_info[shipp->ship_info_index].model_num, bank->submodel_num);
submodel_rotation = true;
}
for (j = 0; j < bank->num_points; j++) {
Assert( bank->points != NULL );
float d, D;
vec3d tempv;
glow_point *gpt = &bank->points[j];
vec3d loc_offset = gpt->pnt;
vec3d loc_norm = gpt->norm;
vec3d world_pnt;
vec3d world_norm;
if ( submodel_rotation ) {
vm_vec_sub(&loc_offset, &gpt->pnt, &submodel_static_offset);
tempv = loc_offset;
find_submodel_instance_point_normal(&loc_offset, &loc_norm, shipp->model_instance_num, bank->submodel_num, &tempv, &loc_norm);
}
vm_vec_unrotate(&world_pnt, &loc_offset, orient);
vm_vec_add2(&world_pnt, pos);
if (shipp) {
// if ship is warping out, check position of the engine glow to the warp plane
if ( (shipp->is_arriving() ) && (shipp->warpin_effect) && Ship_info[shipp->ship_info_index].warpin_type != WT_HYPERSPACE) {
vec3d warp_pnt, tmp;
matrix warp_orient;
shipp->warpin_effect->getWarpPosition(&warp_pnt);
shipp->warpin_effect->getWarpOrientation(&warp_orient);
vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) < 0.0f ) {
break;
}
}
if ( (shipp->flags[Ship::Ship_Flags::Depart_warp] ) && (shipp->warpout_effect) && Ship_info[shipp->ship_info_index].warpout_type != WT_HYPERSPACE) {
vec3d warp_pnt, tmp;
matrix warp_orient;
shipp->warpout_effect->getWarpPosition(&warp_pnt);
shipp->warpout_effect->getWarpOrientation(&warp_orient);
vm_vec_sub( &tmp, &world_pnt, &warp_pnt );
if ( vm_vec_dot( &tmp, &warp_orient.vec.fvec ) > 0.0f ) {
break;
}
}
}
vm_vec_sub(&tempv, &View_position, &world_pnt);
vm_vec_normalize(&tempv);
vm_vec_unrotate(&world_norm, &loc_norm, orient);
D = d = vm_vec_dot(&tempv, &world_norm);
// ADAM: Min throttle draws rad*MIN_SCALE, max uses max.
#define NOISE_SCALE 0.5f
#define MIN_SCALE 3.4f
#define MAX_SCALE 4.7f
float magnitude;
vec3d scale_vec = { { { 1.0f, 0.0f, 0.0f } } };
// normalize banks, in case of incredibly big normals
if ( !IS_VEC_NULL_SQ_SAFE(&world_norm) )
vm_vec_copy_normalize(&scale_vec, &world_norm);
// adjust for thrust
(scale_vec.xyz.x *= thruster_info.length.xyz.x) -= 0.1f;
(scale_vec.xyz.y *= thruster_info.length.xyz.y) -= 0.1f;
(scale_vec.xyz.z *= thruster_info.length.xyz.z) -= 0.1f;
// get magnitude, which we will use as the scaling reference
magnitude = vm_vec_normalize(&scale_vec);
// get absolute value
if (magnitude < 0.0f)
magnitude *= -1.0f;
float scale = magnitude * (MAX_SCALE - MIN_SCALE) + MIN_SCALE;
if (d > 0.0f){
// Make glow bitmap fade in/out quicker from sides.
d *= 3.0f;
if (d > 1.0f)
d = 1.0f;
}
float fog_int = 1.0f;
// fade them in the nebula as well
if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
vm_vec_unrotate(&npnt, &gpt->pnt, orient);
vm_vec_add2(&npnt, pos);
fog_int = (1.0f - (neb2_get_fog_intensity(&npnt)));
if (fog_int > 1.0f)
fog_int = 1.0f;
d *= fog_int;
if (d > 1.0f)
d = 1.0f;
}
float w = gpt->radius * (scale + thruster_info.glow_noise * NOISE_SCALE);
// these lines are used by the tertiary glows, thus we will need to project this all of the time
g3_transfer_vertex( &p, &world_pnt );
// start primary thruster glows
if ( (thruster_info.primary_glow_bitmap >= 0) && (d > 0.0f) ) {
p.r = p.g = p.b = p.a = (ubyte)(255.0f * d);
batching_add_volume_bitmap(thruster_info.primary_glow_bitmap, &p, 0, (w * 0.5f * thruster_info.glow_rad_factor), d, w * 0.325f);
}
// start tertiary thruster glows
if (thruster_info.tertiary_glow_bitmap >= 0) {
p.screen.xyw.w -= w;
p.r = p.g = p.b = p.a = (ubyte)(255.0f * fog_int);
batching_add_volume_bitmap_rotated(thruster_info.tertiary_glow_bitmap, &p, magnitude * 4, w * 0.6f * thruster_info.tertiary_glow_rad_factor, fog_int, -(D > 0) ? D : -D);
}
// begin secondary glows
if (thruster_info.secondary_glow_bitmap >= 0) {
pnt = world_pnt;
scale = magnitude * (MAX_SCALE - (MIN_SCALE / 2)) + (MIN_SCALE / 2);
vm_vec_unrotate(&world_norm, &norm, orient);
d = vm_vec_dot(&tempv, &world_norm);
d += 0.75f;
d *= 3.0f;
if (d > 1.0f)
d = 1.0f;
if (d > 0.0f) {
vm_vec_add(&norm2, &world_norm, &pnt);
vm_vec_sub(&fvec, &norm2, &pnt);
vm_vec_normalize(&fvec);
float wVal = gpt->radius * scale * 2;
vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * thruster_info.glow_length_factor);
if (The_mission.flags[Mission::Mission_Flags::Fullneb]) {
vm_vec_add(&npnt, &pnt, pos);
d *= fog_int;
}
batching_add_beam(thruster_info.secondary_glow_bitmap, &pnt, &norm2, wVal*thruster_info.secondary_glow_rad_factor*0.5f, d);
if (Scene_framebuffer_in_frame && thruster_info.draw_distortion && Cmdline_fb_thrusters) {
vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * thruster_info.distortion_length_factor);
int dist_bitmap;
if (thruster_info.distortion_bitmap > 0) {
dist_bitmap = thruster_info.distortion_bitmap;
}
else {
dist_bitmap = thruster_info.secondary_glow_bitmap;
}
float mag = vm_vec_mag(&gpt->pnt);
mag -= (float)((int)mag);//Valathil - Get a fairly random but constant number to offset the distortion texture
batching_add_distortion_beam(dist_bitmap, &pnt, &norm2, wVal*thruster_info.distortion_rad_factor*0.5f, 1.0f, mag);
}
}
}
// begin particles
if (shipp) {
ship_info *sip = &Ship_info[shipp->ship_info_index];
particle::particle_emitter pe;
thruster_particles *tp;
size_t num_particles = 0;
if (thruster_info.use_ab)
num_particles = sip->afterburner_thruster_particles.size();
else
num_particles = sip->normal_thruster_particles.size();
for (k = 0; k < num_particles; k++) {
if (thruster_info.use_ab)
tp = &sip->afterburner_thruster_particles[k];
else
tp = &sip->normal_thruster_particles[k];
float v = vm_vec_mag_quick(&Objects[shipp->objnum].phys_info.desired_vel);
vm_vec_unrotate(&npnt, &gpt->pnt, orient);
vm_vec_add2(&npnt, pos);
// Where the particles emit from
pe.pos = npnt;
// Initial velocity of all the particles
pe.vel = Objects[shipp->objnum].phys_info.desired_vel;
pe.min_vel = v * 0.75f;
pe.max_vel = v * 1.25f;
// What normal the particle emit around
pe.normal = orient->vec.fvec;
vm_vec_negate(&pe.normal);
// Lowest number of particles to create
pe.num_low = tp->n_low;
// Highest number of particles to create
pe.num_high = tp->n_high;
pe.min_rad = gpt->radius * tp->min_rad;
pe.max_rad = gpt->radius * tp->max_rad;
// How close they stick to that normal 0=on normal, 1=180, 2=360 degree
pe.normal_variance = tp->variance;
pe.min_life = 0.0f;
pe.max_life = 1.0f;
particle::emit( &pe, particle::PARTICLE_BITMAP, tp->thruster_bitmap.first_frame);
}
}
}
}
}
void model_render_insignias(insignia_draw_data *insignia)
{
polymodel *pm = insignia->pm;
int detail_level = insignia->detail_level;
int bitmap_num = insignia->bitmap_num;
// if the model has no insignias, or we don't have a texture, then bail
if ( (pm->num_ins <= 0) || (bitmap_num < 0) )
return;
int idx, s_idx;
vertex vecs[3];
vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] };
vec3d t1, t2, t3;
int i1, i2, i3;
int tmap_flags = TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT | TMAP_HTL_3D_UNLIT;
material insignia_material;
insignia_material.set_depth_bias(1);
// set the proper texture
material_set_unlit(&insignia_material, bitmap_num, 0.65f, true, true);
if ( insignia->clip ) {
insignia_material.set_clip_plane(insignia->clip_normal, insignia->clip_position);
}
// otherwise render them
for(idx=0; idx<pm->num_ins; idx++){
// skip insignias not on our detail level
if(pm->ins[idx].detail_level != detail_level){
continue;
}
for(s_idx=0; s_idx<pm->ins[idx].num_faces; s_idx++){
// get vertex indices
i1 = pm->ins[idx].faces[s_idx][0];
i2 = pm->ins[idx].faces[s_idx][1];
i3 = pm->ins[idx].faces[s_idx][2];
// transform vecs and setup vertices
vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset);
vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset);
vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset);
g3_transfer_vertex(&vecs[0], &t1);
g3_transfer_vertex(&vecs[1], &t2);
g3_transfer_vertex(&vecs[2], &t3);
// setup texture coords
vecs[0].texture_position.u = pm->ins[idx].u[s_idx][0];
vecs[0].texture_position.v = pm->ins[idx].v[s_idx][0];
vecs[1].texture_position.u = pm->ins[idx].u[s_idx][1];
vecs[1].texture_position.v = pm->ins[idx].v[s_idx][1];
vecs[2].texture_position.u = pm->ins[idx].u[s_idx][2];
vecs[2].texture_position.v = pm->ins[idx].v[s_idx][2];
light_apply_rgb( &vecs[0].r, &vecs[0].g, &vecs[0].b, &pm->ins[idx].vecs[i1], &pm->ins[idx].norm[i1], 1.5f );
light_apply_rgb( &vecs[1].r, &vecs[1].g, &vecs[1].b, &pm->ins[idx].vecs[i2], &pm->ins[idx].norm[i2], 1.5f );
light_apply_rgb( &vecs[2].r, &vecs[2].g, &vecs[2].b, &pm->ins[idx].vecs[i3], &pm->ins[idx].norm[i3], 1.5f );
vecs[0].a = vecs[1].a = vecs[2].a = 255;
// draw the polygon
g3_render_primitives_colored_textured(&insignia_material, vecs, 3, PRIM_TYPE_TRIFAN, false);
}
}
}
void model_render_arc(vec3d *v1, vec3d *v2, color *primary, color *secondary, float arc_width)
{
Num_arc_segment_points = 0;
// need need to add the first point
memcpy( &Arc_segment_points[Num_arc_segment_points++], v1, sizeof(vec3d) );
// this should fill in all of the middle, and the last, points
interp_render_arc_segment(v1, v2, 0);
// use primary color for fist pass
Assert( primary );
g3_render_rod(primary, Num_arc_segment_points, Arc_segment_points, arc_width);
if (secondary) {
g3_render_rod(secondary, Num_arc_segment_points, Arc_segment_points, arc_width * 0.33f);
}
}
void model_render_debug_children(polymodel *pm, int mn, int detail_level, uint debug_flags)
{
int i;
if ( (mn < 0) || (mn >= pm->n_models) ) {
Int3();
return;
}
bsp_info *model = &pm->submodel[mn];
if ( model->blown_off ) {
return;
}
// Get submodel rotation data and use submodel orientation matrix
// to put together a matrix describing the final orientation of
// the submodel relative to its parent
angles ang = model->angs;
// Add barrel rotation if needed
if ( model->gun_rotation ) {
if ( pm->gun_submodel_rotation > PI2 ) {
pm->gun_submodel_rotation -= PI2;
} else if ( pm->gun_submodel_rotation < 0.0f ) {
pm->gun_submodel_rotation += PI2;
}
ang.b += pm->gun_submodel_rotation;
}
// Compute final submodel orientation by using the orientation matrix
// and the rotation angles.
// By using this kind of computation, the rotational angles can always
// be computed relative to the submodel itself, instead of relative
// to the parent
matrix rotation_matrix = model->orientation;
vm_rotate_matrix_by_angles(&rotation_matrix, &ang);
matrix inv_orientation;
vm_copy_transpose(&inv_orientation, &model->orientation);
matrix submodel_matrix;
vm_matrix_x_matrix(&submodel_matrix, &rotation_matrix, &inv_orientation);
g3_start_instance_matrix(&model->offset, &submodel_matrix, true);
if ( debug_flags & MR_DEBUG_PIVOTS ) {
model_draw_debug_points( pm, &pm->submodel[mn], debug_flags );
}
i = model->first_child;
while ( i >= 0 ) {
model_render_debug_children( pm, i, detail_level, debug_flags );
i = pm->submodel[i].next_sibling;
}
g3_done_instance(true);
}
void model_render_debug(int model_num, matrix *orient, vec3d * pos, uint flags, uint debug_flags, int objnum, int detail_level_locked )
{
polymodel *pm = model_get(model_num);
g3_start_instance_matrix(pos, orient, true);
if ( debug_flags & MR_DEBUG_RADIUS ) {
if ( !( flags & MR_SHOW_OUTLINE_PRESET ) ) {
gr_set_color(0,64,0);
g3_draw_sphere_ez(&vmd_zero_vector,pm->rad);
}
}
float depth = model_render_determine_depth(objnum, model_num, orient, pos, detail_level_locked);
int detail_level = model_render_determine_detail(depth, objnum, model_num, orient, pos, flags, detail_level_locked);
vec3d auto_back = ZERO_VECTOR;
bool set_autocen = model_render_determine_autocenter(&auto_back, pm, detail_level, flags);
if ( set_autocen ) {
g3_start_instance_matrix(&auto_back, NULL, true);
}
uint save_gr_zbuffering_mode = gr_zbuffer_set(GR_ZBUFF_READ);
int i = pm->submodel[pm->detail[detail_level]].first_child;
while ( i >= 0 ) {
model_render_debug_children( pm, i, detail_level, flags );
i = pm->submodel[i].next_sibling;
}
if ( debug_flags & MR_DEBUG_PIVOTS ) {
model_draw_debug_points( pm, NULL, flags );
model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]], flags );
if ( pm->flags & PM_FLAG_AUTOCEN ) {
gr_set_color(255, 255, 255);
g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
}
}
if ( debug_flags & MR_DEBUG_SHIELDS ) {
model_render_shields(pm, flags);
}
if ( debug_flags & MR_DEBUG_PATHS ) {
model_draw_paths_htl(model_num, flags);
}
if ( debug_flags & MR_DEBUG_BAY_PATHS ) {
model_draw_bay_paths_htl(model_num);
}
if ( (flags & MR_AUTOCENTER) && (set_autocen) ) {
g3_done_instance(true);
}
g3_done_instance(true);
gr_zbuffer_set(save_gr_zbuffering_mode);
}
void model_render_immediate(model_render_params *render_info, int model_num, matrix *orient, vec3d * pos, int render, bool sort)
{
draw_list model_list;
model_list.init();
model_render_queue(render_info, &model_list, model_num, orient, pos);
model_list.init_render(sort);
switch ( render ) {
case MODEL_RENDER_OPAQUE:
model_list.render_all(ZBUFFER_TYPE_FULL);
break;
case MODEL_RENDER_TRANS:
model_list.render_all(ZBUFFER_TYPE_READ);
model_list.render_all(ZBUFFER_TYPE_NONE);
break;
case MODEL_RENDER_ALL:
model_list.render_all();
break;
}
model_list.render_outlines();
model_list.render_insignias();
model_list.render_arcs();
gr_zbias(0);
gr_set_cull(0);
gr_zbuffer_set(ZBUFFER_TYPE_READ);
gr_set_fill_mode(GR_FILL_MODE_SOLID);
gr_clear_states();
gr_set_buffer(-1);
gr_reset_lighting();
gr_set_lighting(false, false);
GL_state.Texture.DisableAll();
if ( render_info->get_debug_flags() ) {
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());
}
}
void model_render_queue(model_render_params *interp, draw_list *scene, int model_num, matrix *orient, vec3d *pos)
{
int i;
const int objnum = interp->get_object_number();
const int model_flags = interp->get_model_flags();
model_material rendering_material;
polymodel *pm = model_get(model_num);
polymodel_instance *pmi = NULL;
float light_factor = model_render_determine_light_factor(interp, pos, model_flags);
if ( light_factor < (1.0f/32.0f) ) {
// If it is too far, exit
return;
}
rendering_material.set_light_factor(light_factor);
if ( interp->is_clip_plane_set() ) {
rendering_material.set_clip_plane(interp->get_clip_plane_normal(), interp->get_clip_plane_pos());
}
if ( interp->is_team_color_set() ) {
rendering_material.set_team_color(interp->get_team_color());
}
if ( model_flags & MR_FORCE_CLAMP ) {
rendering_material.set_texture_addressing(TMAP_ADDRESS_CLAMP);
} else {
rendering_material.set_texture_addressing(TMAP_ADDRESS_WRAP);
}
model_render_set_glow_points(pm, objnum);
if ( !(model_flags & MR_NO_LIGHTING) ) {
scene->set_light_filter(objnum, pos, pm->rad);
}
ship *shipp = NULL;
object *objp = NULL;
if (objnum >= 0) {
objp = &Objects[objnum];
if (objp->type == OBJ_SHIP) {
shipp = &Ships[objp->instance];
pmi = model_get_instance(shipp->model_instance_num);
}
else if (pm->flags & PM_FLAG_HAS_INTRINSIC_ROTATE) {
if (objp->type == OBJ_ASTEROID)
pmi = model_get_instance(Asteroids[objp->instance].model_instance_num);
else if (objp->type == OBJ_WEAPON)
pmi = model_get_instance(Weapons[objp->instance].model_instance_num);
else
Warning(LOCATION, "Unsupported object type %d for rendering intrinsic-rotate submodels!", objp->type);
}
}
// is this a skybox with a rotating submodel?
extern int Nmodel_num, Nmodel_instance_num;
if (model_num == Nmodel_num && Nmodel_instance_num >= 0)
pmi = model_get_instance(Nmodel_instance_num);
// Set the flags we will pass to the tmapper
uint tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
if ( !(model_flags & MR_NO_TEXTURING) ) {
tmap_flags |= TMAP_FLAG_TEXTURED;
if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
tmap_flags |= TMAP_FLAG_TILED;
if ( !(model_flags & MR_NO_CORRECT) ) {
tmap_flags |= TMAP_FLAG_CORRECT;
}
}
if ( model_flags & MR_DESATURATED ) {
rendering_material.set_desaturation(true);
}
if ( interp->get_animated_effect_num() >= 0 ) {
rendering_material.set_animated_effect(interp->get_animated_effect_num(), interp->get_animated_effect_timer());
}
bool is_outlines_only = (model_flags & MR_NO_POLYS) && ((model_flags & MR_SHOW_OUTLINE_PRESET) || (model_flags & MR_SHOW_OUTLINE));
bool is_outlines_only_htl = (model_flags & MR_NO_POLYS) && (model_flags & MR_SHOW_OUTLINE_HTL);
scene->push_transform(pos, orient);
float depth = model_render_determine_depth(objnum, model_num, orient, pos, interp->get_detail_level_lock());
int detail_level = model_render_determine_detail(depth, objnum, model_num, orient, pos, model_flags, interp->get_detail_level_lock());
// If we're rendering attached weapon models, check against the ships' tabled Weapon Model Draw Distance (which defaults to 200)
if ( model_flags & MR_ATTACHED_MODEL && shipp != NULL ) {
if (depth > Ship_info[shipp->ship_info_index].weapon_model_draw_distance) {
scene->pop_transform();
return;
}
}
// #ifndef NDEBUG
// if ( Interp_detail_level == 0 ) {
// MONITOR_INC( NumHiModelsRend, 1 );
// } else if ( Interp_detail_level == pm->n_detail_levels-1 ) {
// MONITOR_INC( NumLowModelsRend, 1 );
// } else {
// MONITOR_INC( NumMedModelsRend, 1 );
// }
// #endif
vec3d auto_back = ZERO_VECTOR;
bool set_autocen = model_render_determine_autocenter(&auto_back, pm, detail_level, model_flags);
if ( set_autocen ) {
scene->push_transform(&auto_back, NULL);
}
// if we're in nebula mode, fog everything except for the warp holes and other non-fogged models
if ( (The_mission.flags[Mission::Mission_Flags::Fullneb]) && (Neb2_render_mode != NEB2_RENDER_NONE) && !(model_flags & MR_NO_FOGGING) ) {
float fog_near = 10.0f, fog_far = 1000.0f;
neb2_get_adjusted_fog_values(&fog_near, &fog_far, objp);
unsigned char r, g, b;
neb2_get_fog_color(&r, &g, &b);
rendering_material.set_fog(r, g, b, fog_near, fog_far);
} else {
rendering_material.set_fog();
}
if ( is_outlines_only_htl ) {
rendering_material.set_fill_mode(GR_FILL_MODE_WIRE);
tmap_flags &= ~TMAP_FLAG_RGB;
} else {
rendering_material.set_fill_mode(GR_FILL_MODE_SOLID);
}
color clr = interp->get_color();
rendering_material.set_color(clr);
if ( model_flags & MR_EDGE_ALPHA ) {
rendering_material.set_center_alpha(-1);
} else if ( model_flags & MR_CENTER_ALPHA ) {
rendering_material.set_center_alpha(1);
} else {
rendering_material.set_center_alpha(0);
}
if ( interp->is_normal_alpha_set() ) {
rendering_material.set_normal_alpha(interp->get_normal_alpha_min(), interp->get_normal_alpha_max());
}
if ( interp->is_normal_extrude_set() ) {
rendering_material.set_normal_extrude(interp->get_normal_extrude_width());
}
if ( ( model_flags & MR_NO_CULL ) || ( model_flags & MR_ALL_XPARENT ) || ( interp->get_warp_bitmap() >= 0 ) ) {
rendering_material.set_cull_mode(false);
} else {
rendering_material.set_cull_mode(true);
}
if ( !(model_flags & MR_NO_LIGHTING) ) {
rendering_material.set_lighting(true);
} else {
rendering_material.set_lighting(false);
}
if ( Rendering_to_shadow_map ) {
rendering_material.set_depth_bias(-1024);
} else {
rendering_material.set_depth_bias(0);
}
if ( !Cmdline_no_batching && !(model_flags & MR_NO_BATCH) && pm->flags & PM_FLAG_BATCHED
&& !(is_outlines_only || is_outlines_only_htl) ) {
// always set batched rendering on if supported
tmap_flags |= TMAP_FLAG_BATCH_TRANSFORMS;
}
if ( (tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS) ) {
scene->start_model_batch(pm->n_models);
model_render_buffers(scene, &rendering_material, interp, &pm->detail_buffers[detail_level], pm, -1, detail_level, tmap_flags);
}
// Draw the subobjects
bool draw_thrusters = false;
bool trans_buffer = false;
i = pm->submodel[pm->detail[detail_level]].first_child;
while( i >= 0 ) {
if ( !pm->submodel[i].is_thruster ) {
model_render_children_buffers( scene, &rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
} else {
draw_thrusters = true;
}
i = pm->submodel[i].next_sibling;
}
model_radius = pm->submodel[pm->detail[detail_level]].rad;
//*************************** draw the hull of the ship *********************************************
vec3d view_pos = scene->get_view_position();
if ( model_render_check_detail_box(&view_pos, pm, pm->detail[detail_level], model_flags) ) {
int detail_model_num = pm->detail[detail_level];
if ( (is_outlines_only || is_outlines_only_htl) && pm->submodel[detail_model_num].outline_buffer != NULL ) {
color outline_color = interp->get_color();
scene->add_outline(pm->submodel[detail_model_num].outline_buffer, pm->submodel[detail_model_num].n_verts_outline, &outline_color);
} else {
model_render_buffers(scene, &rendering_material, interp, &pm->submodel[detail_model_num].buffer, pm, detail_model_num, detail_level, tmap_flags);
if ( pm->submodel[detail_model_num].num_arcs ) {
model_render_add_lightning( scene, interp, pm, &pm->submodel[detail_model_num] );
}
}
}
// make sure batch rendering is unconditionally off.
tmap_flags &= ~TMAP_FLAG_BATCH_TRANSFORMS;
if ( pm->flags & PM_FLAG_TRANS_BUFFER && !(is_outlines_only || is_outlines_only_htl) ) {
trans_buffer = true;
i = pm->submodel[pm->detail[detail_level]].first_child;
while( i >= 0 ) {
if ( !pm->submodel[i].is_thruster ) {
model_render_children_buffers( scene, &rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
}
i = pm->submodel[i].next_sibling;
}
view_pos = scene->get_view_position();
if ( model_render_check_detail_box(&view_pos, pm, pm->detail[detail_level], model_flags) ) {
int detail_model_num = pm->detail[detail_level];
model_render_buffers(scene, &rendering_material, interp, &pm->submodel[detail_model_num].trans_buffer, pm, detail_model_num, detail_level, tmap_flags);
}
}
// Draw the thruster subobjects
if ( draw_thrusters && !(is_outlines_only || is_outlines_only_htl) ) {
i = pm->submodel[pm->detail[detail_level]].first_child;
trans_buffer = false;
while( i >= 0 ) {
if (pm->submodel[i].is_thruster) {
model_render_children_buffers( scene, &rendering_material, interp, pm, pmi, i, detail_level, tmap_flags, trans_buffer );
}
i = pm->submodel[i].next_sibling;
}
}
if ( !( model_flags & MR_NO_TEXTURING ) ) {
scene->add_insignia(interp, pm, detail_level, interp->get_insignia_bitmap());
}
if ( (model_flags & MR_AUTOCENTER) && (set_autocen) ) {
scene->pop_transform();
}
scene->pop_transform();
// start rendering glow points -Bobboau
if ( (pm->n_glow_point_banks) && !is_outlines_only && !is_outlines_only_htl && !Glowpoint_override ) {
model_render_glow_points(pm, shipp, orient, pos, Glowpoint_use_depth_buffer);
}
// Draw the thruster glow
if ( !is_outlines_only && !is_outlines_only_htl ) {
if ( ( model_flags & MR_AUTOCENTER ) && set_autocen ) {
vec3d autoback_rotated;
vm_vec_unrotate(&autoback_rotated, &auto_back, orient);
vm_vec_add2(&autoback_rotated, pos);
model_queue_render_thrusters( interp, pm, objnum, shipp, orient, &autoback_rotated );
} else {
model_queue_render_thrusters( interp, pm, objnum, shipp, orient, pos );
}
}
}