Files
fs2open.github.com/code/weapon/muzzleflash.cpp
Brian Hernandez 08548ea517 Scale distant combat related effects so they stay visible (#3656)
* Scale distant combat related effects so they stay visible
- Effects scaled so that they always maintain a minimum pixel size
- Supports lasers, beams, all trails, muzzle flashes, and thrusters
- Disabled by default, but can be configured through the game_settings.tbl

* Added comments clarifying when min pixel size scaling happens.

* Convert some NULLs to nullptr to make CLANG happy
2021-10-13 19:46:01 -04:00

332 lines
7.7 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.
*
*/
#include "globalincs/systemvars.h"
#include "graphics/2d.h"
#include "math/vecmat.h"
#include "object/object.h"
#include "parse/parselo.h"
#include "particle/particle.h"
#include "weapon/muzzleflash.h"
#include "model/modelrender.h"
// ---------------------------------------------------------------------------------------------------------------------
// MUZZLE FLASH DEFINES/VARS
//
// muzzle flash info - read from a table
typedef struct mflash_blob_info {
char name[MAX_FILENAME_LEN];
int anim_id;
float offset;
float radius;
mflash_blob_info( const mflash_blob_info& mbi )
{
strcpy_s( name, mbi.name );
anim_id = mbi.anim_id;
offset = mbi.offset;
radius = mbi.radius;
}
mflash_blob_info() :
anim_id( -1 ),
offset( 0.0 ),
radius( 0.0 )
{
name[ 0 ] = '\0';
}
mflash_blob_info& operator=( const mflash_blob_info& r )
{
strcpy_s( name, r.name );
anim_id = r.anim_id;
offset = r.offset;
radius = r.radius;
return *this;
}
} mflash_blob_info;
typedef struct mflash_info {
char name[MAX_FILENAME_LEN];
int used_this_level;
SCP_vector<mflash_blob_info> blobs;
mflash_info()
: used_this_level( 0 )
{
name[ 0 ] = '\0';
}
mflash_info( const mflash_info& mi )
{
strcpy_s( name, mi.name );
used_this_level = mi.used_this_level;
blobs = mi.blobs;
}
mflash_info& operator=( const mflash_info& r )
{
strcpy_s( name, r.name );
used_this_level = r.used_this_level;
blobs = r.blobs;
return *this;
}
} mflash_info;
SCP_vector<mflash_info> Mflash_info;
// ---------------------------------------------------------------------------------------------------------------------
// MUZZLE FLASH FUNCTIONS
//
void parse_mflash_tbl(const char *filename)
{
uint i;
try
{
read_file_text(filename, CF_TYPE_TABLES);
reset_parse();
// header
required_string("#Muzzle flash types");
while (optional_string("$Mflash:")) {
mflash_info mflash;
bool override_mflash = false;
required_string("+name:");
stuff_string(mflash.name, F_NAME, MAX_FILENAME_LEN);
if (optional_string("+override"))
override_mflash = true;
// read in all blobs
while (optional_string("+blob_name:")) {
mflash_blob_info mblob;
stuff_string(mblob.name, F_NAME, MAX_FILENAME_LEN);
required_string("+blob_offset:");
stuff_float(&mblob.offset);
required_string("+blob_radius:");
stuff_float(&mblob.radius);
mflash.blobs.push_back(mblob);
}
for (i = 0; i < Mflash_info.size(); i++) {
if (!stricmp(mflash.name, Mflash_info[i].name)) {
if (override_mflash) {
Mflash_info[i] = mflash;
}
break;
}
}
// no matching name exists so add as new
if (i == Mflash_info.size()) {
Mflash_info.push_back(mflash);
}
// a mflash of the same name exists, don't add it again
else {
if (!override_mflash) {
Warning(LOCATION, "Muzzle flash \"%s\" already exists! Using existing entry instead.", mflash.name);
}
}
}
// close
required_string("#end");
}
catch (const parse::ParseException& e)
{
mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
return;
}
}
// initialize muzzle flash stuff for the whole game
void mflash_game_init()
{
// parse main table first
parse_mflash_tbl("mflash.tbl");
// look for any modular tables
parse_modular_table(NOX("*-mfl.tbm"), parse_mflash_tbl);
}
void mflash_mark_as_used(int index)
{
if (index < 0)
return;
Assert( index < (int)Mflash_info.size() );
Mflash_info[index].used_this_level++;
}
void mflash_page_in(bool load_all)
{
uint i, idx;
int num_frames, fps;
// load up all anims
for ( i = 0; i < Mflash_info.size(); i++) {
// skip if it's not used
if ( !load_all && !Mflash_info[i].used_this_level )
continue;
// blobs
size_t original_num_blobs = Mflash_info[i].blobs.size();
int original_idx = 1;
for ( idx = 0; idx < Mflash_info[i].blobs.size(); ) {
mflash_blob_info* mfbip = &Mflash_info[i].blobs[idx];
mfbip->anim_id = bm_load_either(mfbip->name, &num_frames, &fps, NULL, true);
if ( mfbip->anim_id >= 0 ) {
bm_page_in_xparent_texture( mfbip->anim_id );
++idx;
}
else {
Warning(LOCATION, "Muzleflash \"%s\", blob [%d/" SIZE_T_ARG "]\nMuzzleflash blob \"%s\" not found! Deleting.",
Mflash_info[i].name, original_idx, original_num_blobs, Mflash_info[i].blobs[idx].name);
Mflash_info[i].blobs.erase( Mflash_info[i].blobs.begin() + idx );
}
++original_idx;
}
}
}
// initialize muzzle flash stuff for the level
void mflash_level_init()
{
uint i, idx;
// reset all anim usage for this level
for ( i = 0; i < Mflash_info.size(); i++) {
for ( idx = 0; idx < Mflash_info[i].blobs.size(); idx++) {
Mflash_info[i].used_this_level = 0;
}
}
}
// shutdown stuff for the level
void mflash_level_close()
{
uint i, idx;
// release all anims
for ( i = 0; i < Mflash_info.size(); i++) {
// blobs
for ( idx = 0; idx < Mflash_info[i].blobs.size(); idx++) {
if ( Mflash_info[i].blobs[idx].anim_id < 0 )
continue;
bm_release( Mflash_info[i].blobs[idx].anim_id );
Mflash_info[i].blobs[idx].anim_id = -1;
}
}
}
// create a muzzle flash on the guy
void mflash_create(vec3d *gun_pos, vec3d *gun_dir, physics_info *pip, int mflash_type, object *local)
{
// mflash *mflashp;
mflash_info *mi;
mflash_blob_info *mbi;
uint idx;
// standalone server should never create trails
if(Game_mode & GM_STANDALONE_SERVER){
return;
}
// illegal value
if ( (mflash_type < 0) || (mflash_type >= (int)Mflash_info.size()) )
return;
// create the actual animations
mi = &Mflash_info[mflash_type];
if (local != NULL) {
int attached_objnum = OBJ_INDEX(local);
// This muzzle flash is in local space, so its world position must be derived to apply scaling.
vec3d gun_world_pos;
vm_vec_unrotate(&gun_world_pos, gun_pos, &Objects[attached_objnum].orient);
vm_vec_add2(&gun_world_pos, &Objects[attached_objnum].pos);
for (idx = 0; idx < mi->blobs.size(); idx++) {
mbi = &mi->blobs[idx];
// bogus anim
if (mbi->anim_id < 0)
continue;
// fire it up
particle::particle_info p;
vm_vec_scale_add(&p.pos, gun_pos, gun_dir, mbi->offset);
vm_vec_zero(&p.vel);
//vm_vec_scale_add(&p.vel, &pip->rotvel, &pip->vel, 1.0f);
p.type = particle::PARTICLE_BITMAP;
p.optional_data = mbi->anim_id;
p.attached_objnum = attached_objnum;
p.attached_sig = local->signature;
// Scale the radius of the muzzle flash effect so that it always appears some minimum width in pixels.
p.rad = model_render_get_diameter_clamped_to_min_pixel_size(&gun_world_pos, mbi->radius * 2.0f, Min_pizel_size_muzzleflash) / 2.0f;
particle::create(&p);
}
} else {
for (idx = 0; idx < mi->blobs.size(); idx++) {
mbi = &mi->blobs[idx];
// bogus anim
if (mbi->anim_id < 0)
continue;
// fire it up
particle::particle_info p;
vm_vec_scale_add(&p.pos, gun_pos, gun_dir, mbi->offset);
vm_vec_scale_add(&p.vel, &pip->rotvel, &pip->vel, 1.0f);
p.type = particle::PARTICLE_BITMAP;
p.optional_data = mbi->anim_id;
p.attached_objnum = -1;
p.attached_sig = 0;
// Scale the radius of the muzzle flash effect so that it always appears some minimum width in pixels.
p.rad = model_render_get_diameter_clamped_to_min_pixel_size(&p.pos, mbi->radius * 2.0f, Min_pizel_size_muzzleflash) / 2.0f;
particle::create(&p);
}
}
}
// lookup type by name
int mflash_lookup(char *name)
{
uint idx;
// look it up
for (idx = 0; idx < Mflash_info.size(); idx++) {
if ( !stricmp(name, Mflash_info[idx].name) )
return idx;
}
// couldn't find it
return -1;
}