Files
fs2open.github.com/code/model/modelinterp.cpp
T
niffiwan 24e7810b24 Coverity fixes from Flaming_Sword
Uninitialized scalar field 1093859 1093917 1093810
Out-of-bounds read/access 1093669 1093670
Logically dead code 1093413 1093396 1093407 1093408 1093409 1093410 1093423 1093427 1093390
Allocation size mismatch 1093729 1093728
Unused pointer value 1093921 1093922
Wrong sizeof argument 1093732 1093731
Operands don't affect result 1093365
Dereference after null check/return value 1093475 1093455 1093627

git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@9979 387891d4-d844-0410-90c0-e4c51a9137d3
2013-10-27 13:51:50 +10:00

4987 lines
129 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#define MODEL_LIB
#include "model/model.h"
#include "model/modelsinc.h"
#include "graphics/2d.h"
#include "render/3dinternal.h"
#include "math/fvi.h"
#include "lighting/lighting.h"
#include "bmpman/bmpman.h"
#include "io/key.h"
#include "io/timer.h"
#include "mission/missionparse.h"
#include "nebula/neb.h"
#include "math/staticrand.h"
#include "particle/particle.h"
#include "ship/ship.h"
#include "cmdline/cmdline.h"
#include "gamesnd/gamesnd.h"
#include "globalincs/linklist.h"
#include "weapon/shockwave.h"
#include "parse/parselo.h"
#include "graphics/gropengllight.h"
#include "ship/shipfx.h"
#include "gamesequence/gamesequence.h"
#include <limits.h>
float model_radius = 0;
// Some debug variables used externally for displaying stats
#ifndef NDEBUG
int modelstats_num_polys = 0;
int modelstats_num_polys_drawn = 0;
int modelstats_num_verts = 0;
int modelstats_num_sortnorms = 0;
int modelstats_num_boxes = 0;
#endif
extern fix game_get_overall_frametime(); // for texture animation
typedef struct model_light {
ubyte r, g, b;
ubyte spec_r, spec_g, spec_b;
} model_light;
// a lighting object
typedef struct model_light_object {
model_light *lights;
int objnum;
int skip;
int skip_max;
} model_light_object;
// -----------------------
// Local variables
//
static int Num_interp_verts_allocated = 0;
vec3d **Interp_verts = NULL;
static vertex *Interp_points = NULL;
static vertex *Interp_splode_points = NULL;
vec3d *Interp_splode_verts = NULL;
static int Interp_num_verts = 0;
static vertex **Interp_list = NULL;
static int Num_interp_list_verts_allocated = 0;
static float Interp_box_scale = 1.0f; // this is used to scale both detail boxes and spheres
static vec3d Interp_render_box_min = ZERO_VECTOR;
static vec3d Interp_render_box_max = ZERO_VECTOR;
static float Interp_render_sphere_radius = 0.0f;
static vec3d Interp_render_sphere_offset = ZERO_VECTOR;
// -------------------------------------------------------------------
// lighting save stuff
//
model_light_object Interp_lighting_temp;
model_light_object *Interp_lighting = &Interp_lighting_temp;
int Interp_use_saved_lighting = 0;
int Interp_saved_lighting_full = 0;
//
// lighting save stuff
// -------------------------------------------------------------------
static int Num_interp_norms_allocated = 0;
static vec3d **Interp_norms = NULL;
static ubyte *Interp_light_applied = NULL;
static int Interp_num_norms = 0;
static ubyte *Interp_lights;
static float Interp_fog_level = 0.0f;
// Stuff to control rendering parameters
static color Interp_outline_color;
static int Interp_detail_level_locked = 0;
static uint Interp_flags = 0;
static uint Interp_tmap_flags = 0;
bool Interp_desaturate = false;
// If non-zero, then the subobject gets scaled by Interp_thrust_scale.
int Interp_thrust_scale_subobj = 0;
float Interp_thrust_scale = 0.1f;
static float Interp_thrust_scale_x = 0.0f;//added -bobboau
static float Interp_thrust_scale_y = 0.0f;//added -bobboau
static int Interp_thrust_bitmap = -1;
static int Interp_thrust_glow_bitmap = -1;
static float Interp_thrust_glow_noise = 1.0f;
static bool Interp_afterburner = false;
// Bobboau's thruster stuff
static int Interp_secondary_thrust_glow_bitmap = -1;
static int Interp_tertiary_thrust_glow_bitmap = -1;
static int Interp_distortion_thrust_bitmap = -1;
static float Interp_thrust_glow_rad_factor = 1.0f;
static float Interp_secondary_thrust_glow_rad_factor = 1.0f;
static float Interp_tertiary_thrust_glow_rad_factor = 1.0f;
static float Interp_distortion_thrust_rad_factor = 1.0f;
static float Interp_distortion_thrust_length_factor = 1.0f;
static float Interp_thrust_glow_len_factor = 1.0f;
static vec3d Interp_thrust_rotvel = ZERO_VECTOR;
static bool Interp_draw_distortion = true;
// Bobboau's warp stuff
static float Interp_warp_scale_x = 1.0f;
static float Interp_warp_scale_y = 1.0f;
static float Interp_warp_scale_z = 1.0f;
static int Interp_warp_bitmap = -1;
static float Interp_warp_alpha = -1.0f;
static int Interp_objnum = -1;
// if != -1, use this bitmap when rendering ship insignias
static int Interp_insignia_bitmap = -1;
// replacement - Goober5000
// updated - WMC
static int *Interp_new_replacement_textures = NULL;
static fix Interp_base_frametime = 0;
// if != -1, use this bitmap when rendering with a forced texture
static int Interp_forced_bitmap = -1;
// for rendering with the MR_ALL_XPARENT FLAG SET
static float Interp_xparent_alpha = 1.0f;
float Interp_light = 0.0f;
int Interp_multitex_cloakmap = -1;
int Interp_cloakmap_alpha = 255;
// our current level of detail (LOD)
int Interp_detail_level = 0;
static int FULLCLOAK = -1;
// current transformation texture
int Interp_no_flush = 0;
// forward references
int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check);
void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, int objnum = -1);
void model_interp_sortnorm_b2f(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check);
void model_interp_sortnorm_f2b(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check);
void (*model_interp_sortnorm)(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check) = model_interp_sortnorm_b2f;
int model_should_render_engine_glow(int objnum, int bank_obj);
void model_render_buffers(polymodel *pm, int mn, bool is_child = false);
void model_render_children_buffers(polymodel * pm, int mn, int detail_level);
int model_interp_get_texture(texture_info *tinfo, fix base_frametime);
//Valathil - Transparent object buffer
SCP_vector<transparent_submodel> transparent_submodels;
void model_deallocate_interp_data()
{
if (Interp_verts != NULL)
vm_free(Interp_verts);
if (Interp_points != NULL)
vm_free(Interp_points);
if (Interp_splode_points != NULL)
vm_free(Interp_splode_points);
if (Interp_splode_verts != NULL)
vm_free(Interp_splode_verts);
if (Interp_norms != NULL)
vm_free(Interp_norms);
if (Interp_light_applied != NULL)
vm_free(Interp_light_applied);
if (Interp_lighting_temp.lights != NULL)
vm_free(Interp_lighting_temp.lights);
Num_interp_verts_allocated = 0;
Num_interp_norms_allocated = 0;
}
extern void model_collide_allocate_point_list(int n_points);
extern void model_collide_free_point_list();
void model_allocate_interp_data(int n_verts = 0, int n_norms = 0, int n_list_verts = 0)
{
static ubyte dealloc = 0;
if (!dealloc) {
atexit(model_deallocate_interp_data);
atexit(model_collide_free_point_list);
dealloc = 1;
}
Assert( (n_verts >= 0) && (n_norms >= 0) && (n_list_verts >= 0) );
Assert( (n_verts || Num_interp_verts_allocated) && (n_norms || Num_interp_norms_allocated) /*&& (n_list_verts || Num_interp_list_verts_allocated)*/ );
if (n_verts > Num_interp_verts_allocated) {
if (Interp_verts != NULL) {
vm_free(Interp_verts);
Interp_verts = NULL;
}
// Interp_verts can't be reliably realloc'd so free and malloc it on each resize (no data needs to be carried over)
Interp_verts = (vec3d**) vm_malloc( n_verts * sizeof(vec3d *) );
Interp_points = (vertex*) vm_realloc( Interp_points, n_verts * sizeof(vertex) );
Interp_splode_points = (vertex*) vm_realloc( Interp_splode_points, n_verts * sizeof(vertex) );
Interp_splode_verts = (vec3d*) vm_realloc( Interp_splode_verts, n_verts * sizeof(vec3d) );
Num_interp_verts_allocated = n_verts;
// model collide needs a similar size to resize it based on this new value
model_collide_allocate_point_list( n_verts );
}
if (n_norms > Num_interp_norms_allocated) {
if (Interp_norms != NULL) {
vm_free(Interp_norms);
Interp_norms = NULL;
}
// Interp_norms can't be reliably realloc'd so free and malloc it on each resize (no data needs to be carried over)
Interp_norms = (vec3d**) vm_malloc( n_norms * sizeof(vec3d) );
// these next two lighting things aren't values that need to be carried over, but we need to make sure they are 0 by default
if (Interp_light_applied != NULL) {
vm_free(Interp_light_applied);
Interp_light_applied = NULL;
}
if (Interp_lighting_temp.lights != NULL) {
vm_free(Interp_lighting_temp.lights);
Interp_lighting_temp.lights = NULL;
}
Interp_light_applied = (ubyte*) vm_malloc( n_norms * sizeof(ubyte) );
Interp_lighting_temp.lights = (model_light*) vm_malloc( n_norms * sizeof(model_light) );
memset( Interp_light_applied, 0, n_norms * sizeof(ubyte) );
memset( Interp_lighting_temp.lights, 0, n_norms * sizeof(model_light) );
Num_interp_norms_allocated = n_norms;
}
// we should only get here if we are not in HTL mode
if ( n_list_verts > Num_interp_list_verts_allocated ) {
if (Interp_list != NULL) {
vm_free(Interp_list);
Interp_list = NULL;
}
Interp_list = (vertex**) vm_malloc( n_list_verts * sizeof(vertex) );
Verify( Interp_list != NULL );
Num_interp_list_verts_allocated = n_list_verts;
}
Interp_num_verts = n_verts;
Interp_num_norms = n_norms;
// check that everything is still usable (works in release and debug builds)
Verify( Interp_points != NULL );
Verify( Interp_splode_points != NULL );
Verify( Interp_verts != NULL );
Verify( Interp_splode_verts != NULL );
Verify( Interp_norms != NULL );
Verify( Interp_light_applied != NULL );
}
void model_setup_cloak(vec3d *shift, int full_cloak, int alpha)
{
FULLCLOAK=full_cloak;
int unit;
if (full_cloak)
{
unit=0;
Interp_multitex_cloakmap=0;
model_set_insignia_bitmap(-1);
model_set_forced_texture(CLOAKMAP);
gr_set_cull(1);
model_interp_sortnorm=model_interp_sortnorm_f2b;
}
else
{
unit=2;
Interp_multitex_cloakmap=1;
}
Interp_cloakmap_alpha=alpha;
gr_push_texture_matrix(unit);
gr_translate_texture_matrix(unit,shift);
}
void model_finish_cloak(int full_cloak)
{
int unit;
if (full_cloak){ unit=0; gr_set_cull(0);}
else unit=2;
gr_pop_texture_matrix(unit);
Interp_multitex_cloakmap=-1;
model_set_forced_texture(-1);
FULLCLOAK=-1;
}
void interp_clear_instance()
{
Interp_thrust_scale = 0.1f;
Interp_thrust_scale_x = 0.0f;//added-Bobboau
Interp_thrust_scale_y = 0.0f;//added-Bobboau
Interp_thrust_bitmap = -1;
Interp_thrust_glow_bitmap = -1;
Interp_thrust_glow_noise = 1.0f;
Interp_insignia_bitmap = -1;
Interp_afterburner = false;
// Bobboau's thruster stuff
{
Interp_thrust_glow_rad_factor = 1.0f;
Interp_secondary_thrust_glow_bitmap = -1;
Interp_secondary_thrust_glow_rad_factor = 1.0f;
Interp_tertiary_thrust_glow_bitmap = -1;
Interp_tertiary_thrust_glow_rad_factor = 1.0f;
Interp_thrust_glow_len_factor = 1.0f;
vm_vec_zero(&Interp_thrust_rotvel);
}
Interp_box_scale = 1.0f;
Interp_render_box_min = vmd_zero_vector;
Interp_render_box_max = vmd_zero_vector;
Interp_render_sphere_radius = 0.0f;
Interp_render_sphere_offset = vmd_zero_vector;
}
/**
* Scales the engines thrusters by this much
*/
void model_set_thrust(int model_num, mst_info *mst)
{
if (mst == NULL) {
Int3();
return;
}
Interp_thrust_scale = mst->length.xyz.z;
Interp_thrust_scale_x = mst->length.xyz.x;
Interp_thrust_scale_y = mst->length.xyz.y;
CLAMP(Interp_thrust_scale, 0.1f, 1.0f);
Interp_thrust_bitmap = mst->primary_bitmap;
Interp_thrust_glow_bitmap = mst->primary_glow_bitmap;
Interp_secondary_thrust_glow_bitmap = mst->secondary_glow_bitmap;
Interp_tertiary_thrust_glow_bitmap = mst->tertiary_glow_bitmap;
Interp_distortion_thrust_bitmap = mst->distortion_bitmap;
Interp_thrust_glow_noise = mst->glow_noise;
Interp_afterburner = mst->use_ab;
if (mst->rotvel != NULL) {
Interp_thrust_rotvel = *(mst->rotvel);
} else {
vm_vec_zero(&Interp_thrust_rotvel);
}
Interp_thrust_glow_rad_factor = mst->glow_rad_factor;
Interp_secondary_thrust_glow_rad_factor = mst->secondary_glow_rad_factor;
Interp_tertiary_thrust_glow_rad_factor = mst->tertiary_glow_rad_factor;
Interp_thrust_glow_len_factor = mst->glow_length_factor;
Interp_distortion_thrust_rad_factor = mst->distortion_rad_factor;
Interp_distortion_thrust_length_factor = mst->distortion_length_factor;
Interp_draw_distortion = mst->draw_distortion;
}
bool splodeing = false;
int splodeingtexture = -1;
float splode_level = 0.0f;
float GEOMETRY_NOISE = 0.0f;
// Point list
// +0 int id
// +4 int size
// +8 int n_verts
// +12 int n_norms
// +16 int offset from start of chunk to vertex data
// +20 n_verts*char norm_counts
// +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
void model_interp_splode_defpoints(ubyte * p, polymodel *pm, bsp_info *sm, float dist)
{
if(dist==0.0f)return;
if(dist<0.0f)dist*=-1.0f;
int n;
int nverts = w(p+8);
int offset = w(p+16);
int nnorms = 0;
ubyte * normcount = p+20;
vertex *dest = Interp_splode_points;
vec3d *src = vp(p+offset);
for (n = 0; n < nverts; n++) {
nnorms += normcount[n];
}
model_allocate_interp_data(nverts, nnorms);
vec3d dir;
for (n=0; n<nverts; n++ ) {
Interp_splode_verts[n] = *src;
src++;
vm_vec_avg_n(&dir, normcount[n], src);
vm_vec_normalize(&dir);
for(int i=0; i<normcount[n]; i++)src++;
vm_vec_scale_add2(&Interp_splode_verts[n], &dir, dist);
g3_rotate_vertex(dest, &Interp_splode_verts[n]);
dest++;
}
}
// Point list
// +0 int id
// +4 int size
// +8 int n_verts
// +12 int n_norms
// +16 int offset from start of chunk to vertex data
// +20 n_verts*char norm_counts
// +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals.
void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm)
{
if(splodeing)model_interp_splode_defpoints(p, pm, sm, splode_level*model_radius);
int i, n;
int nverts = w(p+8);
int offset = w(p+16);
int next_norm = 0;
int nnorms = 0;
ubyte * normcount = p+20;
vertex *dest = NULL;
vec3d *src = vp(p+offset);
// Get pointer to lights
Interp_lights = p+20+nverts;
for (i = 0; i < nverts; i++) {
nnorms += normcount[i];
}
// allocate new Interp data if size is greater than we already have ready to use
model_allocate_interp_data(nverts, nnorms);
dest = Interp_points;
Assert( dest != NULL );
#ifndef NDEBUG
modelstats_num_verts += nverts;
#endif
if (Interp_thrust_scale_subobj) {
// Only scale vertices that aren't on the "base" of
// the effect. Base is something Adam decided to be
// anything under 1.5 meters, hence the 1.5f.
float min_thruster_dist = -1.5f;
if ( Interp_flags & MR_IS_MISSILE ) {
min_thruster_dist = 0.5f;
}
for (n=0; n<nverts; n++ ) {
vec3d tmp;
Interp_verts[n] = src;
// Only scale vertices that aren't on the "base" of
// the effect. Base is something Adam decided to be
// anything under 1.5 meters, hence the 1.5f.
if ( src->xyz.z < min_thruster_dist ) {
tmp.xyz.x = src->xyz.x * 1.0f;
tmp.xyz.y = src->xyz.y * 1.0f;
tmp.xyz.z = src->xyz.z * Interp_thrust_scale;
} else {
tmp = *src;
}
g3_rotate_vertex(dest,&tmp);
src++; // move to normal
for (i=0; i<normcount[n]; i++ ) {
Interp_light_applied[next_norm] = 0;
Interp_norms[next_norm] = src;
next_norm++;
src++;
}
dest++;
}
} else if ( (Interp_warp_scale_x != 1.0f) || (Interp_warp_scale_y != 1.0f) || (Interp_warp_scale_z != 1.0f)) {
for (n=0; n<nverts; n++ ) {
vec3d tmp;
Interp_verts[n] = src;
tmp.xyz.x = (src->xyz.x) * Interp_warp_scale_x;
tmp.xyz.y = (src->xyz.y) * Interp_warp_scale_y;
tmp.xyz.z = (src->xyz.z) * Interp_warp_scale_z;
g3_rotate_vertex(dest,&tmp);
src++; // move to normal
for (i=0; i<normcount[n]; i++ ) {
Interp_light_applied[next_norm] = 0;
Interp_norms[next_norm] = src;
next_norm++;
src++;
}
dest++;
}
} else {
vec3d point;
for (n=0; n<nverts; n++ ) {
if(!Cmdline_nohtl) {
if(GEOMETRY_NOISE!=0.0f){
GEOMETRY_NOISE = model_radius / 50;
Interp_verts[n] = src;
point.xyz.x = src->xyz.x + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
point.xyz.y = src->xyz.y + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
point.xyz.z = src->xyz.z + frand_range(GEOMETRY_NOISE,-GEOMETRY_NOISE);
g3_rotate_vertex(dest, &point);
}else{
Interp_verts[n] = src;
g3_rotate_vertex(dest, src);
}
}
else {
Interp_verts[n] = src;
g3_rotate_vertex(dest, src);
}
src++; // move to normal
for (i=0; i<normcount[n]; i++ ) {
Interp_light_applied[next_norm] = 0;
Interp_norms[next_norm] = src;
next_norm++;
src++;
}
dest++;
}
}
Interp_num_norms = next_norm;
}
matrix *Interp_orient;
vec3d *Interp_pos;
// Flat Poly
// +0 int id
// +4 int size
// +8 vec3d normal
// +20 vec3d center
// +32 float radius
// +36 int nverts
// +40 byte red
// +41 byte green
// +42 byte blue
// +43 byte pad
// +44 nverts*short*short vertlist, smoothlist
void model_interp_flatpoly(ubyte * p,polymodel * pm)
{
int nv = w(p+36);
if ( nv < 0 )
return;
#ifndef NDEBUG
modelstats_num_polys++;
#endif
if ( !g3_check_normal_facing(vp(p+20), vp(p+8)) )
return;
int i;
short * verts = (short *)(p+44);
int max_n_verts = 0;
int max_n_norms = 0;
// slow? yes. safe? yes.
for (i = 0; i < nv; i++) {
max_n_verts = MAX(verts[i*2+0] + 1, max_n_verts);
max_n_norms = MAX(verts[i*2+1] + 1, max_n_norms);
}
model_allocate_interp_data(max_n_verts, max_n_norms, nv);
for (i = 0; i < nv; i++) {
Interp_list[i] = &Interp_points[verts[i*2]];
if ( Interp_flags & MR_NO_LIGHTING ) {
Interp_list[i]->r = 191;
Interp_list[i]->g = 191;
Interp_list[i]->b = 191;
} else {
int vertnum = verts[i*2+0];
int norm = verts[i*2+1];
if ( Interp_flags & MR_NO_SMOOTHING ) {
light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
} else {
// if we're not using saved lighting
if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
light_apply_rgb( &Interp_lighting->lights[norm].r, &Interp_lighting->lights[norm].g, &Interp_lighting->lights[norm].b, Interp_verts[vertnum], vp(p+8), Interp_light );
Interp_light_applied[norm] = 1;
}
Interp_list[i]->r = Interp_lighting->lights[norm].r;
Interp_list[i]->g = Interp_lighting->lights[norm].g;
Interp_list[i]->b = Interp_lighting->lights[norm].b;
}
}
}
// HACK!!! FIX ME!!! I'M SLOW!!!!
if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
gr_set_color( *(p+40), *(p+41), *(p+42) );
}
if ( !(Interp_flags & MR_NO_POLYS)) {
g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB );
}
if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET)) {
int j;
if ( Interp_flags & MR_SHOW_OUTLINE ) {
gr_set_color_fast( &Interp_outline_color );
}
for (i = 0; i < nv; i++) {
j = (i + 1) % nv;
g3_draw_line(Interp_list[i], Interp_list[j]);
}
}
}
void model_interp_edge_alpha( ubyte *param_r, ubyte *param_g, ubyte *param_b, vec3d *pnt, vec3d *norm, float alpha, bool invert = false)
{
vec3d r;
vm_vec_sub(&r, &View_position, pnt);
vm_vec_normalize(&r);
float d = vm_vec_dot(&r, norm);
if (d < 0.0f)
d = -d;
if (invert)
*param_r = *param_g = *param_b = ubyte( fl2i((1.0f - d) * 254.0f * alpha));
else
*param_r = *param_g = *param_b = ubyte( fl2i(d * 254.0f * alpha) );
}
// Textured Poly
// +0 int id
// +4 int size
// +8 vec3d normal
// +20 vec3d center
// +32 float radius
// +36 int nverts
// +40 int tmap_num
// +44 nverts*(model_tmap_vert) vertlist (n,u,v)
extern int Tmap_show_layers;
int Interp_subspace = 0;
float Interp_subspace_offset_u = 0.0f;
float Interp_subspace_offset_v = 0.0f;
ubyte Interp_subspace_r = 255;
ubyte Interp_subspace_g = 255;
ubyte Interp_subspace_b = 255;
void model_interp_tmappoly(ubyte * p,polymodel * pm)
{
int i;
int nv;
model_tmap_vert *verts;
int cull = 0;
// Goober5000
int tmap_num = w(p+40);
texture_map *tmap = &pm->maps[tmap_num];
texture_info *tbase = &tmap->textures[TM_BASE_TYPE];
texture_info *tglow = &tmap->textures[TM_GLOW_TYPE];
int rt_begin_index = tmap_num*TM_NUM_TYPES;
// Goober5000
Interp_base_frametime = 0;
if (Interp_objnum >= 0)
{
object *objp = &Objects[Interp_objnum];
if (objp->type == OBJ_SHIP)
Interp_base_frametime = Ships[objp->instance].base_texture_anim_frametime;
}
int is_invisible = 0;
if (Interp_warp_bitmap < 0) {
if ( (!Interp_thrust_scale_subobj) && (tbase->GetTexture() < 0) ) {
// Ignore the following if we're drawing in outline mode. Fixes Mantis #2931.
if (!(Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET))) {
// Don't draw invisible polygons.
if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES) )
return;
else
is_invisible = 1;
}
}
}
nv = w(p+36);
#ifndef NDEBUG
modelstats_num_polys++;
#endif
if ( nv < 0 )
return;
verts = (model_tmap_vert *)(p+44);
int max_n_verts = 0;
int max_n_norms = 0;
for (i = 0; i < nv; i++) {
max_n_verts = MAX(verts[i].vertnum + 1, max_n_verts);
max_n_norms = MAX(verts[i].normnum + 1, max_n_norms);
}
model_allocate_interp_data(max_n_verts, max_n_norms, nv);
if ( !Cmdline_nohtl ) {
if (Interp_warp_bitmap < 0) {
if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)){
if(!splodeing) return;
}
}
if(splodeing){
float salpha = 1.0f - splode_level;
for (i=0;i<nv;i++){
Interp_list[i] = &Interp_splode_points[verts[i].vertnum];
Interp_list[i]->texture_position.u = verts[i].u*2;
Interp_list[i]->texture_position.v = verts[i].v*2;
Interp_list[i]->r = (unsigned char)(255*salpha);
Interp_list[i]->g = (unsigned char)(250*salpha);
Interp_list[i]->b = (unsigned char)(200*salpha);
model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], salpha, false);
}
cull = gr_set_cull(0);
gr_set_bitmap( splodeingtexture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, salpha );
g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_GOURAUD);
gr_set_cull(cull);
return;
}
}
for (i=0;i<nv;i++) {
Interp_list[i] = &Interp_points[verts[i].vertnum];
Interp_list[i]->texture_position.u = verts[i].u;
Interp_list[i]->texture_position.v = verts[i].v;
if ( Interp_subspace ) {
Interp_list[i]->texture_position.v += Interp_subspace_offset_u;
Interp_list[i]->texture_position.u += Interp_subspace_offset_v;
Interp_list[i]->r = Interp_subspace_r;
Interp_list[i]->g = Interp_subspace_g;
Interp_list[i]->b = Interp_subspace_b;
} else {
if ( (Interp_flags & MR_NO_LIGHTING) || (tmap->is_ambient)) { //gets the ambient glow to work
Interp_list[i]->r = 191;
Interp_list[i]->g = 191;
Interp_list[i]->b = 191;
Interp_list[i]->spec_r = 0;
Interp_list[i]->spec_g = 0;
Interp_list[i]->spec_b = 0;
if (Interp_flags & MR_EDGE_ALPHA) {
model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], Interp_warp_alpha, false);
}
if (Interp_flags & MR_CENTER_ALPHA) {
model_interp_edge_alpha(&Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[verts[i].vertnum], Interp_norms[verts[i].normnum], Interp_warp_alpha, true);
}
SPECMAP = -1;
NORMMAP = -1;
HEIGHTMAP = -1;
MISCMAP = -1;
} else {
int vertnum = verts[i].vertnum;
int norm = verts[i].normnum;
if ( Interp_flags & MR_NO_SMOOTHING ) {
light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light );
if((Detail.lighting > 2) && (Interp_detail_level < 2) && Cmdline_spec )
light_apply_specular( &Interp_list[i]->spec_r, &Interp_list[i]->spec_g, &Interp_list[i]->spec_b, Interp_verts[vertnum], vp(p+8), &View_position);
} else {
// if we're applying lighting as normal, and not using saved lighting
if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) {
light_apply_rgb( &Interp_lighting->lights[norm].r, &Interp_lighting->lights[norm].g, &Interp_lighting->lights[norm].b, Interp_verts[vertnum], Interp_norms[norm], Interp_light );
if((Detail.lighting > 2) && (Interp_detail_level < 2) && Cmdline_spec )
light_apply_specular( &Interp_lighting->lights[norm].spec_r, &Interp_lighting->lights[norm].spec_g, &Interp_lighting->lights[norm].spec_b, Interp_verts[vertnum], Interp_norms[norm], &View_position);
Interp_light_applied[norm] = 1;
}
Interp_list[i]->spec_r = Interp_lighting->lights[norm].spec_r;
Interp_list[i]->spec_g = Interp_lighting->lights[norm].spec_g;
Interp_list[i]->spec_b = Interp_lighting->lights[norm].spec_b;
Interp_list[i]->r = Interp_lighting->lights[norm].r;
Interp_list[i]->g = Interp_lighting->lights[norm].g;
Interp_list[i]->b = Interp_lighting->lights[norm].b;
}
}
}
}
#ifndef NDEBUG
modelstats_num_polys_drawn++;
#endif
if (!(Interp_flags & MR_NO_POLYS) ) {
if ( is_invisible ) {
gr_set_color( 0, 255, 0 );
g3_draw_poly( nv, Interp_list, 0 );
} else if (Interp_thrust_scale_subobj) {
if ( (Interp_thrust_bitmap >= 0) && (Interp_thrust_scale > 0.0f) ) {
gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f );
g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );
} else {
if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
gr_set_color( 128, 128, 255 );
}
uint tflags = Interp_tmap_flags;
tflags &= (~(TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT));
g3_draw_poly( nv, Interp_list, tflags );
}
} else if (Interp_warp_bitmap >= 0) { //warpin effect-Bobboau
gr_set_bitmap( Interp_warp_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_warp_alpha );
g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED );
}else{
// all textured polys go through here
if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED ) {
// subspace special case
if (Interp_subspace) {
gr_set_bitmap( tbase->GetTexture(), GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f );
}
// all other textures
else {
int texture;
// if we're rendering a nebula background pof, maybe select a custom texture
if((Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0)){
texture = Interp_forced_bitmap;
}else if((Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE] >= 0)){
texture_info tt = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE]);
texture = model_interp_get_texture(&tt, Interp_base_frametime);
} else {
// pick the texture, animating it if necessary
texture = model_interp_get_texture(tbase, Interp_base_frametime);
// doing glow maps?
if ( !(Interp_flags & MR_NO_GLOWMAPS) && (tglow->GetTexture() >= 0) ) {
// shockwaves are special, their current frame has to come out of the shockwave code to get the timing correct
if ( (Interp_objnum >= 0) && (Objects[Interp_objnum].type == OBJ_SHOCKWAVE) && (tglow->GetNumFrames() > 1) ) {
GLOWMAP = tglow->GetTexture() + shockwave_get_framenum(Objects[Interp_objnum].instance, tglow->GetNumFrames());
} else {
GLOWMAP = model_interp_get_texture(tglow, Interp_base_frametime);
}
}
if((Detail.lighting > 2) && (Interp_detail_level < 2))
{
// likewise, etc.
SPECMAP = model_interp_get_texture(&tmap->textures[TM_SPECULAR_TYPE], Interp_base_frametime);
NORMMAP = model_interp_get_texture(&tmap->textures[TM_NORMAL_TYPE], Interp_base_frametime);
HEIGHTMAP = model_interp_get_texture(&tmap->textures[TM_HEIGHT_TYPE], Interp_base_frametime);
}
MISCMAP = model_interp_get_texture(&tmap->textures[TM_MISC_TYPE], Interp_base_frametime);
}
//*****
//WMC - now do other replacements
if(Interp_new_replacement_textures != NULL)
{
texture_info tinfo;
for(int tmn = TM_BASE_TYPE+1; tmn < TM_NUM_TYPES; tmn++)
{
int tex = Interp_new_replacement_textures[rt_begin_index + tmn];
if(tex < 0)
continue;
tinfo = texture_info(tex);
//Figure out actual texture to use
tex = model_interp_get_texture(&tinfo, Interp_base_frametime);
switch(tmn)
{
case TM_GLOW_TYPE:
GLOWMAP = tex;
break;
case TM_SPECULAR_TYPE:
SPECMAP = tex;
break;
case TM_NORMAL_TYPE:
NORMMAP = tex;
break;
case TM_HEIGHT_TYPE:
HEIGHTMAP = tex;
break;
case TM_MISC_TYPE:
MISCMAP = tex;
default:
break;
}
}
}
// muzzle flashes draw xparent
if(Interp_flags & MR_ALL_XPARENT){
gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha );
} else {
if(tmap->is_transparent) { //trying to get transperent textures-Bobboau
gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.8f );
}else{
gr_set_bitmap( texture );
}
}
}
} else {
if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
gr_set_color( 128, 128, 128 );
}
}
if ( Interp_subspace ) {
g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT );
} else {
if(Interp_flags & MR_ALL_XPARENT){
g3_draw_poly( nv, Interp_list, Interp_tmap_flags );
} else {
g3_draw_poly( nv, Interp_list, Interp_tmap_flags|TMAP_FLAG_NONDARKENING );
}
}
}
}
GLOWMAP = -1;
SPECMAP = -1;
NORMMAP = -1;
HEIGHTMAP = -1;
MISCMAP = -1;
if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) ) {
if ( Interp_flags & MR_SHOW_OUTLINE ) {
gr_set_color_fast( &Interp_outline_color );
}
for (i=0; i<nv; i++ ) {
int j = (i + 1) % nv;
g3_draw_line(Interp_list[i], Interp_list[j]);
}
}
}
// Sortnorms
// +0 int id
// +4 int size
// +8 vec3d normal
// +20 vec3d normal_point
// +32 int tmp=0
// 36 int front offset
// 40 int back offset
// 44 int prelist offset
// 48 int postlist offset
// 52 int online offset
void model_interp_sortnorm_b2f(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
{
#ifndef NDEBUG
modelstats_num_sortnorms++;
#endif
int frontlist = w(p+36);
int backlist = w(p+40);
int prelist = w(p+44);
int postlist = w(p+48);
int onlist = w(p+52);
if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
//draw back then front
if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
} else { //not facing. draw front then back
if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
}
if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
}
// Sortnorms
// +0 int id
// +4 int size
// +8 vec3d normal
// +20 vec3d normal_point
// +32 int tmp=0
// 36 int front offset
// 40 int back offset
// 44 int prelist offset
// 48 int postlist offset
// 52 int online offset
void model_interp_sortnorm_f2b(ubyte * p,polymodel * pm, bsp_info *sm, int do_box_check)
{
#ifndef NDEBUG
modelstats_num_sortnorms++;
#endif
int frontlist = w(p+36);
int backlist = w(p+40);
int prelist = w(p+44);
int postlist = w(p+48);
int onlist = w(p+52);
if (postlist) model_interp_sub(p+postlist,pm,sm,do_box_check); // postlist
if (g3_check_normal_facing(vp(p+20),vp(p+8))) { //facing
if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
} else { //not facing. draw front then back
//draw back then front
if (backlist) model_interp_sub(p+backlist,pm,sm,do_box_check);
if (onlist) model_interp_sub(p+onlist,pm,sm,do_box_check); //onlist
if (frontlist) model_interp_sub(p+frontlist,pm,sm,do_box_check);
}
if (prelist) model_interp_sub(p+prelist,pm,sm,do_box_check); // prelist
}
void model_draw_debug_points( polymodel *pm, bsp_info * submodel )
{
if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
return;
}
// Draw a red pivot point
gr_set_color(128,0,0);
g3_draw_sphere_ez(&vmd_zero_vector, 2.0f );
// Draw a green center of mass when drawing the hull
if ( submodel && (submodel->parent==-1) ) {
gr_set_color(0,128,0);
g3_draw_sphere_ez( &pm->center_of_mass, 1.0f );
}
if ( submodel ) {
// Draw a blue center point
gr_set_color(0,0,128);
g3_draw_sphere_ez( &submodel->geometric_center, 0.9f );
}
// Draw the bounding box
int i;
vertex pts[8];
if ( submodel ) {
for (i=0; i<8; i++ ) {
g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] );
}
gr_set_color(128,128,128);
g3_draw_line( &pts[0], &pts[1] );
g3_draw_line( &pts[1], &pts[2] );
g3_draw_line( &pts[2], &pts[3] );
g3_draw_line( &pts[3], &pts[0] );
g3_draw_line( &pts[4], &pts[5] );
g3_draw_line( &pts[5], &pts[6] );
g3_draw_line( &pts[6], &pts[7] );
g3_draw_line( &pts[7], &pts[4] );
g3_draw_line( &pts[0], &pts[4] );
g3_draw_line( &pts[1], &pts[5] );
g3_draw_line( &pts[2], &pts[6] );
g3_draw_line( &pts[3], &pts[7] );
} else {
gr_set_color(0,255,0);
int j;
for (j=0; j<8; j++ ) {
vec3d bounding_box[8]; // caclulated fron min/max
model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max);
for (i=0; i<8; i++ ) {
g3_rotate_vertex( &pts[i], &bounding_box[i] );
}
gr_set_color(128,0,0);
g3_draw_line( &pts[0], &pts[1] );
g3_draw_line( &pts[1], &pts[2] );
g3_draw_line( &pts[2], &pts[3] );
g3_draw_line( &pts[3], &pts[0] );
g3_draw_line( &pts[4], &pts[5] );
g3_draw_line( &pts[5], &pts[6] );
g3_draw_line( &pts[6], &pts[7] );
g3_draw_line( &pts[7], &pts[4] );
g3_draw_line( &pts[0], &pts[4] );
g3_draw_line( &pts[1], &pts[5] );
g3_draw_line( &pts[2], &pts[6] );
g3_draw_line( &pts[3], &pts[7] );
}
}
}
/**
* Debug code to show all the paths of a model
*/
void model_draw_paths( int model_num )
{
int i,j;
vec3d pnt;
polymodel * pm;
if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
return;
}
pm = model_get(model_num);
if (pm->n_paths<1){
return;
}
for (i=0; i<pm->n_paths; i++ ) {
vertex prev_pnt;
for (j=0; j<pm->paths[i].nverts; j++ ) {
// Rotate point into world coordinates
pnt = pm->paths[i].verts[j].pos;
// Pnt is now the x,y,z world coordinates of this vert.
// For this example, I am just drawing a sphere at that
// point.
{
vertex tmp;
g3_rotate_vertex(&tmp,&pnt);
if ( pm->paths[i].verts[j].nturrets > 0 ){
gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
} else {
gr_set_color( 255, 0, 0 );
}
g3_draw_sphere( &tmp, 0.5f );
if (j){
g3_draw_line(&prev_pnt, &tmp);
}
prev_pnt = tmp;
}
}
}
}
/**
* Debug code to show all the paths of a model
*/
void model_draw_paths_htl( int model_num )
{
int i,j;
vec3d pnt;
vec3d prev_pnt;
polymodel * pm;
if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
return;
}
pm = model_get(model_num);
if (pm->n_paths<1){
return;
}
int cull = gr_set_cull(0);
for (i=0; i<pm->n_paths; i++ ) {
for (j=0; j<pm->paths[i].nverts; j++ )
{
// Rotate point into world coordinates
pnt = pm->paths[i].verts[j].pos;
// Pnt is now the x,y,z world coordinates of this vert.
// For this example, I am just drawing a sphere at that
// point.
vertex tmp;
g3_rotate_vertex(&tmp,&pnt);
if ( pm->paths[i].verts[j].nturrets > 0 ){
gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue
} else {
gr_set_color( 255, 0, 0 );
}
g3_draw_htl_sphere(&pnt, 0.5f);
if (j)
{
g3_draw_htl_line(&prev_pnt, &pnt);
}
prev_pnt = pnt;
}
}
gr_set_cull(cull);
}
/**
* Docking bay and fighter bay paths
*/
void model_draw_bay_paths_htl(int model_num)
{
int idx, s_idx;
vec3d v1, v2;
polymodel *pm = model_get(model_num);
if(pm == NULL){
return;
}
int cull = gr_set_cull(0);
// render docking bay normals
gr_set_color(0, 255, 0);
for(idx=0; idx<pm->n_docks; idx++){
for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
v1 = pm->docking_bays[idx].pnt[s_idx];
vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
// draw the point and normal
g3_draw_htl_sphere(&v1, 2.0);
g3_draw_htl_line(&v1, &v2);
}
}
// render figher bay paths
gr_set_color(0, 255, 255);
// iterate through the paths that exist in the polymodel, searching for $bayN pathnames
for (idx = 0; idx<pm->n_paths; idx++) {
if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
v1 = pm->paths[idx].verts[s_idx].pos;
v2 = pm->paths[idx].verts[s_idx+1].pos;
g3_draw_htl_line(&v1, &v2);
}
}
}
gr_set_cull(cull);
}
/**
* Docking bay and fighter bay paths
*/
void model_draw_bay_paths(int model_num)
{
int idx, s_idx;
vec3d v1, v2;
vertex l1, l2;
polymodel *pm = model_get(model_num);
if(pm == NULL){
return;
}
// render docking bay normals
gr_set_color(0, 255, 0);
for(idx=0; idx<pm->n_docks; idx++){
for(s_idx=0; s_idx<pm->docking_bays[idx].num_slots; s_idx++){
v1 = pm->docking_bays[idx].pnt[s_idx];
vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f);
// rotate the points
g3_rotate_vertex(&l1, &v1);
g3_rotate_vertex(&l2, &v2);
// draw the point and normal
g3_draw_sphere(&l1, 2.0);
g3_draw_line(&l1, &l2);
}
}
// render figher bay paths
gr_set_color(0, 255, 255);
// iterate through the paths that exist in the polymodel, searching for $bayN pathnames
for (idx = 0; idx<pm->n_paths; idx++) {
if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) {
for(s_idx=0; s_idx<pm->paths[idx].nverts-1; s_idx++){
v1 = pm->paths[idx].verts[s_idx].pos;
v2 = pm->paths[idx].verts[s_idx+1].pos;
// rotate and draw
g3_rotate_vertex(&l1, &v1);
g3_rotate_vertex(&l2, &v2);
g3_draw_line(&l1, &l2);
}
}
}
}
static const int MAX_ARC_SEGMENT_POINTS = 50;
int Num_arc_segment_points = 0;
vec3d Arc_segment_points[MAX_ARC_SEGMENT_POINTS];
extern int g3_draw_rod(int num_points, vec3d *vecs, float width, uint tmap_flags);
void interp_render_arc_segment( vec3d *v1, vec3d *v2, int depth )
{
float d = vm_vec_dist_quick( v1, v2 );
const float scaler = 0.30f;
if ( (d < scaler) || (depth > 4) ) {
// the real limit appears to be 33, so we should never hit this unless the code changes
Assert( Num_arc_segment_points < MAX_ARC_SEGMENT_POINTS );
memcpy( &Arc_segment_points[Num_arc_segment_points++], v2, sizeof(vec3d) );
} else {
// divide in half
vec3d tmp;
vm_vec_avg( &tmp, v1, v2 );
tmp.xyz.x += (frand() - 0.5f) * d * scaler;
tmp.xyz.y += (frand() - 0.5f) * d * scaler;
tmp.xyz.z += (frand() - 0.5f) * d * scaler;
// add additional point
interp_render_arc_segment( v1, &tmp, depth+1 );
interp_render_arc_segment( &tmp, v2, depth+1 );
}
}
void interp_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);
int tmap_flags = (TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_HTL_3D_UNLIT | TMAP_FLAG_TRISTRIP);
int mode = gr_zbuffer_set(GR_ZBUFF_READ);
// use primary color for fist pass
Assert( primary );
gr_set_color_fast(primary);
g3_draw_rod(Num_arc_segment_points, Arc_segment_points, arc_width, tmap_flags);
if (secondary) {
// now render again with a secondary center color
gr_set_color_fast(secondary);
g3_draw_rod(Num_arc_segment_points, Arc_segment_points, arc_width * 0.33f, tmap_flags);
}
gr_zbuffer_set(mode);
}
int Interp_lightning = 1;
DCF_BOOL( Arcs, Interp_lightning )
const int AR = 64;
const int AG = 64;
const int AB = 5;
const int AR2 = 128;
const int AG2 = 128;
const int AB2 = 10;
void interp_render_lightning( polymodel *pm, bsp_info * sm )
{
int i;
float width = 0.9f;
color primary, secondary;
Assert( sm->num_arcs > 0 );
if (Interp_flags & MR_SHOW_OUTLINE_PRESET) {
return;
}
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
interp_render_arc( &sm->arc_pts[i][0], &sm->arc_pts[i][1], &primary, &secondary, width );
}
}
void model_interp_subcall(polymodel * pm, int mn, int detail_level)
{
int i;
int zbuf_mode = gr_zbuffering_mode;
if ( (mn < 0) || (mn>=pm->n_models) )
return;
Assert( mn >= 0 );
Assert( mn < pm->n_models );
if (pm->submodel[mn].blown_off){
return;
}
if (pm->submodel[mn].is_thruster ) {
if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){
return;
}
Interp_thrust_scale_subobj=1;
} else {
Interp_thrust_scale_subobj=0;
}
// 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 - KeldorKatarn
matrix rotation_matrix = pm->submodel[mn].orientation;
vm_rotate_matrix_by_angles(&rotation_matrix, &pm->submodel[mn].angs);
matrix inv_orientation;
vm_copy_transpose_matrix(&inv_orientation, &pm->submodel[mn].orientation);
matrix submodel_matrix;
vm_matrix_x_matrix(&submodel_matrix, &rotation_matrix, &inv_orientation);
g3_start_instance_matrix(&pm->submodel[mn].offset, &submodel_matrix, true);
if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
light_rotate_all();
}
model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 );
if (Interp_flags & MR_SHOW_PIVOTS )
model_draw_debug_points( pm, &pm->submodel[mn] );
if ( pm->submodel[mn].num_arcs ) {
interp_render_lightning( pm, &pm->submodel[mn]);
}
i = pm->submodel[mn].first_child;
while( i >= 0 ) {
if (!pm->submodel[i].is_thruster ) {
if(Interp_flags & MR_NO_ZBUFFER){
zbuf_mode = GR_ZBUFF_NONE;
} else {
zbuf_mode = GR_ZBUFF_FULL; // read only
}
gr_zbuffer_set(zbuf_mode);
model_interp_subcall( pm, i, detail_level );
}
i = pm->submodel[i].next_sibling;
}
g3_done_instance(true);
}
// Returns one of the following
#define IBOX_ALL_OFF 0
#define IBOX_ALL_ON 1
#define IBOX_SOME_ON_SOME_OFF 2
int interp_box_offscreen( vec3d *min, vec3d *max )
{
if ( keyd_pressed[KEY_LSHIFT] ) {
return IBOX_ALL_ON;
}
vec3d v[8];
v[0].xyz.x = min->xyz.x; v[0].xyz.y = min->xyz.y; v[0].xyz.z = min->xyz.z;
v[1].xyz.x = max->xyz.x; v[1].xyz.y = min->xyz.y; v[1].xyz.z = min->xyz.z;
v[2].xyz.x = max->xyz.x; v[2].xyz.y = max->xyz.y; v[2].xyz.z = min->xyz.z;
v[3].xyz.x = min->xyz.x; v[3].xyz.y = max->xyz.y; v[3].xyz.z = min->xyz.z;
v[4].xyz.x = min->xyz.x; v[4].xyz.y = min->xyz.y; v[4].xyz.z = max->xyz.z;
v[5].xyz.x = max->xyz.x; v[5].xyz.y = min->xyz.y; v[5].xyz.z = max->xyz.z;
v[6].xyz.x = max->xyz.x; v[6].xyz.y = max->xyz.y; v[6].xyz.z = max->xyz.z;
v[7].xyz.x = min->xyz.x; v[7].xyz.y = max->xyz.y; v[7].xyz.z = max->xyz.z;
ubyte and_codes = 0xff;
ubyte or_codes = 0xff;
int i;
for (i=0; i<8; i++ ) {
vertex tmp;
ubyte codes=g3_rotate_vertex( &tmp, &v[i] );
or_codes |= codes;
and_codes &= codes;
}
// If and_codes is set this means that all points lie off to the
// same side of the screen.
if (and_codes) {
return IBOX_ALL_OFF; //all points off screen
}
// If this is set it means at least one of the points is offscreen,
// but they aren't all off to the same side.
if (or_codes) {
return IBOX_SOME_ON_SOME_OFF;
}
// They are all onscreen.
return IBOX_ALL_ON;
}
/**
* Calls the object interpreter to render an object.
* @return true if drawn
*/
int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check )
{
ubyte *p = (ubyte *)model_ptr;
int chunk_type, chunk_size;
int pushed = 0;
chunk_type = w(p);
chunk_size = w(p+4);
while ( chunk_type != OP_EOF ) {
switch (chunk_type) {
case OP_EOF: return 1;
case OP_DEFPOINTS: model_interp_defpoints(p,pm,sm); break;
case OP_FLATPOLY: model_interp_flatpoly(p,pm); break;
case OP_TMAPPOLY: model_interp_tmappoly(p,pm); break;
case OP_SORTNORM: model_interp_sortnorm(p,pm,sm,do_box_check); break;
case OP_BOUNDBOX:
if ( do_box_check ) {
int retval = interp_box_offscreen( vp(p+8), vp(p+20) );
switch( retval ) {
case IBOX_ALL_OFF:
goto DoneWithThis; // Don't need to draw any more polys from this box
break;
case IBOX_ALL_ON:
do_box_check = 0; // Don't need to check boxes any more
break;
case IBOX_SOME_ON_SOME_OFF:
// continue like we were
break;
default:
Int3();
}
}
if (Interp_flags & MR_SHOW_PIVOTS ) {
#ifndef NDEBUG
modelstats_num_boxes++;
#endif
}
if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
if ( pushed ) {
light_filter_pop();
pushed = 0;
}
light_filter_push_box( vp(p+8), vp(p+20) );
pushed = 1;
}
break;
default:
mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size ));
Int3(); // Bad chunk type!
return 0;
}
p += chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
}
DoneWithThis:
if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
if ( pushed ) {
light_filter_pop();
pushed = 0;
}
}
return 1;
}
void model_render_shields( polymodel * pm )
{
model_set_outline_color(255,255,255);
int i, j;
shield_tri *tri;
vertex pnt0, tmp, prev_pnt;
if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) {
return;
}
gr_set_color(0, 0, 200 );
// Scan all the triangles in the mesh.
for (i=0; i<pm->shield.ntris; i++ ) {
tri = &pm->shield.tris[i];
if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) {
// Process the vertices.
// Note this rotates each vertex each time it's needed, very dumb.
for (j=0; j<3; j++ ) {
g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos );
if (j)
g3_draw_line(&prev_pnt, &tmp);
else
pnt0 = tmp;
prev_pnt = tmp;
}
g3_draw_line(&pnt0, &prev_pnt);
}
}
}
void model_render_insignias(polymodel *pm, int detail_level)
{
// if the model has no insignias, or we don't have a texture, then bail
if ( (pm->num_ins <= 0) || (Interp_insignia_bitmap < 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;
// set the proper texture
gr_set_bitmap(Interp_insignia_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 0.65f);
// 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);
if(Cmdline_nohtl){
g3_rotate_vertex(&vecs[0], &t1);
g3_rotate_vertex(&vecs[1], &t2);
g3_rotate_vertex(&vecs[2], &t3);
}else{
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];
if (!Cmdline_nohtl) {
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 );
tmap_flags |= (TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD);
}
// draw the polygon
g3_draw_poly(3, vlist, tmap_flags);
}
}
}
int Model_texturing = 1;
int Model_polys = 1;
DCF_BOOL( model_texturing, Model_texturing )
DCF_BOOL( model_polys, Model_polys )
MONITOR( NumModelsRend )
MONITOR( NumHiModelsRend )
MONITOR( NumMedModelsRend )
MONITOR( NumLowModelsRend )
/**
* Draws a bitmap with the specified 3d width & height
* @return 1 if off screen, 0 if not
*/
int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v)
{
float sa, ca;
int i;
Assert( G3_count == 1 );
sa = (float)sin(angle);
ca = (float)cos(angle);
float width, height;
width = height = rad;
v[0].world.xyz.x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
v[0].world.xyz.y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
v[0].world.xyz.z = pnt->world.xyz.z;
v[0].screen.xyw.w = 0.0f;
v[0].texture_position.u = 0.0f;
v[0].texture_position.v = 0.0f;
v[1].world.xyz.x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
v[1].world.xyz.y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
v[1].world.xyz.z = pnt->world.xyz.z;
v[1].screen.xyw.w = 0.0f;
v[1].texture_position.u = 1.0f;
v[1].texture_position.v = 0.0f;
v[2].world.xyz.x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
v[2].world.xyz.y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
v[2].world.xyz.z = pnt->world.xyz.z;
v[2].screen.xyw.w = 0.0f;
v[2].texture_position.u = 1.0f;
v[2].texture_position.v = 1.0f;
v[3].world.xyz.x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x;
v[3].world.xyz.y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y;
v[3].world.xyz.z = pnt->world.xyz.z;
v[3].screen.xyw.w = 0.0f;
v[3].texture_position.u = 0.0f;
v[3].texture_position.v = 1.0f;
ubyte codes_and=0xff;
float sw,z;
z = pnt->world.xyz.z - rad / 4.0f;
if ( z < 0.0f ) z = 0.0f;
sw = 1.0f / z;
for (i=0; i<4; i++ ) {
//now code the four points
codes_and &= g3_code_vertex(&v[i]);
v[i].flags = 0; // mark as not yet projected
g3_project_vertex(&v[i]);
v[i].screen.xyw.w = sw;
}
if (codes_and)
return 1; //1 means off screen
return 0;
}
float Interp_depth_scale = 1500.0f;
DCF(model_darkening,"Makes models darker with distance")
{
if ( Dc_command ) {
dc_get_arg(ARG_FLOAT);
Interp_depth_scale = Dc_arg_float;
}
if ( Dc_help ) {
dc_printf( "Usage: model_darkening float\n" );
Dc_status = 0; // don't print status if help is printed. Too messy.
}
if ( Dc_status ) {
dc_printf( "model_darkening = %.1f\n", Interp_depth_scale );
}
}
void model_render(int model_num, matrix *orient, vec3d * pos, uint flags, int objnum, int lighting_skip, int *replacement_textures)
{
int cull = 0;
// replacement textures - Goober5000
model_set_replacement_textures(replacement_textures);
polymodel *pm = model_get(model_num);
model_do_dumb_rotation(model_num);
if (flags & MR_FORCE_CLAMP)
gr_set_texture_addressing(TMAP_ADDRESS_CLAMP);
int time = timestamp();
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 (bank->glow_timestamp == 0)
bank->glow_timestamp=time;
if(bank->off_time){
if(bank->is_on){
if( (bank->on_time) > ((time - bank->disp_time) % (bank->on_time + bank->off_time)) ){
bank->glow_timestamp=time;
bank->is_on=0;
}
}else{
if( (bank->off_time) < ((time - bank->disp_time) % (bank->on_time + bank->off_time)) ){
bank->glow_timestamp=time;
bank->is_on=1;
}
}
}
}
// maybe turn off (hardware) culling
if(flags & MR_NO_CULL){
cull = gr_set_cull(0);
}
Interp_objnum = objnum;
if ( flags & MR_NO_LIGHTING ) {
Interp_light = 1.0f;
} else if ( flags & MR_IS_ASTEROID ) {
// Dim it based on distance
float depth = vm_vec_dist_quick( pos, &Eye_position );
if ( depth > Interp_depth_scale ) {
Interp_light = Interp_depth_scale/depth;
// If it is too far, exit
if ( Interp_light < (1.0f/32.0f) ) {
Interp_light = 0.0f;
return;
} else if ( Interp_light > 1.0f ) {
Interp_light = 1.0f;
}
} else {
Interp_light = 1.0f;
}
} else {
Interp_light = 1.0f;
}
if ( !(flags & MR_NO_LIGHTING ) ) {
light_filter_push( objnum, pos, pm->rad );
}
model_really_render(model_num, orient, pos, flags, objnum);
if ( !(flags & MR_NO_LIGHTING ) ) {
light_filter_pop();
}
// maybe turn culling back on
if(flags & MR_NO_CULL){
gr_set_cull(cull);
}
// turn off fog after each model renders
if(The_mission.flags & MISSION_FLAG_FULLNEB){
gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
}
if (flags & MR_FORCE_CLAMP)
gr_set_texture_addressing(TMAP_ADDRESS_WRAP);
}
// tmp_detail_level
// 0 - Max
// 1
// 2
// 3
// 4 - None
#if MAX_DETAIL_LEVEL != 4
#error MAX_DETAIL_LEVEL is assumed to be 4 in ModelInterp.cpp
#endif
/**
* Find the distance from p0 to the closest point on a box.
* The box's dimensions from 'min' to 'max'.
*/
float interp_closest_dist_to_box( vec3d *hitpt, vec3d *p0, vec3d *min, vec3d *max )
{
float *origin = (float *)&p0->xyz.x;
float *minB = (float *)min;
float *maxB = (float *)max;
float *coord = (float *)&hitpt->xyz.x;
int inside = 1;
int i;
for (i=0; i<3; i++ ) {
if ( origin[i] < minB[i] ) {
coord[i] = minB[i];
inside = 0;
} else if (origin[i] > maxB[i] ) {
coord[i] = maxB[i];
inside = 0;
} else {
coord[i] = origin[i];
}
}
if ( inside ) {
return 0.0f;
}
return vm_vec_dist(hitpt,p0);
}
// Finds the closest point on a model to a point in space. Actually only finds a point
// on the bounding box of the model.
// Given:
// model_num Which model
// orient Orientation of the model
// pos Position of the model
// eye_pos Point that you want to find the closest point to
// Returns:
// distance from eye_pos to closest_point. 0 means eye_pos is
// on or inside the bounding box.
// Also fills in outpnt with the actual closest point.
float model_find_closest_point( vec3d *outpnt, int model_num, int submodel_num, matrix *orient, vec3d * pos, vec3d *eye_pos )
{
vec3d closest_pos, tempv, eye_rel_pos;
polymodel *pm = model_get(model_num);
if ( submodel_num < 0 ) {
submodel_num = pm->detail[0];
}
// Rotate eye pos into object coordinates
vm_vec_sub(&tempv,pos,eye_pos );
vm_vec_rotate(&eye_rel_pos,&tempv,orient );
return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max );
}
int tiling = 1;
DCF(tiling, "")
{
tiling = !tiling;
if(tiling){
dc_printf("Tiled textures\n");
} else {
dc_printf("Non-tiled textures\n");
}
}
void moldel_calc_facing_pts( vec3d *top, vec3d *bot, vec3d *fvec, vec3d *pos, float w, float z_add, vec3d *Eyeposition )
{
vec3d uvec, rvec;
vec3d temp;
temp = *pos;
vm_vec_sub( &rvec, Eyeposition, &temp );
vm_vec_normalize( &rvec );
vm_vec_crossprod(&uvec,fvec,&rvec);
vm_vec_normalize(&uvec);
vm_vec_scale_add( top, &temp, &uvec, w/2.0f );
vm_vec_scale_add( bot, &temp, &uvec, -w/2.0f );
}
extern bool Scene_framebuffer_in_frame;
/**
* Maybe draw mode thruster glows
*/
void model_render_thrusters(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 ( pm == NULL ) {
Int3();
return;
}
if ( !(Interp_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;
}
// primary_thruster_batcher
if (Interp_thrust_glow_bitmap >= 0) {
do_render = true;
}
// secondary_thruster_batcher
if (Interp_secondary_thrust_glow_bitmap >= 0) {
do_render = true;
}
// tertiary_thruster_batcher
if (Interp_tertiary_thrust_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 *= Interp_thrust_rotvel.xyz.y/2;
norm.xyz.y *= Interp_thrust_rotvel.xyz.x/2;
vm_vec_normalize(&norm);
// we need to disable fogging
if (The_mission.flags & MISSION_FLAG_FULLNEB)
gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
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 ( shipp && 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, &Objects[objnum], 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->flags & (SF_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 & (SF_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 *= Interp_thrust_scale_x) -= 0.1f;
(scale_vec.xyz.y *= Interp_thrust_scale_y) -= 0.1f;
(scale_vec.xyz.z *= Interp_thrust_scale) -= 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_FLAG_FULLNEB) {
vm_vec_rotate(&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 + Interp_thrust_glow_noise * NOISE_SCALE);
// these lines are used by the tertiary glows, thus we will need to project this all of the time
if (Cmdline_nohtl) {
g3_rotate_vertex( &p, &world_pnt );
} else {
g3_transfer_vertex( &p, &world_pnt );
}
// start primary thruster glows
if ( (Interp_thrust_glow_bitmap >= 0) && (d > 0.0f) ) {
p.r = p.g = p.b = p.a = (ubyte)(255.0f * d);
batch_add_bitmap(
Interp_thrust_glow_bitmap,
TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD,
&p,
0,
(w * 0.5f * Interp_thrust_glow_rad_factor),
1.0f,
(w * 0.325f)
);
}
// start tertiary thruster glows
if (Interp_tertiary_thrust_glow_bitmap >= 0) {
p.screen.xyw.w -= w;
p.r = p.g = p.b = p.a = (ubyte)(255.0f * fog_int);
batch_add_bitmap_rotated(
Interp_tertiary_thrust_glow_bitmap,
TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD,
&p,
(magnitude * 4),
(w * 0.6f * Interp_tertiary_thrust_glow_rad_factor),
1.0f,
(-(D > 0) ? D : -D)
);
}
// begin secondary glows
if (Interp_secondary_thrust_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 * Interp_thrust_glow_len_factor);
if (The_mission.flags & MISSION_FLAG_FULLNEB) {
vm_vec_add(&npnt, &pnt, pos);
d *= fog_int;
}
batch_add_beam(Interp_secondary_thrust_glow_bitmap,
TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT | TMAP_HTL_3D_UNLIT,
&pnt, &norm2, wVal*Interp_secondary_thrust_glow_rad_factor*0.5f, d
);
if (Scene_framebuffer_in_frame && Interp_draw_distortion) {
vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * Interp_distortion_thrust_length_factor);
int dist_bitmap;
if (Interp_distortion_thrust_bitmap > 0) {
dist_bitmap = Interp_distortion_thrust_bitmap;
}
else {
dist_bitmap = Interp_secondary_thrust_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
distortion_add_beam(dist_bitmap,
TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT | TMAP_HTL_3D_UNLIT | TMAP_FLAG_DISTORTION_THRUSTER | TMAP_FLAG_SOFT_QUAD,
&pnt, &norm2, wVal*Interp_distortion_thrust_rad_factor*0.5f, 1.0f, mag
);
}
}
}
// begin particles
if (shipp) {
ship_info *sip = &Ship_info[shipp->ship_info_index];
particle_emitter pe;
thruster_particles *tp;
size_t num_particles = 0;
if (Interp_afterburner)
num_particles = sip->afterburner_thruster_particles.size();
else
num_particles = sip->normal_thruster_particles.size();
for (k = 0; k < num_particles; k++) {
if (Interp_afterburner)
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.0;
pe.max_life = 1.0;
particle_emit( &pe, PARTICLE_BITMAP, tp->thruster_bitmap.first_frame);
}
}
}
}
// save current zbuffer, and set the correct mode for us
int zbuff_save = gr_zbuffering_mode;
gr_zbuffer_set(GR_ZBUFF_READ);
gr_zbuffer_set(zbuff_save);
}
void model_render_glow_points(polymodel *pm, ship *shipp, matrix *orient, vec3d *pos, bool use_depth_buffer = true)
{
int i, j;
int cull = gr_set_cull(0);
for (i = 0; i < pm->n_glow_point_banks; i++ ) {
glow_point_bank *bank = &pm->glow_point_banks[i];
if (pm->submodel[bank->submodel_parent].blown_off)
continue;
if (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) {
glow_point *gpt = &bank->points[j];
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, &Objects[shipp->objnum], 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) && (shipp->flags & (SF_ARRIVING | SF_DEPART_WARP) ) && (shipp->warpin_effect) && Ship_info[shipp->ship_info_index].warpin_type != WT_HYPERSPACE) {
if (g3_point_behind_user_plane(&world_pnt))
continue;
}
switch (bank->type)
{
case 0:
{
float d;
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;
}
if (d > 0.0f) {
vertex p;
d *= 3.0f;
if (d > 1.0f)
d = 1.0f;
float w = gpt->radius;
// fade them in the nebula as well
if (The_mission.flags & MISSION_FLAG_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
}
// disable fogging
if (Interp_tmap_flags & TMAP_FLAG_PIXEL_FOG)
gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
if (!Cmdline_nohtl) {
g3_transfer_vertex(&p, &world_pnt);
} else {
g3_rotate_vertex(&p, &world_pnt);
}
p.r = p.g = p.b = p.a = (ubyte)(255.0f * d);
int gpflags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT;
if (use_depth_buffer)
gpflags |= TMAP_FLAG_SOFT_QUAD;
batch_add_bitmap(
bank->glow_bitmap,
gpflags,
&p,
0,
(w * 0.5f),
d
);
} //d>0.0f
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);
int idx = 0;
if (Cmdline_nohtl) {
g3_rotate_vertex(&verts[0], &bottom1);
g3_rotate_vertex(&verts[1], &bottom2);
g3_rotate_vertex(&verts[2], &top2);
g3_rotate_vertex(&verts[3], &top1);
for (idx = 0; idx < 4; idx++) {
g3_project_vertex(&verts[idx]);
}
} else {
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_FLAG_FULLNEB) {
gr_set_bitmap(bank->glow_neb_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f);
} else {
gr_set_bitmap(bank->glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f);
}
gr_render(4, verts, TMAP_FLAG_TILED | TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT | TMAP_HTL_3D_UNLIT);
break;
}
} // switch(bank->type)
} // flick
} // for slot
} // bank is on
} // for bank
gr_set_cull(cull);
}
void light_set_all_relevent();
extern int Warp_model;
void model_really_render(int model_num, matrix *orient, vec3d * pos, uint flags, int objnum )
{
int i;
int cull = 1;
polymodel * pm;
uint save_gr_zbuffering_mode;
int zbuf_mode;
ship *shipp = NULL;
object *objp = NULL;
bool set_autocen = false;
bool draw_thrusters = false;
// just to be on the safe side
Assert( Interp_objnum == objnum );
if (objnum >= 0) {
objp = &Objects[objnum];
if (objp->type == OBJ_SHIP) {
shipp = &Ships[objp->instance];
if (shipp->flags2 & SF2_GLOWMAPS_DISABLED)
flags |= MR_NO_GLOWMAPS;
}
}
if (FULLCLOAK != 1)
model_interp_sortnorm = model_interp_sortnorm_b2f;
MONITOR_INC( NumModelsRend, 1 );
Interp_orient = orient;
Interp_pos = pos;
int tmp_detail_level = Game_detail_level;
// Turn off engine effect
Interp_thrust_scale_subobj=0;
if (!Model_texturing)
flags |= MR_NO_TEXTURING;
if ( !Model_polys ) {
flags |= MR_NO_POLYS;
}
Interp_flags = flags;
pm = model_get(model_num);
// Set the flags we will pass to the tmapper
Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
// if we're in nebula mode, fog everything except for the warp holes and other non-fogged models
if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE) && !(flags & MR_NO_FOGGING)){
Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
}
if ( !(Interp_flags & MR_NO_TEXTURING) ) {
Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling)
Interp_tmap_flags |= TMAP_FLAG_TILED;
if ( !(Interp_flags & MR_NO_CORRECT) ) {
Interp_tmap_flags |= TMAP_FLAG_CORRECT;
}
}
if ( Interp_flags & MR_ANIMATED_SHADER )
Interp_tmap_flags |= TMAP_ANIMATED_SHADER;
if ( Interp_desaturate ) {
Interp_tmap_flags |= TMAP_FLAG_DESATURATE;
}
save_gr_zbuffering_mode = gr_zbuffering_mode;
zbuf_mode = gr_zbuffering_mode;
if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) {
gr_set_color(0,128,0);
g3_draw_sphere_ez( pos, pm->rad );
return;
}
bool is_outlines_only = (flags & MR_NO_POLYS) && ((flags & MR_SHOW_OUTLINE_PRESET) || (flags & MR_SHOW_OUTLINE));
bool is_outlines_only_htl = !Cmdline_nohtl && (flags & MR_NO_POLYS) && (flags & MR_SHOW_OUTLINE_HTL);
bool use_api = !is_outlines_only_htl || (gr_screen.mode == GR_OPENGL);
g3_start_instance_matrix(pos, orient, use_api);
if ( Interp_flags & MR_SHOW_RADIUS ) {
if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) {
gr_set_color(0,64,0);
g3_draw_sphere_ez(&vmd_zero_vector,pm->rad);
}
}
Assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS );
vec3d closest_pos;
float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position );
if ( pm->n_detail_levels > 1 ) {
if ( Interp_flags & MR_LOCK_DETAIL ) {
i = Interp_detail_level_locked+1;
} else {
#if MAX_DETAIL_LEVEL != 4
#error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4
#endif
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_FLAG_FULLNEB){
depth *= neb2_get_lod_scale(Interp_objnum);
}
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;
}
}
// maybe force lower detail
if (Interp_flags & MR_FORCE_LOWER_DETAIL) {
i++;
}
Interp_detail_level = i-1-tmp_detail_level;
if ( Interp_detail_level < 0 )
Interp_detail_level = 0;
else if (Interp_detail_level >= pm->n_detail_levels )
Interp_detail_level = pm->n_detail_levels-1;
} else {
Interp_detail_level = 0;
}
#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
// scale the render box settings based on the "Model Detail" slider
switch (Detail.detail_distance)
{
// 1st dot is 20%
case 0:
Interp_box_scale = 0.2f;
break;
// 2nd dot is 50%
case 1:
Interp_box_scale = 0.5f;
break;
// 3rd dot is 80%
case 2:
Interp_box_scale = 0.8f;
break;
// 4th dot is 100% (this is the default setting for "High" and "Very High" settings)
case 3:
Interp_box_scale = 1.0f;
break;
// 5th dot (max) is 120%
case 4:
Interp_box_scale = 1.2f;
break;
}
vec3d auto_back = ZERO_VECTOR;
if (Interp_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);
set_autocen = true;
}
// fake autocenter if we are a missile and don't already have autocen info
else if (Interp_flags & MR_IS_MISSILE) {
auto_back.xyz.x = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.x + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.x) / 2.0f );
auto_back.xyz.y = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.y + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.y) / 2.0f );
auto_back.xyz.z = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.z + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.z) / 2.0f );
set_autocen = true;
}
if (set_autocen)
g3_start_instance_matrix(&auto_back, NULL, true);
}
gr_zbias(1);
if(Interp_tmap_flags & TMAP_FLAG_PIXEL_FOG)
{
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);
gr_fog_set(GR_FOGMODE_FOG, r, g, b, fog_near, fog_far);
}
if (is_outlines_only_htl) {
gr_set_fill_mode( GR_FILL_MODE_WIRE );
gr_set_color_fast( &Interp_outline_color );
// lines shouldn't be rendered with textures or special RGB colors (assuming preset colors)
Interp_flags |= MR_NO_TEXTURING;
Interp_tmap_flags &= ~TMAP_FLAG_TEXTURED;
Interp_tmap_flags &= ~TMAP_FLAG_RGB;
// don't render with lighting either
Interp_flags |= MR_NO_LIGHTING;
} else {
gr_set_fill_mode( GR_FILL_MODE_SOLID );
}
if ( (Interp_flags & MR_NO_ZBUFFER) || (Interp_flags & MR_ALL_XPARENT) ) {
zbuf_mode = GR_ZBUFF_NONE;
} else {
zbuf_mode = GR_ZBUFF_FULL;
}
gr_zbuffer_set(zbuf_mode);
if (Interp_flags & MR_EDGE_ALPHA) {
gr_center_alpha(-1);
} else if (Interp_flags & MR_CENTER_ALPHA) {
gr_center_alpha(1);
} else {
gr_center_alpha(0);
}
if ( (Interp_flags & MR_NO_CULL) || (Interp_flags & MR_ALL_XPARENT) || (Interp_warp_bitmap >= 0) ) {
cull = gr_set_cull(0);
} else {
cull = gr_set_cull(1);
}
// Goober5000
Interp_base_frametime = 0;
if ( (objp != NULL) && (objp->type == OBJ_SHIP) ) {
Interp_base_frametime = Ships[objp->instance].base_texture_anim_frametime;
}
if ( !(Interp_flags & MR_NO_LIGHTING) ) {
gr_set_lighting(true, true);
}
// rotate lights
if ( !(Interp_flags & MR_NO_LIGHTING) ) {
light_rotate_all();
if ( !Cmdline_nohtl ) {
light_set_all_relevent();
}
}
if ( !(Interp_flags & MR_NO_LIGHTING) && (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) ) {
opengl_change_active_lights(0); // Set up OpenGl lighting;
}
if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
gr_set_buffer(pm->vertex_buffer_id);
}
// Draw the subobjects
i = pm->submodel[pm->detail[Interp_detail_level]].first_child;
while( i >= 0 ) {
if ( !pm->submodel[i].is_thruster ) {
// When in htl mode render with htl method unless its a jump node
if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
model_render_children_buffers( pm, i, Interp_detail_level );
} else {
model_interp_subcall( pm, i, Interp_detail_level );
}
} else {
draw_thrusters = true;
}
i = pm->submodel[i].next_sibling;
}
gr_zbias(0);
model_radius = pm->submodel[pm->detail[Interp_detail_level]].rad;
//*************************** draw the hull of the ship *********************************************
// When in htl mode render with htl method unless its a jump node
if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
transparent_submodel ts;
ts.is_submodel = false;
ts.pop_matrix = false;
transparent_submodels.push_back(ts);
model_render_buffers(pm, pm->detail[Interp_detail_level]);
} else {
model_interp_subcall(pm, pm->detail[Interp_detail_level], Interp_detail_level);
}
// Draw the thruster subobjects
if (draw_thrusters) {
i = pm->submodel[pm->detail[Interp_detail_level]].first_child;
while( i >= 0 ) {
if (pm->submodel[i].is_thruster) {
// When in htl mode render with htl method unless its a jump node
if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
transparent_submodel ts;
ts.is_submodel = false;
ts.pop_matrix = false;
transparent_submodels.push_back(ts);
model_render_children_buffers( pm, i, Interp_detail_level );
} else {
model_interp_subcall( pm, i, Interp_detail_level );
}
}
i = pm->submodel[i].next_sibling;
}
}
// Valathil - now draw the saved transparent objects
SCP_vector<transparent_submodel>::iterator ts;
SCP_vector<transparent_object>::iterator obj;
for(ts = transparent_submodels.begin(); ts != transparent_submodels.end(); ++ts)
{
if(ts->is_submodel)
g3_start_instance_matrix(&ts->model->offset, &ts->orient, true);
for(obj = ts->transparent_objects.begin(); obj != ts->transparent_objects.end(); ++obj)
{
GLOWMAP = obj->glow_map;
SPECMAP = obj->spec_map;
NORMMAP = obj->norm_map;
HEIGHTMAP = obj->height_map;
MISCMAP = obj->misc_map;
gr_push_scale_matrix(&obj->scale);
gr_set_bitmap(obj->texture, obj->blend_filter, GR_BITBLT_MODE_NORMAL, obj->alpha);
int zbuff = gr_zbuffer_set(GR_ZBUFF_READ);
gr_render_buffer(0, obj->buffer, obj->i, obj->tmap_flags);
gr_zbuffer_set(zbuff);
gr_pop_scale_matrix();
GLOWMAP = -1;
SPECMAP = -1;
NORMMAP = -1;
HEIGHTMAP = -1;
MISCMAP = -1;
}
ts->transparent_objects.clear();
if(ts->pop_matrix)
g3_done_instance(true);
}
transparent_submodels.clear();
if ( !Interp_no_flush ) {
gr_flush_data_states();
if (is_outlines_only_htl || (!Cmdline_nohtl && !is_outlines_only)) {
gr_set_buffer(-1);
}
}
if (is_outlines_only_htl) {
gr_set_fill_mode(GR_FILL_MODE_SOLID);
}
if ( !(Interp_flags & MR_NO_LIGHTING) ) {
gr_reset_lighting();
gr_set_lighting(false, false);
}
if (Interp_flags & MR_SHOW_PIVOTS ) {
model_draw_debug_points( pm, NULL );
model_draw_debug_points( pm, &pm->submodel[pm->detail[Interp_detail_level]] );
if(pm->flags & PM_FLAG_AUTOCEN){
gr_set_color(255, 255, 255);
g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f);
}
}
model_radius = 0.0f;
// render model insignias
gr_zbias(1);
if (!Cmdline_nohtl) gr_set_texture_panning(0.0, 0.0, false);
gr_zbuffer_set(GR_ZBUFF_READ);
model_render_insignias(pm, Interp_detail_level);
gr_zbias(0);
if (FULLCLOAK != -1) model_finish_cloak(FULLCLOAK);
if ( pm->submodel[pm->detail[0]].num_arcs ) {
interp_render_lightning( pm, &pm->submodel[pm->detail[0]]);
}
if ( Interp_flags & MR_SHOW_SHIELDS ) {
model_render_shields(pm);
}
if ( Interp_flags & MR_SHOW_PATHS ){
if (Cmdline_nohtl) model_draw_paths(model_num);
else model_draw_paths_htl(model_num);
}
if (Interp_flags & MR_BAY_PATHS ){
if (Cmdline_nohtl) model_draw_bay_paths(model_num);
else model_draw_bay_paths_htl(model_num);
}
if ( (Interp_flags & MR_AUTOCENTER) && (set_autocen) ) {
g3_done_instance(use_api);
}
g3_done_instance(use_api);
// 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 ( ( Interp_flags & MR_AUTOCENTER ) && set_autocen ) {
vec3d autoback_rotated;
vm_vec_unrotate(&autoback_rotated, &auto_back, orient);
vm_vec_add2(&autoback_rotated, pos);
model_render_thrusters( pm, objnum, shipp, orient, &autoback_rotated );
} else {
model_render_thrusters( pm, objnum, shipp, orient, pos );
}
}
gr_zbuffer_set(save_gr_zbuffering_mode);
gr_set_cull(cull);
}
void submodel_render(int model_num, int submodel_num, matrix *orient, vec3d * pos, uint flags, int objnum, int *replacement_textures)
{
// replacement textures - Goober5000
model_set_replacement_textures(replacement_textures);
polymodel * pm;
MONITOR_INC( NumModelsRend, 1 );
if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return;
// Turn off engine effect
Interp_thrust_scale_subobj=0;
if (!Model_texturing)
flags |= MR_NO_TEXTURING;
Interp_flags = flags;
Interp_pos=pos;
pm = model_get(model_num);
// Set the flags we will pass to the tmapper
Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB;
// if we're in nebula mode
if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){
Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG;
}
if ( !(Interp_flags & MR_NO_TEXTURING) ) {
Interp_tmap_flags |= TMAP_FLAG_TEXTURED;
if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling )
Interp_tmap_flags |= TMAP_FLAG_TILED;
if ( !(Interp_flags & MR_NO_CORRECT) ) {
Interp_tmap_flags |= TMAP_FLAG_CORRECT;
}
}
if ( Interp_flags & MR_ANIMATED_SHADER )
Interp_tmap_flags |= TMAP_ANIMATED_SHADER;
bool is_outlines_only_htl = !Cmdline_nohtl && (flags & MR_NO_POLYS) && (flags & MR_SHOW_OUTLINE_HTL);
//set to true since D3d and OGL need the api matrices set
g3_start_instance_matrix(pos, orient, true);
bool set_autocen = false;
vec3d auto_back = ZERO_VECTOR;
if (Interp_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);
set_autocen = true;
}
// fake autocenter if we are a missile and don't already have autocen info
else if (Interp_flags & MR_IS_MISSILE) {
auto_back.xyz.x = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.x + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.x) / 2.0f );
auto_back.xyz.y = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.y + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.y) / 2.0f );
auto_back.xyz.z = -( (pm->submodel[pm->detail[Interp_detail_level]].max.xyz.z + pm->submodel[pm->detail[Interp_detail_level]].min.xyz.z) / 2.0f );
set_autocen = true;
}
if (set_autocen)
g3_start_instance_matrix(&auto_back, NULL, true);
}
if (is_outlines_only_htl) {
gr_set_fill_mode( GR_FILL_MODE_WIRE );
// lines shouldn't be rendered with textures or special RGB colors (assuming preset colors)
Interp_flags |= MR_NO_TEXTURING;
Interp_tmap_flags &= ~TMAP_FLAG_TEXTURED;
Interp_tmap_flags &= ~TMAP_FLAG_RGB;
// don't render with lighting either
Interp_flags |= MR_NO_LIGHTING;
} else {
gr_set_fill_mode( GR_FILL_MODE_SOLID );
}
if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
Interp_light = 1.0f;
light_filter_push( -1, pos, pm->submodel[submodel_num].rad );
light_rotate_all();
if (!Cmdline_nohtl) {
light_set_all_relevent();
}
gr_set_lighting(true, true);
}
Interp_base_frametime = 0;
if (objnum >= 0) {
object *objp = &Objects[objnum];
if (objp->type == OBJ_SHIP) {
Interp_base_frametime = Ships[objp->instance].base_texture_anim_frametime;
}
}
// fixes disappearing HUD in OGL - taylor
int cull = gr_set_cull(1);
if (!Cmdline_nohtl) {
// RT - Put this here to fog debris
if(Interp_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);
gr_fog_set(GR_FOGMODE_FOG, r, g, b, fog_near, fog_far);
}
gr_set_buffer(pm->vertex_buffer_id);
transparent_submodel ts;
ts.is_submodel = false;
ts.pop_matrix = false;
transparent_submodels.push_back(ts);
model_render_buffers(pm, submodel_num);
SCP_vector<transparent_submodel>::iterator ts_i = transparent_submodels.begin();
SCP_vector<transparent_object>::iterator obj;
for(obj = ts_i->transparent_objects.begin(); obj != ts_i->transparent_objects.end(); ++obj)
{
GLOWMAP = obj->glow_map;
SPECMAP = obj->spec_map;
NORMMAP = obj->norm_map;
HEIGHTMAP = obj->height_map;
MISCMAP = obj->misc_map;
gr_push_scale_matrix(&obj->scale);
gr_set_bitmap(obj->texture, obj->blend_filter, GR_BITBLT_MODE_NORMAL, obj->alpha);
int zbuff = gr_zbuffer_set(GR_ZBUFF_READ);
gr_render_buffer(0, obj->buffer, obj->i, obj->tmap_flags);
gr_zbuffer_set(zbuff);
gr_pop_scale_matrix();
GLOWMAP = -1;
SPECMAP = -1;
NORMMAP = -1;
HEIGHTMAP = -1;
MISCMAP = -1;
}
ts_i->transparent_objects.clear();
transparent_submodels.clear();
gr_set_buffer(-1);
} else {
model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 );
}
if ( !(Interp_flags & MR_NO_LIGHTING) ) {
gr_set_lighting(false, false);
gr_reset_lighting();
}
gr_set_cull(cull);
gr_set_fill_mode(GR_FILL_MODE_SOLID);
if ( pm->submodel[submodel_num].num_arcs ) {
interp_render_lightning( pm, &pm->submodel[submodel_num]);
}
if (Interp_flags & MR_SHOW_PIVOTS )
model_draw_debug_points( pm, &pm->submodel[submodel_num] );
if ( !(Interp_flags & MR_NO_LIGHTING ) ) {
light_filter_pop();
}
if (set_autocen)
g3_done_instance(true);
g3_done_instance(true);
// turn off fog after each model renders, RT This fixes HUD being fogged when debris is in target box
if(The_mission.flags & MISSION_FLAG_FULLNEB){
gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0);
}
gr_flush_data_states();
}
// Fills in an array with points from a model.
// Only gets up to max_num verts;
// Returns number of verts found;
static int submodel_get_points_internal(int model_num, int submodel_num)
{
polymodel * pm;
pm = model_get(model_num);
if ( submodel_num < 0 ) {
submodel_num = pm->detail[0];
}
ubyte *p = pm->submodel[submodel_num].bsp_data;
int chunk_type, chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
while (chunk_type != OP_EOF) {
switch (chunk_type) {
case OP_DEFPOINTS: {
int n;
int nverts = w(p+8);
int offset = w(p+16);
int nnorms = 0;
ubyte * normcount = p+20;
vec3d *src = vp(p+offset);
for (n = 0; n < nverts; n++) {
nnorms += normcount[n];
}
model_allocate_interp_data(nverts, nnorms);
// this must happen only after the interp_data allocation call (since the address changes)
vec3d **verts = Interp_verts;
vec3d **norms = Interp_norms;
for (n=0; n<nverts; n++ ) {
*verts++ = src;
*norms++ = src + 1; // first normal associated with the point
src += normcount[n]+1;
}
return nverts; // Read in 'n' points
}
break;
case OP_FLATPOLY: break;
case OP_TMAPPOLY: break;
case OP_SORTNORM: break;
case OP_BOUNDBOX: break;
default:
mprintf(( "Bad chunk type %d, len=%d in submodel_get_points\n", chunk_type, chunk_size ));
Int3(); // Bad chunk type!
return 0;
}
p += chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
}
return 0; // Couldn't find 'em
}
/**
* Gets two random points on a model
*/
void submodel_get_two_random_points(int model_num, int submodel_num, vec3d *v1, vec3d *v2, vec3d *n1, vec3d *n2 )
{
int nv = submodel_get_points_internal(model_num, submodel_num);
// this is not only because of the immediate div-0 error but also because of the less immediate expectation for at least one point (preferably two) to be found
if (nv <= 0) {
polymodel *pm = model_get(model_num);
Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_points_internal!", model_num, (pm == NULL) ? "<null model?!?>" : pm->filename);
// in case people ignore the error...
vm_vec_zero(v1);
vm_vec_zero(v2);
if (n1 != NULL) {
vm_vec_zero(n1);
}
if (n2 != NULL) {
vm_vec_zero(n2);
}
return;
}
int vn1 = (myrand()>>5) % nv;
int vn2 = (myrand()>>5) % nv;
*v1 = *Interp_verts[vn1];
*v2 = *Interp_verts[vn2];
if(n1 != NULL){
*n1 = *Interp_norms[vn1];
}
if(n2 != NULL){
*n2 = *Interp_norms[vn2];
}
}
void submodel_get_two_random_points_better(int model_num, int submodel_num, vec3d *v1, vec3d *v2)
{
polymodel *pm = model_get(model_num);
if (pm != NULL) {
if ( submodel_num < 0 ) {
submodel_num = pm->detail[0];
}
bsp_collision_tree *tree = model_get_bsp_collision_tree(pm->submodel[submodel_num].collision_tree_index);
int nv = tree->n_verts;
// this is not only because of the immediate div-0 error but also because of the less immediate expectation for at least one point (preferably two) to be found
if (nv <= 0) {
Error(LOCATION, "Model %d ('%s') must have at least one point from submodel_get_points_internal!", model_num, (pm == NULL) ? "<null model?!?>" : pm->filename);
// in case people ignore the error...
vm_vec_zero(v1);
vm_vec_zero(v2);
return;
}
Assert(nv > 0); // Goober5000 - to avoid div-0 error
int vn1 = (myrand()>>5) % nv;
int vn2 = (myrand()>>5) % nv;
*v1 = tree->point_list[vn1];
*v2 = tree->point_list[vn2];
}
}
// If MR_FLAG_OUTLINE bit set this color will be used for outlines.
// This defaults to black.
void model_set_outline_color(int r, int g, int b )
{
gr_init_color( &Interp_outline_color, r, g, b );
}
// If MR_FLAG_OUTLINE bit set this color will be used for outlines.
// This defaults to black.
void model_set_outline_color_fast(void *outline_color)
{
Interp_outline_color = *((color*)(outline_color));
}
// IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n'
// This defaults to 0. (0=highest, larger=lower)
void model_set_detail_level(int n)
{
Interp_detail_level_locked = n;
}
/**
* Returns number of verts in a submodel;
*/
int submodel_get_num_verts(int model_num, int submodel_num )
{
polymodel * pm;
pm = model_get(model_num);
ubyte *p = pm->submodel[submodel_num].bsp_data;
int chunk_type, chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
while (chunk_type != OP_EOF) {
switch (chunk_type) {
case OP_DEFPOINTS: {
int n=w(p+8);
return n; // Read in 'n' points
}
break;
case OP_FLATPOLY: break;
case OP_TMAPPOLY: break;
case OP_SORTNORM: break;
case OP_BOUNDBOX: break;
default:
mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size ));
Int3(); // Bad chunk type!
return 0;
}
p += chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
}
return 0; // Couldn't find 'em
}
/**
* Returns number of tmaps & flat polys in a submodel;
*/
int submodel_get_num_polys_sub( ubyte *p )
{
int chunk_type = w(p);
int chunk_size = w(p+4);
int n = 0;
while (chunk_type != OP_EOF) {
switch (chunk_type) {
case OP_DEFPOINTS: break;
case OP_FLATPOLY: n++; break;
case OP_TMAPPOLY: n++; break;
case OP_SORTNORM: {
int frontlist = w(p+36);
int backlist = w(p+40);
int prelist = w(p+44);
int postlist = w(p+48);
int onlist = w(p+52);
n += submodel_get_num_polys_sub(p+frontlist);
n += submodel_get_num_polys_sub(p+backlist);
n += submodel_get_num_polys_sub(p+prelist);
n += submodel_get_num_polys_sub(p+postlist );
n += submodel_get_num_polys_sub(p+onlist );
}
break;
case OP_BOUNDBOX: break;
default:
mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size ));
Int3(); // Bad chunk type!
return 0;
}
p += chunk_size;
chunk_type = w(p);
chunk_size = w(p+4);
}
return n;
}
/**
* Returns number of tmaps & flat polys in a submodel
*/
int submodel_get_num_polys(int model_num, int submodel_num )
{
polymodel * pm;
pm = model_get(model_num);
return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data );
}
// Sets the submodel instance data in a submodel
// If show_damaged is true it shows only damaged submodels.
// If it is false it shows only undamaged submodels.
void model_show_damaged(int model_num, int show_damaged )
{
polymodel * pm;
int i;
pm = model_get(model_num);
for (i=0; i<pm->n_models; i++ ) {
bsp_info *sm = &pm->submodel[i];
// Set the "blown out" flags
sm->blown_off = 0;
}
for (i=0; i<pm->n_models; i++ ) {
bsp_info *sm = &pm->submodel[i];
// Set the "blown out" flags
if ( show_damaged ) {
if ( sm->my_replacement > -1 ) {
pm->submodel[sm->my_replacement].blown_off = 0;
sm->blown_off = 1;
}
} else {
if ( sm->my_replacement > -1 ) {
pm->submodel[sm->my_replacement].blown_off = 1;
sm->blown_off = 0;
}
}
}
}
/**
* Set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether)
*/
void model_set_insignia_bitmap(int bmap)
{
Interp_insignia_bitmap = bmap;
}
void model_set_replacement_textures(int *replacement_textures)
{
Interp_new_replacement_textures = replacement_textures;
}
/**
* Set the forces bitmap
*/
void model_set_forced_texture(int bmap)
{
Interp_forced_bitmap = bmap;
}
/**
* Set model transparency for use with MR_ALL_XPARENT
*/
void model_set_alpha(float alpha)
{
Interp_xparent_alpha = alpha;
}
/**
* See if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error
*/
int model_find_texture(int model_num, int bitmap)
{
polymodel * pm;
int idx;
// get a handle to the model
pm = model_get(model_num);
if(pm == NULL){
return -1;
}
// find the texture
for(idx=0; idx<pm->n_textures; idx++)
{
if(pm->maps[idx].FindTexture(bitmap) > -1)
{
return 1;
}
}
// no texture
return 0;
}
// find closest point on extended bounding box (the bounding box plus all the planes that make it up)
// returns closest distance to extended box
// positive return value means start_point is outside extended box
// displaces closest point an optional amount delta to the outside of the box
// closest_box_point can be NULL.
float get_model_closest_box_point_with_delta(vec3d *closest_box_point, vec3d *start_point, int modelnum, int *is_inside, float delta)
{
int i, idx;
vec3d box_point, ray_direction, *extremes;
float dist, best_dist;
polymodel *pm;
int inside = 0;
int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
int mask_inside = 0x3f;
best_dist = FLT_MAX;
pm = model_get(modelnum);
for (i=0; i<6; i++) {
idx = i / 2; // which row vector of Identity matrix
memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vec3d));
// do negative, then positive plane for each axis
if (2 * idx == i) {
extremes = &pm->mins;
vm_vec_negate(&ray_direction);
} else {
extremes = &pm->maxs;
}
// a negative distance means started outside the box
dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f);
if (dist > 0) {
inside |= masks[i];
}
if (fabs(dist) < fabs(best_dist)) {
best_dist = dist;
if (closest_box_point) {
vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta);
}
}
}
// is start_point inside the box
if (is_inside) {
*is_inside = (inside == mask_inside);
}
return -best_dist;
}
// find closest point on extended bounding box (the bounding box plus all the planes that make it up)
// returns closest distance to extended box
// positive return value means start_point is outside extended box
// displaces closest point an optional amount delta to the outside of the box
// closest_box_point can be NULL.
float get_world_closest_box_point_with_delta(vec3d *closest_box_point, object *box_obj, vec3d *start_point, int *is_inside, float delta)
{
vec3d temp, box_start;
float dist;
int modelnum;
// get modelnum
modelnum = Ship_info[Ships[box_obj->instance].ship_info_index].model_num;
// rotate start_point to box_obj RF
vm_vec_sub(&temp, start_point, &box_obj->pos);
vm_vec_rotate(&box_start, &temp, &box_obj->orient);
dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta);
// rotate closest_box_point to world RF
if (closest_box_point) {
vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient);
vm_vec_add(closest_box_point, &temp, &box_obj->pos);
}
return dist;
}
void model_set_fog_level(float l)
{
Interp_fog_level = l;
}
/**
* Given a newly loaded model, page in all textures
*/
void model_page_in_textures(int modelnum, int ship_info_index)
{
int i, idx;
polymodel *pm = model_get(modelnum);
// bogus
if (pm == NULL)
return;
for (idx = 0; idx < pm->n_textures; idx++) {
pm->maps[idx].PageIn();
}
for (i = 0; i < pm->n_glow_point_banks; i++) {
glow_point_bank *bank = &pm->glow_point_banks[i];
bm_page_in_texture(bank->glow_bitmap);
bm_page_in_texture(bank->glow_neb_bitmap);
}
if (ship_info_index >= 0)
ship_page_in_textures(ship_info_index);
}
// unload all textures for a given model
// "release" should only be set if called from model_unload()!!!
void model_page_out_textures(int model_num, bool release)
{
int i, j;
if (model_num < 0)
return;
polymodel *pm = model_get(model_num);
if (pm == NULL)
return;
if (release && (pm->used_this_mission > 0))
return;
for (i = 0; i < pm->n_textures; i++) {
pm->maps[i].PageOut(release);
}
// NOTE: "release" doesn't work here for some, as of yet unknown, reason - taylor
for (j = 0; j < pm->n_glow_point_banks; j++) {
glow_point_bank *bank = &pm->glow_point_banks[j];
if (bank->glow_bitmap >= 0) {
// if (release) {
// bm_release(bank->glow_bitmap);
// bank->glow_bitmap = -1;
// } else {
bm_unload(bank->glow_bitmap);
// }
}
if (bank->glow_neb_bitmap >= 0) {
// if (release) {
// bm_release(bank->glow_neb_bitmap);
// bank->glow_neb_bitmap = -1;
// } else {
bm_unload(bank->glow_neb_bitmap);
// }
}
}
}
//**********vertex buffer stuff**********//
int tri_count[MAX_MODEL_TEXTURES];
poly_list polygon_list[MAX_MODEL_TEXTURES];
void parse_defpoint(int off, ubyte *bsp_data)
{
int i, n;
int nverts = w(off+bsp_data+8);
int offset = w(off+bsp_data+16);
int next_norm = 0;
ubyte *normcount = off+bsp_data+20;
vec3d *src = vp(off+bsp_data+offset);
// Get pointer to lights
Interp_lights = off+bsp_data+20+nverts;
#ifndef NDEBUG
modelstats_num_verts += nverts;
#endif
for (n = 0; n < nverts; n++) {
Interp_verts[n] = src;
src++; // move to normal
for (i = 0; i < normcount[n]; i++) {
Interp_norms[next_norm] = src;
next_norm++;
src++;
}
}
}
int check_values(vec3d *N)
{
// Values equal to -1.#IND0
if(!is_valid_vec(N))
{
N->xyz.x = 1.0f;
N->xyz.y = 0.0f;
N->xyz.z = 0.0f;
return 1;
}
return 0;
}
int Parse_normal_problem_count = 0;
void parse_tmap(int offset, ubyte *bsp_data)
{
int pof_tex = w(bsp_data+offset+40);
int n_vert = w(bsp_data+offset+36);
ubyte *p = &bsp_data[offset+8];
model_tmap_vert *tverts;
tverts = (model_tmap_vert *)&bsp_data[offset+44];
vertex *V;
vec3d *v;
vec3d *N;
int problem_count = 0;
for (int i = 1; i < (n_vert-1); i++) {
V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)];
N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)];
v = Interp_verts[(int)tverts[0].vertnum];
V->world.xyz.x = v->xyz.x;
V->world.xyz.y = v->xyz.y;
V->world.xyz.z = v->xyz.z;
V->texture_position.u = tverts[0].u;
V->texture_position.v = tverts[0].v;
*N = *Interp_norms[(int)tverts[0].normnum];
if ( IS_VEC_NULL(N) )
*N = *vp(p);
problem_count += check_values(N);
vm_vec_normalize_safe(N);
V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)+1];
N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)+1];
v = Interp_verts[(int)tverts[i].vertnum];
V->world.xyz.x = v->xyz.x;
V->world.xyz.y = v->xyz.y;
V->world.xyz.z = v->xyz.z;
V->texture_position.u = tverts[i].u;
V->texture_position.v = tverts[i].v;
*N = *Interp_norms[(int)tverts[i].normnum];
if ( IS_VEC_NULL(N) )
*N = *vp(p);
problem_count += check_values(N);
vm_vec_normalize_safe(N);
V = &polygon_list[pof_tex].vert[(polygon_list[pof_tex].n_verts)+2];
N = &polygon_list[pof_tex].norm[(polygon_list[pof_tex].n_verts)+2];
v = Interp_verts[(int)tverts[i+1].vertnum];
V->world.xyz.x = v->xyz.x;
V->world.xyz.y = v->xyz.y;
V->world.xyz.z = v->xyz.z;
V->texture_position.u = tverts[i+1].u;
V->texture_position.v = tverts[i+1].v;
*N = *Interp_norms[(int)tverts[i+1].normnum];
if ( IS_VEC_NULL(N) )
*N = *vp(p);
problem_count += check_values(N);
vm_vec_normalize_safe(N);
polygon_list[pof_tex].n_verts += 3;
}
Parse_normal_problem_count += problem_count;
}
void parse_sortnorm(int offset, ubyte *bsp_data);
void parse_bsp(int offset, ubyte *bsp_data)
{
int id = w(bsp_data+offset);
int size = w(bsp_data+offset+4);
while (id != 0) {
switch (id)
{
case OP_DEFPOINTS:
parse_defpoint(offset, bsp_data);
break;
case OP_SORTNORM:
parse_sortnorm(offset, bsp_data);
break;
case OP_FLATPOLY:
break;
case OP_TMAPPOLY:
parse_tmap(offset, bsp_data);
break;
case OP_BOUNDBOX:
break;
default:
return;
}
offset += size;
id = w(bsp_data+offset);
size = w(bsp_data+offset+4);
if (size < 1)
id = OP_EOF;
}
}
void parse_sortnorm(int offset, ubyte *bsp_data)
{
int frontlist, backlist, prelist, postlist, onlist;
frontlist = w(bsp_data+offset+36);
backlist = w(bsp_data+offset+40);
prelist = w(bsp_data+offset+44);
postlist = w(bsp_data+offset+48);
onlist = w(bsp_data+offset+52);
if (prelist) parse_bsp(offset+prelist,bsp_data);
if (backlist) parse_bsp(offset+backlist, bsp_data);
if (onlist) parse_bsp(offset+onlist, bsp_data);
if (frontlist) parse_bsp(offset+frontlist, bsp_data);
if (postlist) parse_bsp(offset+postlist, bsp_data);
}
void find_tmap(int offset, ubyte *bsp_data)
{
int pof_tex = w(bsp_data+offset+40);
int n_vert = w(bsp_data+offset+36);
tri_count[pof_tex] += n_vert-2;
}
void find_defpoint(int off, ubyte *bsp_data)
{
int n;
int nverts = w(off+bsp_data+8);
ubyte * normcount = off+bsp_data+20;
// Get pointer to lights
Interp_lights = off+bsp_data+20+nverts;
#ifndef NDEBUG
modelstats_num_verts += nverts;
#endif
int norm_num = 0;
for (n = 0; n < nverts; n++) {
norm_num += normcount[n];
}
Interp_num_verts = nverts;
Interp_num_norms = norm_num;
}
void find_sortnorm(int offset, ubyte *bsp_data);
// tri_count
void find_tri_counts(int offset, ubyte *bsp_data)
{
int id = w(bsp_data+offset);
int size = w(bsp_data+offset+4);
while (id != 0) {
switch (id)
{
case OP_DEFPOINTS:
find_defpoint(offset, bsp_data);
break;
case OP_SORTNORM:
find_sortnorm(offset, bsp_data);
break;
case OP_FLATPOLY:
break;
case OP_TMAPPOLY:
find_tmap(offset, bsp_data);
break;
case OP_BOUNDBOX:
break;
default:
return;
}
offset += size;
id = w(bsp_data+offset);
size = w(bsp_data+offset+4);
if (size < 1)
id = OP_EOF;
}
}
void find_sortnorm(int offset, ubyte *bsp_data)
{
int frontlist, backlist, prelist, postlist, onlist;
frontlist = w(bsp_data+offset+36);
backlist = w(bsp_data+offset+40);
prelist = w(bsp_data+offset+44);
postlist = w(bsp_data+offset+48);
onlist = w(bsp_data+offset+52);
if (prelist) find_tri_counts(offset+prelist,bsp_data);
if (backlist) find_tri_counts(offset+backlist, bsp_data);
if (onlist) find_tri_counts(offset+onlist, bsp_data);
if (frontlist) find_tri_counts(offset+frontlist, bsp_data);
if (postlist) find_tri_counts(offset+postlist, bsp_data);
}
static void allocate_poly_list()
{
for (int i = 0; i < MAX_MODEL_TEXTURES; i++) {
polygon_list[i].allocate(tri_count[i]*3);
}
model_allocate_interp_data(Interp_num_verts, Interp_num_norms);
}
void interp_pack_vertex_buffers(polymodel *pm, int mn)
{
Assert( pm->vertex_buffer_id >= 0 );
Assert( (mn >= 0) && (mn < pm->n_models) );
bsp_info *model = &pm->submodel[mn];
if ( !model->buffer.model_list ) {
return;
}
bool rval = gr_pack_buffer(pm->vertex_buffer_id, &model->buffer);
if ( !rval ) {
Error( LOCATION, "Unable to pack vertex buffer for '%s'\n", pm->filename );
}
}
void interp_configure_vertex_buffers(polymodel *pm, int mn)
{
int i, j, first_index;
uint total_verts = 0;
SCP_vector<int> vertex_list;
Assert( (mn >= 0) && (mn < pm->n_models) );
bsp_info *model = &pm->submodel[mn];
for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
polygon_list[i].n_verts = 0;
tri_count[i] = 0;
}
find_tri_counts(0, model->bsp_data);
for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
total_verts += tri_count[i];
// for the moment we can only support INT_MAX worth of verts per index buffer
if (total_verts > INT_MAX) {
Error( LOCATION, "Unable to generate vertex buffer data because model '%s' with %i verts is over the maximum of %i verts!\n", pm->filename, total_verts, INT_MAX);
}
}
if (total_verts < 1) {
return;
}
allocate_poly_list();
parse_bsp(0, model->bsp_data);
total_verts = 0;
for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
total_verts += polygon_list[i].n_verts;
}
poly_list *model_list = new(std::nothrow) poly_list;
if ( !model_list ) {
Error( LOCATION, "Unable to allocate memory for poly_list!\n" );
}
model->buffer.model_list = model_list;
model_list->allocate( (int)total_verts );
for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
if ( !polygon_list[i].n_verts )
continue;
memcpy( (model_list->vert) + model_list->n_verts, polygon_list[i].vert, sizeof(vertex) * polygon_list[i].n_verts );
memcpy( (model_list->norm) + model_list->n_verts, polygon_list[i].norm, sizeof(vec3d) * polygon_list[i].n_verts );
if (Cmdline_normal) {
memcpy( (model_list->tsb) + model_list->n_verts, polygon_list[i].tsb, sizeof(tsb_t) * polygon_list[i].n_verts );
}
model_list->n_verts += polygon_list[i].n_verts;
}
// IBX stuff
extern IBX ibuffer_info;
if (ibuffer_info.read != NULL) {
int ibx_verts = 0;
int ibx_size = 0;
ibx_verts = cfread_int( ibuffer_info.read );
ibuffer_info.size -= sizeof(int); // subtract
// vertex count (indexed vertex count)
ibx_size += ibx_verts * sizeof(int);
// index count (original vertex count)
ibx_size += model_list->n_verts * sizeof(int);
// safety check for this section
// ibuffer_info.size should be greater than or equal to ibx_size at this point
if (ibx_size > ibuffer_info.size) {
// AAAAAHH! not enough stored data - Abort, Retry, Fail?
Warning(LOCATION, "IBX: Safety Check Failure! The file doesn't contain enough data, deleting '%s'\n", ibuffer_info.name);
cfclose( ibuffer_info.read );
ibuffer_info.read = NULL;
ibuffer_info.size = 0;
cf_delete( ibuffer_info.name, CF_TYPE_CACHE );
// force generate
model_list->make_index_buffer(vertex_list);
vertex_list.clear(); // don't actually need this now
} else {
poly_list *tlist = new(std::nothrow) poly_list;
if ( !tlist ) {
Error( LOCATION, "Unable to allocate memory for IBX poly_list!\n" );
}
tlist->allocate( ibx_verts );
// we have to generate tangent data manually for model_list
// (since it's otherwise done during make_index_buffer())
model_list->calculate_tangent();
for (i = 0; i < ibx_verts; i++) {
int ivert = cfread_int( ibuffer_info.read );
tlist->vert[i] = model_list->vert[ivert];
tlist->norm[i] = model_list->norm[ivert];
if (Cmdline_normal) {
tlist->tsb[i] = model_list->tsb[ivert];
}
}
tlist->n_verts = ibx_verts;
// change from old model_list to new one
delete model_list;
model->buffer.model_list = tlist;
model_list = tlist;
// subtract this block of data from the total size for next check
// remember that this includes the next set of reads too
ibuffer_info.size -= ibx_size;
}
} else {
// no read file so we'll have to generate
model_list->make_index_buffer(vertex_list);
if (ibuffer_info.write != NULL) {
cfwrite_int( model_list->n_verts, ibuffer_info.write );
int count = (int)vertex_list.size();
Assert( model_list->n_verts == count );
for (i = 0; i < count; i++) {
cfwrite_int( vertex_list[i], ibuffer_info.write );
}
}
vertex_list.clear(); // done
}
// end IBX stuff
int vertex_flags = (VB_FLAG_POSITION | VB_FLAG_NORMAL | VB_FLAG_UV1);
if (model_list->tsb != NULL) {
Assert( Cmdline_normal );
vertex_flags |= VB_FLAG_TANGENT;
}
model->buffer.flags = vertex_flags;
for (i = 0; i < MAX_MODEL_TEXTURES; i++) {
if ( !polygon_list[i].n_verts )
continue;
buffer_data new_buffer(polygon_list[i].n_verts);
Verify( new_buffer.get_index() != NULL );
for (j = 0; j < polygon_list[i].n_verts; j++) {
if (ibuffer_info.read != NULL) {
first_index = cfread_int(ibuffer_info.read);
Assert( first_index >= 0 );
new_buffer.assign(j, first_index);
} else {
first_index = model_list->find_index(&polygon_list[i], j);
Assert(first_index != -1);
new_buffer.assign(j, first_index);
if (ibuffer_info.write != NULL) {
cfwrite_int(first_index, ibuffer_info.write);
}
}
}
new_buffer.texture = i;
new_buffer.flags = 0;
if (polygon_list[i].n_verts >= USHRT_MAX) {
new_buffer.flags |= VB_FLAG_LARGE_INDEX;
}
model->buffer.tex_buf.push_back( new_buffer );
}
bool rval = gr_config_buffer(pm->vertex_buffer_id, &model->buffer);
if ( !rval ) {
Error( LOCATION, "Unable to configure vertex buffer for '%s'\n", pm->filename );
}
}
inline int in_box(vec3d *min, vec3d *max, vec3d *pos)
{
vec3d point;
vm_vec_sub(&point, &View_position, pos);
if ( (point.xyz.x >= min->xyz.x) && (point.xyz.x <= max->xyz.x)
&& (point.xyz.y >= min->xyz.y) && (point.xyz.y <= max->xyz.y)
&& (point.xyz.z >= min->xyz.z) && (point.xyz.z <= max->xyz.z) )
{
return 1;
}
return -1;
}
inline int in_sphere(vec3d *pos, float radius)
{
if ( vm_vec_dist(&View_position, pos) <= radius )
return 1;
else
return -1;
}
void model_render_children_buffers(polymodel *pm, int mn, int detail_level)
{
int i;
if ( (mn < 0) || (mn >= pm->n_models) ) {
Int3();
return;
}
bsp_info *model = &pm->submodel[mn];
if (model->blown_off)
return;
uint fl = Interp_flags;
if (model->is_thruster) {
if ( !(Interp_flags & MR_SHOW_THRUSTERS) )
return;
Interp_flags |= MR_NO_LIGHTING;
Interp_thrust_scale_subobj = 1;
gr_set_lighting(false, false);
} else {
Interp_thrust_scale_subobj = 0;
}
// if using detail boxes or spheres, check that we are valid for the range
if ( !(Interp_flags & MR_FULL_DETAIL) && model->use_render_box ) {
vm_vec_copy_scale(&Interp_render_box_min, &model->render_box_min, Interp_box_scale);
vm_vec_copy_scale(&Interp_render_box_max, &model->render_box_max, Interp_box_scale);
if ( (-model->use_render_box + in_box(&Interp_render_box_min, &Interp_render_box_max, &model->offset)) )
return;
}
if ( !(Interp_flags & MR_FULL_DETAIL) && model->use_render_sphere ) {
Interp_render_sphere_radius = model->render_sphere_radius * Interp_box_scale;
// TODO: doesn't consider submodel rotations yet -zookeeper
vec3d offset;
model_find_submodel_offset(&offset, pm->id, mn);
vm_vec_add2(&offset, &model->render_sphere_offset);
if ( (-model->use_render_sphere + in_sphere(&offset, Interp_render_sphere_radius)) )
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_matrix(&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);
transparent_submodel ts;
ts.is_submodel = true;
ts.pop_matrix = true;
bool childs = false;
i = model->first_child;
while (i >= 0) {
if ( !pm->submodel[i].is_thruster ) {
childs = true;
ts.pop_matrix = false;
break;
}
i = pm->submodel[i].next_sibling;
}
ts.model = model;
memcpy(&ts.orient,&submodel_matrix,sizeof(matrix));
transparent_submodels.push_back(ts);
model_render_buffers(pm, mn, true);
if (Interp_flags & MR_SHOW_PIVOTS)
model_draw_debug_points( pm, &pm->submodel[mn] );
if (model->num_arcs)
interp_render_lightning( pm, &pm->submodel[mn]);
i = model->first_child;
while (i >= 0) {
if ( !pm->submodel[i].is_thruster ) {
model_render_children_buffers( pm, i, detail_level );
}
i = pm->submodel[i].next_sibling;
}
if(childs)
{
ts.is_submodel = false;
ts.pop_matrix = true;
ts.model = 0;
ts.transparent_objects.clear();
transparent_submodels.push_back(ts);
}
Interp_flags = fl;
if (Interp_thrust_scale_subobj) {
Interp_thrust_scale_subobj = 0;
if ( !(Interp_flags & MR_NO_LIGHTING) ) {
gr_set_lighting(true, true);
}
}
g3_done_instance(true);
}
void model_render_buffers(polymodel *pm, int mn, bool is_child)
{
if (pm->vertex_buffer_id < 0)
return;
if ( (mn < 0) || (mn >= pm->n_models) ) {
Int3();
return;
}
bsp_info *model = &pm->submodel[mn];
// if using detail boxes or spheres, check that we are valid for the range
if ( !is_child && !(Interp_flags & MR_FULL_DETAIL) && model->use_render_box ) {
vm_vec_copy_scale(&Interp_render_box_min, &model->render_box_min, Interp_box_scale);
vm_vec_copy_scale(&Interp_render_box_max, &model->render_box_max, Interp_box_scale);
if ( (-model->use_render_box + in_box(&Interp_render_box_min, &Interp_render_box_max, &model->offset)) )
return;
}
if ( !is_child && !(Interp_flags & MR_FULL_DETAIL) && model->use_render_sphere ) {
Interp_render_sphere_radius = model->render_sphere_radius * Interp_box_scale;
// TODO: doesn't consider submodel rotations yet -zookeeper
vec3d offset;
model_find_submodel_offset(&offset, pm->id, mn);
vm_vec_add2(&offset, &model->render_sphere_offset);
if ( (-model->use_render_sphere + in_sphere(&offset, Interp_render_sphere_radius)) )
return;
}
vec3d scale;
if (Interp_thrust_scale_subobj) {
scale.xyz.x = 1.0f;
scale.xyz.y = 1.0f;
scale.xyz.z = Interp_thrust_scale;
} else {
scale.xyz.x = Interp_warp_scale_x;
scale.xyz.y = Interp_warp_scale_y;
scale.xyz.z = Interp_warp_scale_z;
}
texture_info tex_replace[TM_NUM_TYPES];
int no_texturing = (Interp_flags & MR_NO_TEXTURING);
int forced_texture = -2;
float forced_alpha = 1.0f;
int forced_blend_filter = GR_ALPHABLEND_NONE;
if ( (Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0) ) {
forced_texture = Interp_forced_bitmap;
} else if (Interp_warp_bitmap >= 0) {
forced_texture = Interp_warp_bitmap;
forced_alpha = Interp_warp_alpha;
forced_blend_filter = GR_ALPHABLEND_FILTER;
} else if (Interp_thrust_scale_subobj) {
if ( (Interp_thrust_bitmap >= 0) && (Interp_thrust_scale > 0.0f) ) {
forced_texture = Interp_thrust_bitmap;
} else {
forced_texture = -1;
}
forced_alpha = 1.2f;
forced_blend_filter = GR_ALPHABLEND_FILTER;
} else if (Interp_flags & MR_ALL_XPARENT) {
forced_alpha = Interp_xparent_alpha;
forced_blend_filter = GR_ALPHABLEND_FILTER;
}
if (!Interp_thrust_scale_subobj) {
gr_push_scale_matrix(&scale);
}
size_t buffer_size = model->buffer.tex_buf.size();
for (size_t i = 0; i < buffer_size; i++) {
int texture = -1;
int tmap_num = model->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;
if (forced_texture != -2) {
texture = forced_texture;
alpha = forced_alpha;
}
else if ( !no_texturing ) {
// pick the texture, animating it if necessary
if ( (Interp_new_replacement_textures != NULL) && (Interp_new_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 = -1;
} else if ( (Interp_new_replacement_textures != NULL) && (Interp_new_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(Interp_new_replacement_textures[rt_begin_index + TM_BASE_TYPE]);
texture = model_interp_get_texture(&tex_replace[TM_BASE_TYPE], Interp_base_frametime);
} else {
// we just use the underlying texture
texture = model_interp_get_texture(&tmap->textures[TM_BASE_TYPE], Interp_base_frametime);
}
if (texture < 0) {
continue;
}
// doing glow maps?
if ( !(Interp_flags & MR_NO_GLOWMAPS) ) {
texture_info *tglow = &tmap->textures[TM_GLOW_TYPE];
if ( (Interp_new_replacement_textures != NULL) && (Interp_new_replacement_textures[rt_begin_index + TM_GLOW_TYPE] >= 0) ) {
tex_replace[TM_GLOW_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_GLOW_TYPE]);
GLOWMAP = model_interp_get_texture(&tex_replace[TM_GLOW_TYPE], Interp_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 ( (Interp_objnum >= 0) && (Objects[Interp_objnum].type == OBJ_SHOCKWAVE) && (tglow->GetNumFrames() > 1) ) {
GLOWMAP = tglow->GetTexture() + shockwave_get_framenum(Objects[Interp_objnum].instance, tglow->GetNumFrames());
} else {
GLOWMAP = model_interp_get_texture(tglow, Interp_base_frametime);
}
}
}
if ( (Detail.lighting > 2) && (Interp_detail_level < 2) ) {
// likewise, etc.
texture_info *spec_map = &tmap->textures[TM_SPECULAR_TYPE];
texture_info *norm_map = &tmap->textures[TM_NORMAL_TYPE];
texture_info *height_map = &tmap->textures[TM_HEIGHT_TYPE];
if (Interp_new_replacement_textures != NULL) {
if (Interp_new_replacement_textures[rt_begin_index + TM_SPECULAR_TYPE] >= 0) {
tex_replace[TM_SPECULAR_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_SPECULAR_TYPE]);
spec_map = &tex_replace[TM_SPECULAR_TYPE];
}
if (Interp_new_replacement_textures[rt_begin_index + TM_NORMAL_TYPE] >= 0) {
tex_replace[TM_NORMAL_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_NORMAL_TYPE]);
norm_map = &tex_replace[TM_NORMAL_TYPE];
}
if (Interp_new_replacement_textures[rt_begin_index + TM_HEIGHT_TYPE] >= 0) {
tex_replace[TM_HEIGHT_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_HEIGHT_TYPE]);
height_map = &tex_replace[TM_HEIGHT_TYPE];
}
}
SPECMAP = model_interp_get_texture(spec_map, Interp_base_frametime);
NORMMAP = model_interp_get_texture(norm_map, Interp_base_frametime);
HEIGHTMAP = model_interp_get_texture(height_map, Interp_base_frametime);
}
texture_info *misc_map = &tmap->textures[TM_MISC_TYPE];
if (Interp_new_replacement_textures != NULL) {
if (Interp_new_replacement_textures[rt_begin_index + TM_MISC_TYPE] >= 0) {
tex_replace[TM_MISC_TYPE] = texture_info(Interp_new_replacement_textures[rt_begin_index + TM_MISC_TYPE]);
misc_map = &tex_replace[TM_MISC_TYPE];
}
}
MISCMAP = model_interp_get_texture(misc_map, Interp_base_frametime);
} else {
alpha = forced_alpha;
}
if ( (texture == -1) && !no_texturing ) {
continue;
}
// trying to get transperent textures-Bobboau
if (tmap->is_transparent) {
// for special shockwave/warpmap usage
alpha = (Interp_warp_alpha != -1.0f) ? Interp_warp_alpha : 0.8f;
blend_filter = GR_ALPHABLEND_FILTER;
//Valathil - Save the object for later rendering
transparent_object tobj;
tobj.alpha=alpha;
tobj.blend_filter = blend_filter;
tobj.buffer = &model->buffer;
tobj.glow_map = GLOWMAP;
tobj.height_map = HEIGHTMAP;
tobj.misc_map = MISCMAP;
tobj.i = i;
tobj.norm_map = NORMMAP;
tobj.spec_map = SPECMAP;
tobj.texture = texture;
tobj.tmap_flags = Interp_tmap_flags;
memcpy(&tobj.scale,&scale,sizeof(vec3d));
transparent_submodels.back().transparent_objects.push_back(tobj);
}
else
{
if (forced_blend_filter != GR_ALPHABLEND_NONE) {
blend_filter = forced_blend_filter;
}
gr_set_bitmap(texture, blend_filter, GR_BITBLT_MODE_NORMAL, alpha);
gr_render_buffer(0, &model->buffer, i, Interp_tmap_flags);
}
GLOWMAP = -1;
SPECMAP = -1;
NORMMAP = -1;
HEIGHTMAP = -1;
MISCMAP = -1;
}
gr_pop_scale_matrix();
}
// returns 1 if the thruster should be drawn
// 0 if it shouldn't
int model_should_render_engine_glow(int objnum, int bank_obj)
{
if ((bank_obj <= -1) || (objnum <= -1))
return 1;
object *obj = &Objects[objnum];
if (obj->type == OBJ_SHIP) {
ship_subsys *ssp;
ship *shipp = &Ships[obj->instance];
ship_info *sip = &Ship_info[shipp->ship_info_index];
Assert( bank_obj < sip->n_subsystems );
char subname[MAX_NAME_LEN];
// shipp->subsystems isn't always valid here so don't use it
strcpy_s(subname, sip->subsystems[bank_obj].subobj_name);
ssp = GET_FIRST(&shipp->subsys_list);
while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
if ( !strcmp(subname, ssp->system_info->subobj_name) ) {
// this subsystem has 0 or less hits, ie. it's destroyed
if ( ssp->current_hits <= 0 )
return 0;
// see if the subsystem is disrupted, in which case it should be inoperable
if ( ship_subsys_disrupted(ssp) )
return 0;
return 1;
}
ssp = GET_NEXT( ssp );
}
} else if (obj->type == OBJ_WEAPON) {
// for weapons, if needed in the future
}
// default to render glow
return 1;
}
// Goober5000
// uses same algorithms as in ship_do_thruster_frame
int model_interp_get_texture(texture_info *tinfo, fix base_frametime)
{
int texture, frame, num_frames;
float cur_time, total_time;
// get texture
num_frames = tinfo->GetNumFrames();
texture = tinfo->GetTexture();
total_time = tinfo->GetTotalTime();
// maybe animate it
if (texture >= 0 && num_frames > 1)
{
// sanity check total_time first thing
Assert(total_time > 0.0f);
cur_time = f2fl((game_get_overall_frametime() - base_frametime) % fl2f(total_time));
// get animation frame
frame = fl2i((cur_time * num_frames) / total_time);
if (frame < 0) frame = 0;
if (frame >= num_frames) frame = num_frames - 1;
// advance to the correct frame
texture += frame;
}
// done
return texture;
}
void model_set_warp_globals(float scale_x, float scale_y, float scale_z, int bitmap_id, float alpha)
{
Interp_warp_scale_x = scale_x;
Interp_warp_scale_y = scale_y;
Interp_warp_scale_z = scale_z;
Interp_warp_bitmap = bitmap_id;
Interp_warp_alpha = alpha;
}
//********************-----CLASS: texture_info-----********************//
texture_info::texture_info()
{
clear();
}
texture_info::texture_info(int bm_handle)
{
if(!bm_is_valid(bm_handle))
{
clear();
return;
}
this->original_texture = bm_handle;
this->ResetTexture();
}
void texture_info::clear()
{
texture = original_texture = -1;
num_frames = 0;
total_time = 1.0f;
}
int texture_info::GetNumFrames()
{
return num_frames;
}
int texture_info::GetOriginalTexture()
{
return original_texture;
}
int texture_info::GetTexture()
{
return texture;
}
float texture_info::GetTotalTime()
{
return total_time;
}
int texture_info::LoadTexture(char *filename, char *dbg_name = "<UNKNOWN>")
{
if (strlen(filename) + 4 >= NAME_LENGTH) //Filenames are passed in without extension
{
mprintf(("Generated texture name %s is too long. Skipping...\n"));
return -1;
}
this->original_texture = bm_load_either(filename, NULL, NULL, NULL, 1, CF_TYPE_MAPS);
if(this->original_texture < 0)
nprintf(("Maps", "For \"%s\" I couldn't find %s.ani\n", dbg_name, filename));
this->ResetTexture();
return texture;
}
void texture_info::PageIn()
{
bm_page_in_texture(texture);
}
void texture_info::PageOut(bool release)
{
if (texture >= 0) {
if (release) {
bm_release(texture);
texture = -1;
num_frames = 0;
total_time = 1.0f;
} else {
bm_unload(texture);
}
}
}
int texture_info::ResetTexture()
{
return this->SetTexture(original_texture);
}
int texture_info::SetTexture(int n_tex)
{
if(n_tex != -1 && !bm_is_valid(n_tex))
return texture;
//Set the new texture
texture = n_tex;
//If it is intentionally invalid, blank everything else
if(n_tex == -1)
{
num_frames = 0;
total_time = 1.0f;
}
else
{
//Determine the num_frames and total_time values.
int fps = 0;
this->num_frames = 1;
bm_get_info(texture, NULL, NULL, NULL, &this->num_frames, &fps);
this->total_time = (num_frames / ((fps > 0) ? (float)fps : 1.0f));
}
return texture;
}
//********************-----CLASS: texture_map-----********************//
int texture_map::FindTexture(int bm_handle)
{
if(!bm_is_valid(bm_handle))
return -1;
for(int i = 0; i < TM_NUM_TYPES; i++)
{
if (this->textures[i].GetTexture() == bm_handle)
return i;
}
return -1;
}
int texture_map::FindTexture(char *fname)
{
if(fname == NULL || !strlen(fname))
return -1;
char buf[NAME_LENGTH];
for(int i = 0; i < TM_NUM_TYPES; i++)
{
bm_get_filename(this->textures[i].GetTexture(), buf);
if (!strextcmp(buf, fname)) {
return i;
}
}
return -1;
}
void texture_map::PageIn()
{
for(int i = 0; i < TM_NUM_TYPES; i++)
this->textures[i].PageIn();
}
void texture_map::PageOut(bool release)
{
for(int i = 0; i < TM_NUM_TYPES; i++)
this->textures[i].PageOut(release);
}
void texture_map::Clear()
{
is_ambient = false;
is_transparent = false;
for(int i = 0; i < TM_NUM_TYPES; i++)
this->textures[i].clear();
}
void texture_map::ResetToOriginal()
{
for(int i = 0; i < TM_NUM_TYPES; i++)
this->textures[i].ResetTexture();
}