mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-11-22 04:12:09 +00:00
1643 lines
45 KiB
C++
1643 lines
45 KiB
C++
|
/*
|
||
|
===========================================================================
|
||
|
|
||
|
Doom 3 BFG Edition GPL Source Code
|
||
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
||
|
|
||
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
||
|
|
||
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
||
|
|
||
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||
|
|
||
|
===========================================================================
|
||
|
*/
|
||
|
|
||
|
#include "../idlib/precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
idCVar binaryLoadParticles( "binaryLoadParticles", "1", 0, "enable binary load/write of particle decls" );
|
||
|
|
||
|
static const byte BPRT_VERSION = 101;
|
||
|
static const unsigned int BPRT_MAGIC = ( 'B' << 24 ) | ( 'P' << 16 ) | ( 'R' << 8 ) | BPRT_VERSION;
|
||
|
|
||
|
struct ParticleParmDesc {
|
||
|
const char *name;
|
||
|
int count;
|
||
|
const char *desc;
|
||
|
};
|
||
|
|
||
|
const ParticleParmDesc ParticleDistributionDesc[] = {
|
||
|
{ "rect", 3, "" },
|
||
|
{ "cylinder", 4, "" },
|
||
|
{ "sphere", 3, "" }
|
||
|
};
|
||
|
|
||
|
const ParticleParmDesc ParticleDirectionDesc[] = {
|
||
|
{ "cone", 1, "" },
|
||
|
{ "outward", 1, "" },
|
||
|
};
|
||
|
|
||
|
const ParticleParmDesc ParticleOrientationDesc[] = {
|
||
|
{ "view", 0, "" },
|
||
|
{ "aimed", 2, "" },
|
||
|
{ "x", 0, "" },
|
||
|
{ "y", 0, "" },
|
||
|
{ "z", 0, "" }
|
||
|
};
|
||
|
|
||
|
const ParticleParmDesc ParticleCustomDesc[] = {
|
||
|
{ "standard", 0, "Standard" },
|
||
|
{ "helix", 5, "sizeX Y Z radialSpeed axialSpeed" },
|
||
|
{ "flies", 3, "radialSpeed axialSpeed size" },
|
||
|
{ "orbit", 2, "radius speed"},
|
||
|
{ "drip", 2, "something something" }
|
||
|
};
|
||
|
|
||
|
const int CustomParticleCount = sizeof( ParticleCustomDesc ) / sizeof( const ParticleParmDesc );
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
idDeclParticle::Size
|
||
|
=================
|
||
|
*/
|
||
|
size_t idDeclParticle::Size() const {
|
||
|
return sizeof( idDeclParticle );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=====================
|
||
|
idDeclParticle::GetStageBounds
|
||
|
=====================
|
||
|
*/
|
||
|
void idDeclParticle::GetStageBounds( idParticleStage *stage ) {
|
||
|
|
||
|
stage->bounds.Clear();
|
||
|
|
||
|
// this isn't absolutely guaranteed, but it should be close
|
||
|
|
||
|
particleGen_t g;
|
||
|
|
||
|
renderEntity_t renderEntity;
|
||
|
memset( &renderEntity, 0, sizeof( renderEntity ) );
|
||
|
renderEntity.axis = mat3_identity;
|
||
|
|
||
|
renderView_t renderView;
|
||
|
memset( &renderView, 0, sizeof( renderView ) );
|
||
|
renderView.viewaxis = mat3_identity;
|
||
|
|
||
|
g.renderEnt = &renderEntity;
|
||
|
g.renderView = &renderView;
|
||
|
g.origin.Zero();
|
||
|
g.axis = mat3_identity;
|
||
|
|
||
|
idRandom steppingRandom;
|
||
|
steppingRandom.SetSeed( 0 );
|
||
|
|
||
|
// just step through a lot of possible particles as a representative sampling
|
||
|
for ( int i = 0 ; i < 1000 ; i++ ) {
|
||
|
g.random = g.originalRandom = steppingRandom;
|
||
|
|
||
|
int maxMsec = stage->particleLife * 1000;
|
||
|
for ( int inCycleTime = 0 ; inCycleTime < maxMsec ; inCycleTime += 16 ) {
|
||
|
|
||
|
// make sure we get the very last tic, which may make up an extreme edge
|
||
|
if ( inCycleTime + 16 > maxMsec ) {
|
||
|
inCycleTime = maxMsec - 1;
|
||
|
}
|
||
|
|
||
|
g.frac = (float)inCycleTime / ( stage->particleLife * 1000 );
|
||
|
g.age = inCycleTime * 0.001f;
|
||
|
|
||
|
// if the particle doesn't get drawn because it is faded out or beyond a kill region,
|
||
|
// don't increment the verts
|
||
|
|
||
|
idVec3 origin;
|
||
|
stage->ParticleOrigin( &g, origin );
|
||
|
stage->bounds.AddPoint( origin );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// find the max size
|
||
|
float maxSize = 0;
|
||
|
|
||
|
for ( float f = 0; f <= 1.0f; f += 1.0f / 64 ) {
|
||
|
float size = stage->size.Eval( f, steppingRandom );
|
||
|
float aspect = stage->aspect.Eval( f, steppingRandom );
|
||
|
if ( aspect > 1 ) {
|
||
|
size *= aspect;
|
||
|
}
|
||
|
if ( size > maxSize ) {
|
||
|
maxSize = size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
maxSize += 8; // just for good measure
|
||
|
// users can specify a per-stage bounds expansion to handle odd cases
|
||
|
stage->bounds.ExpandSelf( maxSize + stage->boundsExpansion );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::ParseParms
|
||
|
|
||
|
Parses a variable length list of parms on one line
|
||
|
================
|
||
|
*/
|
||
|
void idDeclParticle::ParseParms( idLexer &src, float *parms, int maxParms ) {
|
||
|
idToken token;
|
||
|
|
||
|
memset( parms, 0, maxParms * sizeof( *parms ) );
|
||
|
int count = 0;
|
||
|
while( 1 ) {
|
||
|
if ( !src.ReadTokenOnLine( &token ) ) {
|
||
|
return;
|
||
|
}
|
||
|
if ( count == maxParms ) {
|
||
|
src.Error( "too many parms on line" );
|
||
|
return;
|
||
|
}
|
||
|
token.StripQuotes();
|
||
|
parms[count] = atof( token );
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::ParseParametric
|
||
|
================
|
||
|
*/
|
||
|
void idDeclParticle::ParseParametric( idLexer &src, idParticleParm *parm ) {
|
||
|
idToken token;
|
||
|
|
||
|
parm->table = NULL;
|
||
|
parm->from = parm->to = 0.0f;
|
||
|
|
||
|
if ( !src.ReadToken( &token ) ) {
|
||
|
src.Error( "not enough parameters" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( token.IsNumeric() ) {
|
||
|
// can have a to + 2nd parm
|
||
|
parm->from = parm->to = atof( token );
|
||
|
if ( src.ReadToken( &token ) ) {
|
||
|
if ( !token.Icmp( "to" ) ) {
|
||
|
if ( !src.ReadToken( &token ) ) {
|
||
|
src.Error( "missing second parameter" );
|
||
|
return;
|
||
|
}
|
||
|
parm->to = atof( token );
|
||
|
} else {
|
||
|
src.UnreadToken( &token );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// table
|
||
|
parm->table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token, false ) );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::ParseParticleStage
|
||
|
================
|
||
|
*/
|
||
|
idParticleStage *idDeclParticle::ParseParticleStage( idLexer &src ) {
|
||
|
idToken token;
|
||
|
|
||
|
idParticleStage *stage = new (TAG_DECL) idParticleStage;
|
||
|
stage->Default();
|
||
|
|
||
|
while (1) {
|
||
|
if ( src.HadError() ) {
|
||
|
break;
|
||
|
}
|
||
|
if ( !src.ReadToken( &token ) ) {
|
||
|
break;
|
||
|
}
|
||
|
if ( !token.Icmp( "}" ) ) {
|
||
|
break;
|
||
|
}
|
||
|
if ( !token.Icmp( "material" ) ) {
|
||
|
src.ReadToken( &token );
|
||
|
stage->material = declManager->FindMaterial( token.c_str() );
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "count" ) ) {
|
||
|
stage->totalParticles = src.ParseInt();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "time" ) ) {
|
||
|
stage->particleLife = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "cycles" ) ) {
|
||
|
stage->cycles = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "timeOffset" ) ) {
|
||
|
stage->timeOffset = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "deadTime" ) ) {
|
||
|
stage->deadTime = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "randomDistribution" ) ) {
|
||
|
stage->randomDistribution = src.ParseBool();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "bunching" ) ) {
|
||
|
stage->spawnBunching = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "distribution" ) ) {
|
||
|
src.ReadToken( &token );
|
||
|
if ( !token.Icmp( "rect" ) ) {
|
||
|
stage->distributionType = PDIST_RECT;
|
||
|
} else if ( !token.Icmp( "cylinder" ) ) {
|
||
|
stage->distributionType = PDIST_CYLINDER;
|
||
|
} else if ( !token.Icmp( "sphere" ) ) {
|
||
|
stage->distributionType = PDIST_SPHERE;
|
||
|
} else {
|
||
|
src.Error( "bad distribution type: %s\n", token.c_str() );
|
||
|
}
|
||
|
ParseParms( src, stage->distributionParms, sizeof( stage->distributionParms ) / sizeof( stage->distributionParms[0] ) );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "direction" ) ) {
|
||
|
src.ReadToken( &token );
|
||
|
if ( !token.Icmp( "cone" ) ) {
|
||
|
stage->directionType = PDIR_CONE;
|
||
|
} else if ( !token.Icmp( "outward" ) ) {
|
||
|
stage->directionType = PDIR_OUTWARD;
|
||
|
} else {
|
||
|
src.Error( "bad direction type: %s\n", token.c_str() );
|
||
|
}
|
||
|
ParseParms( src, stage->directionParms, sizeof( stage->directionParms ) / sizeof( stage->directionParms[0] ) );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "orientation" ) ) {
|
||
|
src.ReadToken( &token );
|
||
|
if ( !token.Icmp( "view" ) ) {
|
||
|
stage->orientation = POR_VIEW;
|
||
|
} else if ( !token.Icmp( "aimed" ) ) {
|
||
|
stage->orientation = POR_AIMED;
|
||
|
} else if ( !token.Icmp( "x" ) ) {
|
||
|
stage->orientation = POR_X;
|
||
|
} else if ( !token.Icmp( "y" ) ) {
|
||
|
stage->orientation = POR_Y;
|
||
|
} else if ( !token.Icmp( "z" ) ) {
|
||
|
stage->orientation = POR_Z;
|
||
|
} else {
|
||
|
src.Error( "bad orientation type: %s\n", token.c_str() );
|
||
|
}
|
||
|
ParseParms( src, stage->orientationParms, sizeof( stage->orientationParms ) / sizeof( stage->orientationParms[0] ) );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "customPath" ) ) {
|
||
|
src.ReadToken( &token );
|
||
|
if ( !token.Icmp( "standard" ) ) {
|
||
|
stage->customPathType = PPATH_STANDARD;
|
||
|
} else if ( !token.Icmp( "helix" ) ) {
|
||
|
stage->customPathType = PPATH_HELIX;
|
||
|
} else if ( !token.Icmp( "flies" ) ) {
|
||
|
stage->customPathType = PPATH_FLIES;
|
||
|
} else if ( !token.Icmp( "spherical" ) ) {
|
||
|
stage->customPathType = PPATH_ORBIT;
|
||
|
} else {
|
||
|
src.Error( "bad path type: %s\n", token.c_str() );
|
||
|
}
|
||
|
ParseParms( src, stage->customPathParms, sizeof( stage->customPathParms ) / sizeof( stage->customPathParms[0] ) );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "speed" ) ) {
|
||
|
ParseParametric( src, &stage->speed );
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "rotation" ) ) {
|
||
|
ParseParametric( src, &stage->rotationSpeed );
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "angle" ) ) {
|
||
|
stage->initialAngle = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "entityColor" ) ) {
|
||
|
stage->entityColor = src.ParseBool();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "size" ) ) {
|
||
|
ParseParametric( src, &stage->size );
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "aspect" ) ) {
|
||
|
ParseParametric( src, &stage->aspect );
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "fadeIn" ) ) {
|
||
|
stage->fadeInFraction = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "fadeOut" ) ) {
|
||
|
stage->fadeOutFraction = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "fadeIndex" ) ) {
|
||
|
stage->fadeIndexFraction = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "color" ) ) {
|
||
|
stage->color[0] = src.ParseFloat();
|
||
|
stage->color[1] = src.ParseFloat();
|
||
|
stage->color[2] = src.ParseFloat();
|
||
|
stage->color[3] = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "fadeColor" ) ) {
|
||
|
stage->fadeColor[0] = src.ParseFloat();
|
||
|
stage->fadeColor[1] = src.ParseFloat();
|
||
|
stage->fadeColor[2] = src.ParseFloat();
|
||
|
stage->fadeColor[3] = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp("offset" ) ) {
|
||
|
stage->offset[0] = src.ParseFloat();
|
||
|
stage->offset[1] = src.ParseFloat();
|
||
|
stage->offset[2] = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "animationFrames" ) ) {
|
||
|
stage->animationFrames = src.ParseInt();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "animationRate" ) ) {
|
||
|
stage->animationRate = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "boundsExpansion" ) ) {
|
||
|
stage->boundsExpansion = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
if ( !token.Icmp( "gravity" ) ) {
|
||
|
src.ReadToken( &token );
|
||
|
if ( !token.Icmp( "world" ) ) {
|
||
|
stage->worldGravity = true;
|
||
|
} else {
|
||
|
src.UnreadToken( &token );
|
||
|
}
|
||
|
stage->gravity = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
src.Error( "unknown token %s\n", token.c_str() );
|
||
|
}
|
||
|
|
||
|
// derive values
|
||
|
stage->cycleMsec = ( stage->particleLife + stage->deadTime ) * 1000;
|
||
|
|
||
|
return stage;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::Parse
|
||
|
================
|
||
|
*/
|
||
|
bool idDeclParticle::Parse( const char *text, const int textLength, bool allowBinaryVersion ) {
|
||
|
|
||
|
if ( cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
|
||
|
fileSystem->AddParticlePreload( GetName() );
|
||
|
}
|
||
|
|
||
|
idLexer src;
|
||
|
idToken token;
|
||
|
|
||
|
unsigned int sourceChecksum = 0;
|
||
|
idStrStatic< MAX_OSPATH > generatedFileName;
|
||
|
if ( allowBinaryVersion ) {
|
||
|
// Try to load the generated version of it
|
||
|
// If successful,
|
||
|
// - Create an MD5 of the hash of the source
|
||
|
// - Load the MD5 of the generated, if they differ, create a new generated
|
||
|
generatedFileName = "generated/particles/";
|
||
|
generatedFileName.AppendPath( GetName() );
|
||
|
generatedFileName.SetFileExtension( ".bprt" );
|
||
|
|
||
|
idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
|
||
|
sourceChecksum = MD5_BlockChecksum( text, textLength );
|
||
|
|
||
|
if ( binaryLoadParticles.GetBool() && LoadBinary( file, sourceChecksum ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
|
||
|
src.SetFlags( DECL_LEXER_FLAGS );
|
||
|
src.SkipUntilString( "{" );
|
||
|
|
||
|
depthHack = 0.0f;
|
||
|
|
||
|
while (1) {
|
||
|
if ( !src.ReadToken( &token ) ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "}" ) ) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "{" ) ) {
|
||
|
if ( stages.Num() >= MAX_PARTICLE_STAGES ) {
|
||
|
src.Error( "Too many particle stages" );
|
||
|
MakeDefault();
|
||
|
return false;
|
||
|
}
|
||
|
idParticleStage *stage = ParseParticleStage( src );
|
||
|
if ( !stage ) {
|
||
|
src.Warning( "Particle stage parse failed" );
|
||
|
MakeDefault();
|
||
|
return false;
|
||
|
}
|
||
|
stages.Append( stage );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( !token.Icmp( "depthHack" ) ) {
|
||
|
depthHack = src.ParseFloat();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
src.Warning( "bad token %s", token.c_str() );
|
||
|
MakeDefault();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// don't calculate bounds or write binary files for defaulted ( non-existent ) particles in resource builds
|
||
|
if ( fileSystem->UsingResourceFiles() ) {
|
||
|
bounds = idBounds( vec3_origin ).Expand( 8.0f );
|
||
|
return true;
|
||
|
}
|
||
|
//
|
||
|
// calculate the bounds
|
||
|
//
|
||
|
bounds.Clear();
|
||
|
for( int i = 0; i < stages.Num(); i++ ) {
|
||
|
GetStageBounds( stages[i] );
|
||
|
bounds.AddBounds( stages[i]->bounds );
|
||
|
}
|
||
|
|
||
|
if ( bounds.GetVolume() <= 0.1f ) {
|
||
|
bounds = idBounds( vec3_origin ).Expand( 8.0f );
|
||
|
}
|
||
|
|
||
|
if ( allowBinaryVersion && binaryLoadParticles.GetBool() ) {
|
||
|
idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
|
||
|
idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
|
||
|
WriteBinary( outputFile, sourceChecksum );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
========================
|
||
|
idDeclParticle::LoadBinary
|
||
|
========================
|
||
|
*/
|
||
|
bool idDeclParticle::LoadBinary( idFile * file, unsigned int checksum ) {
|
||
|
|
||
|
if ( file == NULL ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
struct local {
|
||
|
static void LoadParticleParm( idFile * file, idParticleParm & parm ) {
|
||
|
idStr name;
|
||
|
file->ReadString( name );
|
||
|
if ( name.IsEmpty() ) {
|
||
|
parm.table = NULL;
|
||
|
} else {
|
||
|
parm.table = (idDeclTable *)declManager->FindType( DECL_TABLE, name, false );
|
||
|
}
|
||
|
|
||
|
file->ReadFloat( parm.from );
|
||
|
file->ReadFloat( parm.to );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
unsigned int magic = 0;
|
||
|
file->ReadBig( magic );
|
||
|
if ( magic != BPRT_MAGIC ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
unsigned int loadedChecksum;
|
||
|
file->ReadBig( loadedChecksum );
|
||
|
if ( checksum != loadedChecksum && !fileSystem->InProductionMode() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int numStages;
|
||
|
file->ReadBig( numStages );
|
||
|
|
||
|
for ( int i = 0; i < numStages; i++ ) {
|
||
|
idParticleStage * s = new (TAG_DECL) idParticleStage;
|
||
|
stages.Append( s );
|
||
|
assert( stages.Num() <= MAX_PARTICLE_STAGES );
|
||
|
|
||
|
idStr name;
|
||
|
file->ReadString( name );
|
||
|
if ( name.IsEmpty() ) {
|
||
|
s->material = NULL;
|
||
|
} else {
|
||
|
s->material = declManager->FindMaterial( name );
|
||
|
}
|
||
|
|
||
|
file->ReadBig( s->totalParticles );
|
||
|
file->ReadFloat( s->cycles );
|
||
|
file->ReadBig( s->cycleMsec );
|
||
|
file->ReadFloat( s->spawnBunching );
|
||
|
file->ReadFloat( s->particleLife );
|
||
|
file->ReadFloat( s->timeOffset );
|
||
|
file->ReadFloat( s->deadTime );
|
||
|
file->ReadBig( s->distributionType );
|
||
|
file->ReadBigArray( s->distributionParms, sizeof( s->distributionParms ) / sizeof ( s->distributionParms[0] ) );
|
||
|
file->ReadBig( s->directionType );
|
||
|
file->ReadBigArray( s->directionParms, sizeof( s->directionParms ) / sizeof ( s->directionParms[0] ) );
|
||
|
local::LoadParticleParm( file, s->speed );
|
||
|
file->ReadFloat( s->gravity );
|
||
|
file->ReadBig( s->worldGravity );
|
||
|
file->ReadBig( s->randomDistribution );
|
||
|
file->ReadBig( s->entityColor );
|
||
|
file->ReadBig( s->customPathType );
|
||
|
file->ReadBigArray( s->customPathParms, sizeof( s->customPathParms ) / sizeof ( s->customPathParms[0] ) );
|
||
|
file->ReadVec3( s->offset );
|
||
|
file->ReadBig( s->animationFrames );
|
||
|
file->ReadFloat( s->animationRate );
|
||
|
file->ReadFloat( s->initialAngle );
|
||
|
local::LoadParticleParm( file, s->rotationSpeed );
|
||
|
file->ReadBig( s->orientation );
|
||
|
file->ReadBigArray( s->orientationParms, sizeof( s->orientationParms ) / sizeof ( s->orientationParms[0] ) );
|
||
|
local::LoadParticleParm( file, s->size );
|
||
|
local::LoadParticleParm( file, s->aspect );
|
||
|
file->ReadVec4( s->color );
|
||
|
file->ReadVec4( s->fadeColor );
|
||
|
file->ReadFloat( s->fadeInFraction );
|
||
|
file->ReadFloat( s->fadeOutFraction );
|
||
|
file->ReadFloat( s->fadeIndexFraction );
|
||
|
file->ReadBig( s->hidden );
|
||
|
file->ReadFloat( s->boundsExpansion );
|
||
|
file->ReadVec3( s->bounds[0] );
|
||
|
file->ReadVec3( s->bounds[1] );
|
||
|
}
|
||
|
|
||
|
file->ReadVec3( bounds[0] );
|
||
|
file->ReadVec3( bounds[1] );
|
||
|
file->ReadFloat( depthHack );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
========================
|
||
|
idDeclParticle::WriteBinary
|
||
|
========================
|
||
|
*/
|
||
|
void idDeclParticle::WriteBinary( idFile * file, unsigned int checksum ) {
|
||
|
|
||
|
if ( file == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
struct local {
|
||
|
static void WriteParticleParm( idFile * file, idParticleParm & parm ) {
|
||
|
if ( parm.table != NULL && parm.table->GetName() != NULL ) {
|
||
|
file->WriteString( parm.table->GetName() );
|
||
|
} else {
|
||
|
file->WriteString( "" );
|
||
|
}
|
||
|
file->WriteFloat( parm.from );
|
||
|
file->WriteFloat( parm.to );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
file->WriteBig( BPRT_MAGIC );
|
||
|
file->WriteBig( checksum );
|
||
|
file->WriteBig( stages.Num() );
|
||
|
|
||
|
for ( int i = 0; i < stages.Num(); i++ ) {
|
||
|
idParticleStage * s = stages[i];
|
||
|
|
||
|
if ( s->material != NULL && s->material->GetName() != NULL ) {
|
||
|
file->WriteString( s->material->GetName() );
|
||
|
} else {
|
||
|
file->WriteString( "" );
|
||
|
}
|
||
|
file->WriteBig( s->totalParticles );
|
||
|
file->WriteFloat( s->cycles );
|
||
|
file->WriteBig( s->cycleMsec );
|
||
|
file->WriteFloat( s->spawnBunching );
|
||
|
file->WriteFloat( s->particleLife );
|
||
|
file->WriteFloat( s->timeOffset );
|
||
|
file->WriteFloat( s->deadTime );
|
||
|
file->WriteBig( s->distributionType );
|
||
|
file->WriteBigArray( s->distributionParms, sizeof( s->distributionParms ) / sizeof ( s->distributionParms[0] ) );
|
||
|
file->WriteBig( s->directionType );
|
||
|
file->WriteBigArray( s->directionParms, sizeof( s->directionParms ) / sizeof ( s->directionParms[0] ) );
|
||
|
local::WriteParticleParm( file, s->speed );
|
||
|
file->WriteFloat( s->gravity );
|
||
|
file->WriteBig( s->worldGravity );
|
||
|
file->WriteBig( s->randomDistribution );
|
||
|
file->WriteBig( s->entityColor );
|
||
|
file->WriteBig( s->customPathType );
|
||
|
file->WriteBigArray( s->customPathParms, sizeof( s->customPathParms ) / sizeof ( s->customPathParms[0] ) );
|
||
|
file->WriteVec3( s->offset );
|
||
|
file->WriteBig( s->animationFrames );
|
||
|
file->WriteFloat( s->animationRate );
|
||
|
file->WriteFloat( s->initialAngle );
|
||
|
local::WriteParticleParm( file, s->rotationSpeed );
|
||
|
file->WriteBig( s->orientation );
|
||
|
file->WriteBigArray( s->orientationParms, sizeof( s->orientationParms ) / sizeof ( s->orientationParms[0] ) );
|
||
|
local::WriteParticleParm( file, s->size );
|
||
|
local::WriteParticleParm( file, s->aspect );
|
||
|
file->WriteVec4( s->color );
|
||
|
file->WriteVec4( s->fadeColor );
|
||
|
file->WriteFloat( s->fadeInFraction );
|
||
|
file->WriteFloat( s->fadeOutFraction );
|
||
|
file->WriteFloat( s->fadeIndexFraction );
|
||
|
file->WriteBig( s->hidden );
|
||
|
file->WriteFloat( s->boundsExpansion );
|
||
|
file->WriteVec3( s->bounds[0] );
|
||
|
file->WriteVec3( s->bounds[1] );
|
||
|
}
|
||
|
|
||
|
file->WriteVec3( bounds[0] );
|
||
|
file->WriteVec3( bounds[1] );
|
||
|
file->WriteFloat( depthHack );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::FreeData
|
||
|
================
|
||
|
*/
|
||
|
void idDeclParticle::FreeData() {
|
||
|
stages.DeleteContents( true );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::DefaultDefinition
|
||
|
================
|
||
|
*/
|
||
|
const char *idDeclParticle::DefaultDefinition() const {
|
||
|
return
|
||
|
"{\n"
|
||
|
"\t" "{\n"
|
||
|
"\t\t" "material\t_default\n"
|
||
|
"\t\t" "count\t20\n"
|
||
|
"\t\t" "time\t\t1.0\n"
|
||
|
"\t" "}\n"
|
||
|
"}";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::WriteParticleParm
|
||
|
================
|
||
|
*/
|
||
|
void idDeclParticle::WriteParticleParm( idFile *f, idParticleParm *parm, const char *name ) {
|
||
|
|
||
|
f->WriteFloatString( "\t\t%s\t\t\t\t ", name );
|
||
|
if ( parm->table ) {
|
||
|
f->WriteFloatString( "%s\n", parm->table->GetName() );
|
||
|
} else {
|
||
|
f->WriteFloatString( "\"%.3f\" ", parm->from );
|
||
|
if ( parm->from == parm->to ) {
|
||
|
f->WriteFloatString( "\n" );
|
||
|
} else {
|
||
|
f->WriteFloatString( " to \"%.3f\"\n", parm->to );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::WriteStage
|
||
|
================
|
||
|
*/
|
||
|
void idDeclParticle::WriteStage( idFile *f, idParticleStage *stage ) {
|
||
|
|
||
|
int i;
|
||
|
|
||
|
f->WriteFloatString( "\t{\n" );
|
||
|
f->WriteFloatString( "\t\tcount\t\t\t\t%i\n", stage->totalParticles );
|
||
|
f->WriteFloatString( "\t\tmaterial\t\t\t%s\n", stage->material->GetName() );
|
||
|
if ( stage->animationFrames ) {
|
||
|
f->WriteFloatString( "\t\tanimationFrames \t%i\n", stage->animationFrames );
|
||
|
}
|
||
|
if ( stage->animationRate ) {
|
||
|
f->WriteFloatString( "\t\tanimationRate \t\t%.3f\n", stage->animationRate );
|
||
|
}
|
||
|
f->WriteFloatString( "\t\ttime\t\t\t\t%.3f\n", stage->particleLife );
|
||
|
f->WriteFloatString( "\t\tcycles\t\t\t\t%.3f\n", stage->cycles );
|
||
|
if ( stage->timeOffset ) {
|
||
|
f->WriteFloatString( "\t\ttimeOffset\t\t\t%.3f\n", stage->timeOffset );
|
||
|
}
|
||
|
if ( stage->deadTime ) {
|
||
|
f->WriteFloatString( "\t\tdeadTime\t\t\t%.3f\n", stage->deadTime );
|
||
|
}
|
||
|
f->WriteFloatString( "\t\tbunching\t\t\t%.3f\n", stage->spawnBunching );
|
||
|
|
||
|
f->WriteFloatString( "\t\tdistribution\t\t%s ", ParticleDistributionDesc[stage->distributionType].name );
|
||
|
for ( i = 0; i < ParticleDistributionDesc[stage->distributionType].count; i++ ) {
|
||
|
f->WriteFloatString( "%.3f ", stage->distributionParms[i] );
|
||
|
}
|
||
|
f->WriteFloatString( "\n" );
|
||
|
|
||
|
f->WriteFloatString( "\t\tdirection\t\t\t%s ", ParticleDirectionDesc[stage->directionType].name );
|
||
|
for ( i = 0; i < ParticleDirectionDesc[stage->directionType].count; i++ ) {
|
||
|
f->WriteFloatString( "\"%.3f\" ", stage->directionParms[i] );
|
||
|
}
|
||
|
f->WriteFloatString( "\n" );
|
||
|
|
||
|
f->WriteFloatString( "\t\torientation\t\t\t%s ", ParticleOrientationDesc[stage->orientation].name );
|
||
|
for ( i = 0; i < ParticleOrientationDesc[stage->orientation].count; i++ ) {
|
||
|
f->WriteFloatString( "%.3f ", stage->orientationParms[i] );
|
||
|
}
|
||
|
f->WriteFloatString( "\n" );
|
||
|
|
||
|
if ( stage->customPathType != PPATH_STANDARD ) {
|
||
|
f->WriteFloatString( "\t\tcustomPath %s ", ParticleCustomDesc[stage->customPathType].name );
|
||
|
for ( i = 0; i < ParticleCustomDesc[stage->customPathType].count; i++ ) {
|
||
|
f->WriteFloatString( "%.3f ", stage->customPathParms[i] );
|
||
|
}
|
||
|
f->WriteFloatString( "\n" );
|
||
|
}
|
||
|
|
||
|
if ( stage->entityColor ) {
|
||
|
f->WriteFloatString( "\t\tentityColor\t\t\t1\n" );
|
||
|
}
|
||
|
|
||
|
WriteParticleParm( f, &stage->speed, "speed" );
|
||
|
WriteParticleParm( f, &stage->size, "size" );
|
||
|
WriteParticleParm( f, &stage->aspect, "aspect" );
|
||
|
|
||
|
if ( stage->rotationSpeed.from ) {
|
||
|
WriteParticleParm( f, &stage->rotationSpeed, "rotation" );
|
||
|
}
|
||
|
|
||
|
if ( stage->initialAngle ) {
|
||
|
f->WriteFloatString( "\t\tangle\t\t\t\t%.3f\n", stage->initialAngle );
|
||
|
}
|
||
|
|
||
|
f->WriteFloatString( "\t\trandomDistribution\t\t\t\t%i\n", static_cast<int>( stage->randomDistribution ) );
|
||
|
f->WriteFloatString( "\t\tboundsExpansion\t\t\t\t%.3f\n", stage->boundsExpansion );
|
||
|
|
||
|
|
||
|
f->WriteFloatString( "\t\tfadeIn\t\t\t\t%.3f\n", stage->fadeInFraction );
|
||
|
f->WriteFloatString( "\t\tfadeOut\t\t\t\t%.3f\n", stage->fadeOutFraction );
|
||
|
f->WriteFloatString( "\t\tfadeIndex\t\t\t\t%.3f\n", stage->fadeIndexFraction );
|
||
|
|
||
|
f->WriteFloatString( "\t\tcolor \t\t\t\t%.3f %.3f %.3f %.3f\n", stage->color.x, stage->color.y, stage->color.z, stage->color.w );
|
||
|
f->WriteFloatString( "\t\tfadeColor \t\t\t%.3f %.3f %.3f %.3f\n", stage->fadeColor.x, stage->fadeColor.y, stage->fadeColor.z, stage->fadeColor.w );
|
||
|
|
||
|
f->WriteFloatString( "\t\toffset \t\t\t\t%.3f %.3f %.3f\n", stage->offset.x, stage->offset.y, stage->offset.z );
|
||
|
f->WriteFloatString( "\t\tgravity \t\t\t" );
|
||
|
if ( stage->worldGravity ) {
|
||
|
f->WriteFloatString( "world " );
|
||
|
}
|
||
|
f->WriteFloatString( "%.3f\n", stage->gravity );
|
||
|
f->WriteFloatString( "\t}\n" );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::RebuildTextSource
|
||
|
================
|
||
|
*/
|
||
|
bool idDeclParticle::RebuildTextSource() {
|
||
|
idFile_Memory f;
|
||
|
|
||
|
f.WriteFloatString("\n\n/*\n"
|
||
|
"\tGenerated by the Particle Editor.\n"
|
||
|
"\tTo use the particle editor, launch the game and type 'editParticles' on the console.\n"
|
||
|
"*/\n" );
|
||
|
|
||
|
f.WriteFloatString( "particle %s {\n", GetName() );
|
||
|
|
||
|
if ( depthHack ) {
|
||
|
f.WriteFloatString( "\tdepthHack\t%f\n", depthHack );
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < stages.Num(); i++ ) {
|
||
|
WriteStage( &f, stages[i] );
|
||
|
}
|
||
|
|
||
|
f.WriteFloatString( "}" );
|
||
|
|
||
|
SetText( f.GetDataPtr() );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idDeclParticle::Save
|
||
|
================
|
||
|
*/
|
||
|
bool idDeclParticle::Save( const char *fileName ) {
|
||
|
RebuildTextSource();
|
||
|
if ( fileName ) {
|
||
|
declManager->CreateNewDecl( DECL_PARTICLE, GetName(), fileName );
|
||
|
}
|
||
|
ReplaceSourceFileText();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================================================================================
|
||
|
|
||
|
idParticleParm
|
||
|
|
||
|
====================================================================================
|
||
|
*/
|
||
|
|
||
|
float idParticleParm::Eval( float frac, idRandom &rand ) const {
|
||
|
if ( table ) {
|
||
|
return table->TableLookup( frac );
|
||
|
}
|
||
|
return from + frac * ( to - from );
|
||
|
}
|
||
|
|
||
|
float idParticleParm::Integrate( float frac, idRandom &rand ) const {
|
||
|
if ( table ) {
|
||
|
common->Printf( "idParticleParm::Integrate: can't integrate tables\n" );
|
||
|
return 0;
|
||
|
}
|
||
|
return ( from + frac * ( to - from ) * 0.5f ) * frac;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================================================================================
|
||
|
|
||
|
idParticleStage
|
||
|
|
||
|
====================================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idParticleStage::idParticleStage
|
||
|
================
|
||
|
*/
|
||
|
idParticleStage::idParticleStage() {
|
||
|
material = NULL;
|
||
|
totalParticles = 0;
|
||
|
cycles = 0.0f;
|
||
|
cycleMsec = 0;
|
||
|
spawnBunching = 0.0f;
|
||
|
particleLife = 0.0f;
|
||
|
timeOffset = 0.0f;
|
||
|
deadTime = 0.0f;
|
||
|
distributionType = PDIST_RECT;
|
||
|
distributionParms[0] = distributionParms[1] = distributionParms[2] = distributionParms[3] = 0.0f;
|
||
|
directionType = PDIR_CONE;
|
||
|
directionParms[0] = directionParms[1] = directionParms[2] = directionParms[3] = 0.0f;
|
||
|
// idParticleParm speed;
|
||
|
gravity = 0.0f;
|
||
|
worldGravity = false;
|
||
|
customPathType = PPATH_STANDARD;
|
||
|
customPathParms[0] = customPathParms[1] = customPathParms[2] = customPathParms[3] = 0.0f;
|
||
|
customPathParms[4] = customPathParms[5] = customPathParms[6] = customPathParms[7] = 0.0f;
|
||
|
offset.Zero();
|
||
|
animationFrames = 0;
|
||
|
animationRate = 0.0f;
|
||
|
randomDistribution = true;
|
||
|
entityColor = false;
|
||
|
initialAngle = 0.0f;
|
||
|
// idParticleParm rotationSpeed;
|
||
|
orientation = POR_VIEW;
|
||
|
orientationParms[0] = orientationParms[1] = orientationParms[2] = orientationParms[3] = 0.0f;
|
||
|
// idParticleParm size
|
||
|
// idParticleParm aspect
|
||
|
color.Zero();
|
||
|
fadeColor.Zero();
|
||
|
fadeInFraction = 0.0f;
|
||
|
fadeOutFraction = 0.0f;
|
||
|
fadeIndexFraction = 0.0f;
|
||
|
hidden = false;
|
||
|
boundsExpansion = 0.0f;
|
||
|
bounds.Clear();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idParticleStage::Default
|
||
|
|
||
|
Sets the stage to a default state
|
||
|
================
|
||
|
*/
|
||
|
void idParticleStage::Default() {
|
||
|
material = declManager->FindMaterial( "_default" );
|
||
|
totalParticles = 100;
|
||
|
spawnBunching = 1.0f;
|
||
|
particleLife = 1.5f;
|
||
|
timeOffset = 0.0f;
|
||
|
deadTime = 0.0f;
|
||
|
distributionType = PDIST_RECT;
|
||
|
distributionParms[0] = 8.0f;
|
||
|
distributionParms[1] = 8.0f;
|
||
|
distributionParms[2] = 8.0f;
|
||
|
distributionParms[3] = 0.0f;
|
||
|
directionType = PDIR_CONE;
|
||
|
directionParms[0] = 90.0f;
|
||
|
directionParms[1] = 0.0f;
|
||
|
directionParms[2] = 0.0f;
|
||
|
directionParms[3] = 0.0f;
|
||
|
orientation = POR_VIEW;
|
||
|
orientationParms[0] = 0.0f;
|
||
|
orientationParms[1] = 0.0f;
|
||
|
orientationParms[2] = 0.0f;
|
||
|
orientationParms[3] = 0.0f;
|
||
|
speed.from = 150.0f;
|
||
|
speed.to = 150.0f;
|
||
|
speed.table = NULL;
|
||
|
gravity = 1.0f;
|
||
|
worldGravity = false;
|
||
|
customPathType = PPATH_STANDARD;
|
||
|
customPathParms[0] = 0.0f;
|
||
|
customPathParms[1] = 0.0f;
|
||
|
customPathParms[2] = 0.0f;
|
||
|
customPathParms[3] = 0.0f;
|
||
|
customPathParms[4] = 0.0f;
|
||
|
customPathParms[5] = 0.0f;
|
||
|
customPathParms[6] = 0.0f;
|
||
|
customPathParms[7] = 0.0f;
|
||
|
offset.Zero();
|
||
|
animationFrames = 0;
|
||
|
animationRate = 0.0f;
|
||
|
initialAngle = 0.0f;
|
||
|
rotationSpeed.from = 0.0f;
|
||
|
rotationSpeed.to = 0.0f;
|
||
|
rotationSpeed.table = NULL;
|
||
|
size.from = 4.0f;
|
||
|
size.to = 4.0f;
|
||
|
size.table = NULL;
|
||
|
aspect.from = 1.0f;
|
||
|
aspect.to = 1.0f;
|
||
|
aspect.table = NULL;
|
||
|
color.x = 1.0f;
|
||
|
color.y = 1.0f;
|
||
|
color.z = 1.0f;
|
||
|
color.w = 1.0f;
|
||
|
fadeColor.x = 0.0f;
|
||
|
fadeColor.y = 0.0f;
|
||
|
fadeColor.z = 0.0f;
|
||
|
fadeColor.w = 0.0f;
|
||
|
fadeInFraction = 0.1f;
|
||
|
fadeOutFraction = 0.25f;
|
||
|
fadeIndexFraction = 0.0f;
|
||
|
boundsExpansion = 0.0f;
|
||
|
randomDistribution = true;
|
||
|
entityColor = false;
|
||
|
cycleMsec = ( particleLife + deadTime ) * 1000;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idParticleStage::NumQuadsPerParticle
|
||
|
|
||
|
includes trails and cross faded animations
|
||
|
================
|
||
|
*/
|
||
|
int idParticleStage::NumQuadsPerParticle() const {
|
||
|
int count = 1;
|
||
|
|
||
|
if ( orientation == POR_AIMED ) {
|
||
|
int trails = idMath::Ftoi( orientationParms[0] );
|
||
|
// each trail stage will add an extra quad
|
||
|
count *= ( 1 + trails );
|
||
|
}
|
||
|
|
||
|
// if we are doing strip-animation, we need to double the number and cross fade them
|
||
|
if ( animationFrames > 1 ) {
|
||
|
count *= 2;
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idParticleStage::ParticleOrigin
|
||
|
===============
|
||
|
*/
|
||
|
void idParticleStage::ParticleOrigin( particleGen_t *g, idVec3 &origin ) const {
|
||
|
if ( customPathType == PPATH_STANDARD ) {
|
||
|
//
|
||
|
// find intial origin distribution
|
||
|
//
|
||
|
float radiusSqr, angle1, angle2;
|
||
|
|
||
|
switch( distributionType ) {
|
||
|
case PDIST_RECT: { // ( sizeX sizeY sizeZ )
|
||
|
origin[0] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[0];
|
||
|
origin[1] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[1];
|
||
|
origin[2] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[2];
|
||
|
break;
|
||
|
}
|
||
|
case PDIST_CYLINDER: { // ( sizeX sizeY sizeZ ringFraction )
|
||
|
angle1 = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * idMath::TWO_PI;
|
||
|
|
||
|
idMath::SinCos16( angle1, origin[0], origin[1] );
|
||
|
origin[2] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f );
|
||
|
|
||
|
// reproject points that are inside the ringFraction to the outer band
|
||
|
if ( distributionParms[3] > 0.0f ) {
|
||
|
radiusSqr = origin[0] * origin[0] + origin[1] * origin[1];
|
||
|
if ( radiusSqr < distributionParms[3] * distributionParms[3] ) {
|
||
|
// if we are inside the inner reject zone, rescale to put it out into the good zone
|
||
|
float f = sqrt( radiusSqr ) / distributionParms[3];
|
||
|
float invf = 1.0f / f;
|
||
|
float newRadius = distributionParms[3] + f * ( 1.0f - distributionParms[3] );
|
||
|
float rescale = invf * newRadius;
|
||
|
|
||
|
origin[0] *= rescale;
|
||
|
origin[1] *= rescale;
|
||
|
}
|
||
|
}
|
||
|
origin[0] *= distributionParms[0];
|
||
|
origin[1] *= distributionParms[1];
|
||
|
origin[2] *= distributionParms[2];
|
||
|
break;
|
||
|
}
|
||
|
case PDIST_SPHERE: { // ( sizeX sizeY sizeZ ringFraction )
|
||
|
// iterating with rejection is the only way to get an even distribution over a sphere
|
||
|
if ( randomDistribution ) {
|
||
|
do {
|
||
|
origin[0] = g->random.CRandomFloat();
|
||
|
origin[1] = g->random.CRandomFloat();
|
||
|
origin[2] = g->random.CRandomFloat();
|
||
|
radiusSqr = origin[0] * origin[0] + origin[1] * origin[1] + origin[2] * origin[2];
|
||
|
} while( radiusSqr > 1.0f );
|
||
|
} else {
|
||
|
origin.Set( 1.0f, 1.0f, 1.0f );
|
||
|
radiusSqr = 3.0f;
|
||
|
}
|
||
|
|
||
|
if ( distributionParms[3] > 0.0f ) {
|
||
|
// we could iterate until we got something that also satisfied ringFraction,
|
||
|
// but for narrow rings that could be a lot of work, so reproject inside points instead
|
||
|
if ( radiusSqr < distributionParms[3] * distributionParms[3] ) {
|
||
|
// if we are inside the inner reject zone, rescale to put it out into the good zone
|
||
|
float f = sqrt( radiusSqr ) / distributionParms[3];
|
||
|
float invf = 1.0f / f;
|
||
|
float newRadius = distributionParms[3] + f * ( 1.0f - distributionParms[3] );
|
||
|
float rescale = invf * newRadius;
|
||
|
|
||
|
origin[0] *= rescale;
|
||
|
origin[1] *= rescale;
|
||
|
origin[2] *= rescale;
|
||
|
}
|
||
|
}
|
||
|
origin[0] *= distributionParms[0];
|
||
|
origin[1] *= distributionParms[1];
|
||
|
origin[2] *= distributionParms[2];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// offset will effect all particle origin types
|
||
|
// add this before the velocity and gravity additions
|
||
|
origin += offset;
|
||
|
|
||
|
//
|
||
|
// add the velocity over time
|
||
|
//
|
||
|
idVec3 dir;
|
||
|
|
||
|
switch( directionType ) {
|
||
|
case PDIR_CONE: {
|
||
|
// angle is the full angle, so 360 degrees is any spherical direction
|
||
|
angle1 = g->random.CRandomFloat() * directionParms[0] * idMath::M_DEG2RAD;
|
||
|
angle2 = g->random.CRandomFloat() * idMath::PI;
|
||
|
|
||
|
float s1, c1, s2, c2;
|
||
|
idMath::SinCos16( angle1, s1, c1 );
|
||
|
idMath::SinCos16( angle2, s2, c2 );
|
||
|
|
||
|
dir[0] = s1 * c2;
|
||
|
dir[1] = s1 * s2;
|
||
|
dir[2] = c1;
|
||
|
break;
|
||
|
}
|
||
|
case PDIR_OUTWARD: {
|
||
|
dir = origin;
|
||
|
dir.Normalize();
|
||
|
dir[2] += directionParms[0];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// add speed
|
||
|
float iSpeed = speed.Integrate( g->frac, g->random );
|
||
|
origin += dir * iSpeed * particleLife;
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// custom paths completely override both the origin and velocity calculations, but still
|
||
|
// use the standard gravity
|
||
|
//
|
||
|
float angle1, angle2, speed1, speed2;
|
||
|
switch( customPathType ) {
|
||
|
case PPATH_HELIX: { // ( sizeX sizeY sizeZ radialSpeed axialSpeed )
|
||
|
speed1 = g->random.CRandomFloat();
|
||
|
speed2 = g->random.CRandomFloat();
|
||
|
angle1 = g->random.RandomFloat() * idMath::TWO_PI + customPathParms[3] * speed1 * g->age;
|
||
|
|
||
|
float s1, c1;
|
||
|
idMath::SinCos16( angle1, s1, c1 );
|
||
|
|
||
|
origin[0] = c1 * customPathParms[0];
|
||
|
origin[1] = s1 * customPathParms[1];
|
||
|
origin[2] = g->random.RandomFloat() * customPathParms[2] + customPathParms[4] * speed2 * g->age;
|
||
|
break;
|
||
|
}
|
||
|
case PPATH_FLIES: { // ( radialSpeed axialSpeed size )
|
||
|
speed1 = idMath::ClampFloat( 0.4f, 1.0f, g->random.CRandomFloat() );
|
||
|
speed2 = idMath::ClampFloat( 0.4f, 1.0f, g->random.CRandomFloat() );
|
||
|
angle1 = g->random.RandomFloat() * idMath::PI * 2 + customPathParms[0] * speed1 * g->age;
|
||
|
angle2 = g->random.RandomFloat() * idMath::PI * 2 + customPathParms[1] * speed1 * g->age;
|
||
|
|
||
|
float s1, c1, s2, c2;
|
||
|
idMath::SinCos16( angle1, s1, c1 );
|
||
|
idMath::SinCos16( angle2, s2, c2 );
|
||
|
|
||
|
origin[0] = c1 * c2;
|
||
|
origin[1] = s1 * c2;
|
||
|
origin[2] = -s2;
|
||
|
origin *= customPathParms[2];
|
||
|
break;
|
||
|
}
|
||
|
case PPATH_ORBIT: { // ( radius speed axis )
|
||
|
angle1 = g->random.RandomFloat() * idMath::TWO_PI + customPathParms[1] * g->age;
|
||
|
|
||
|
float s1, c1;
|
||
|
idMath::SinCos16( angle1, s1, c1 );
|
||
|
|
||
|
origin[0] = c1 * customPathParms[0];
|
||
|
origin[1] = s1 * customPathParms[0];
|
||
|
origin.ProjectSelfOntoSphere( customPathParms[0] );
|
||
|
break;
|
||
|
}
|
||
|
case PPATH_DRIP: { // ( speed )
|
||
|
origin[0] = 0.0f;
|
||
|
origin[1] = 0.0f;
|
||
|
origin[2] = -( g->age * customPathParms[0] );
|
||
|
break;
|
||
|
}
|
||
|
default: {
|
||
|
common->Error( "idParticleStage::ParticleOrigin: bad customPathType" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
origin += offset;
|
||
|
}
|
||
|
|
||
|
// adjust for the per-particle smoke offset
|
||
|
origin *= g->axis;
|
||
|
origin += g->origin;
|
||
|
|
||
|
// add gravity after adjusting for axis
|
||
|
if ( worldGravity ) {
|
||
|
idVec3 gra( 0, 0, -gravity );
|
||
|
gra *= g->renderEnt->axis.Transpose();
|
||
|
origin += gra * g->age * g->age;
|
||
|
} else {
|
||
|
origin[2] -= gravity * g->age * g->age;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::ParticleVerts
|
||
|
==================
|
||
|
*/
|
||
|
int idParticleStage::ParticleVerts( particleGen_t *g, idVec3 origin, idDrawVert *verts ) const {
|
||
|
float psize = size.Eval( g->frac, g->random );
|
||
|
float paspect = aspect.Eval( g->frac, g->random );
|
||
|
|
||
|
float width = psize;
|
||
|
float height = psize * paspect;
|
||
|
|
||
|
idVec3 left, up;
|
||
|
|
||
|
if ( orientation == POR_AIMED ) {
|
||
|
// reset the values to an earlier time to get a previous origin
|
||
|
idRandom currentRandom = g->random;
|
||
|
float currentAge = g->age;
|
||
|
float currentFrac = g->frac;
|
||
|
idDrawVert *verts_p = verts;
|
||
|
idVec3 stepOrigin = origin;
|
||
|
idVec3 stepLeft;
|
||
|
int numTrails = idMath::Ftoi( orientationParms[0] );
|
||
|
float trailTime = orientationParms[1];
|
||
|
|
||
|
if ( trailTime == 0 ) {
|
||
|
trailTime = 0.5f;
|
||
|
}
|
||
|
|
||
|
float height = 1.0f / ( 1 + numTrails );
|
||
|
float t = 0;
|
||
|
|
||
|
for ( int i = 0 ; i <= numTrails ; i++ ) {
|
||
|
g->random = g->originalRandom;
|
||
|
g->age = currentAge - ( i + 1 ) * trailTime / ( numTrails + 1 ); // time to back up
|
||
|
g->frac = g->age / particleLife;
|
||
|
|
||
|
idVec3 oldOrigin;
|
||
|
ParticleOrigin( g, oldOrigin );
|
||
|
|
||
|
up = stepOrigin - oldOrigin; // along the direction of travel
|
||
|
|
||
|
idVec3 forwardDir;
|
||
|
g->renderEnt->axis.ProjectVector( g->renderView->viewaxis[0], forwardDir );
|
||
|
|
||
|
up -= ( up * forwardDir ) * forwardDir;
|
||
|
|
||
|
up.Normalize();
|
||
|
|
||
|
|
||
|
left = up.Cross( forwardDir );
|
||
|
left *= psize;
|
||
|
|
||
|
verts_p[0] = verts[0];
|
||
|
verts_p[1] = verts[1];
|
||
|
verts_p[2] = verts[2];
|
||
|
verts_p[3] = verts[3];
|
||
|
|
||
|
if ( i == 0 ) {
|
||
|
verts_p[0].xyz = stepOrigin - left;
|
||
|
verts_p[1].xyz = stepOrigin + left;
|
||
|
} else {
|
||
|
verts_p[0].xyz = stepOrigin - stepLeft;
|
||
|
verts_p[1].xyz = stepOrigin + stepLeft;
|
||
|
}
|
||
|
verts_p[2].xyz = oldOrigin - left;
|
||
|
verts_p[3].xyz = oldOrigin + left;
|
||
|
|
||
|
// modify texcoords
|
||
|
verts_p[0].SetTexCoordT( t );
|
||
|
verts_p[1].SetTexCoordT( t );
|
||
|
verts_p[2].SetTexCoordT( t + height );
|
||
|
verts_p[3].SetTexCoordT( t + height );
|
||
|
|
||
|
t += height;
|
||
|
|
||
|
verts_p += 4;
|
||
|
|
||
|
stepOrigin = oldOrigin;
|
||
|
stepLeft = left;
|
||
|
}
|
||
|
|
||
|
g->random = currentRandom;
|
||
|
g->age = currentAge;
|
||
|
g->frac = currentFrac;
|
||
|
|
||
|
return 4 * (numTrails+1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// constant rotation
|
||
|
//
|
||
|
float angle;
|
||
|
|
||
|
angle = ( initialAngle ) ? initialAngle : 360 * g->random.RandomFloat();
|
||
|
|
||
|
float angleMove = rotationSpeed.Integrate( g->frac, g->random ) * particleLife;
|
||
|
// have hald the particles rotate each way
|
||
|
if ( g->index & 1 ) {
|
||
|
angle += angleMove;
|
||
|
} else {
|
||
|
angle -= angleMove;
|
||
|
}
|
||
|
|
||
|
angle = angle / 180 * idMath::PI;
|
||
|
float c = idMath::Cos16( angle );
|
||
|
float s = idMath::Sin16( angle );
|
||
|
|
||
|
if ( orientation == POR_Z ) {
|
||
|
// oriented in entity space
|
||
|
left[0] = s;
|
||
|
left[1] = c;
|
||
|
left[2] = 0;
|
||
|
up[0] = c;
|
||
|
up[1] = -s;
|
||
|
up[2] = 0;
|
||
|
} else if ( orientation == POR_X ) {
|
||
|
// oriented in entity space
|
||
|
left[0] = 0;
|
||
|
left[1] = c;
|
||
|
left[2] = s;
|
||
|
up[0] = 0;
|
||
|
up[1] = -s;
|
||
|
up[2] = c;
|
||
|
} else if ( orientation == POR_Y ) {
|
||
|
// oriented in entity space
|
||
|
left[0] = c;
|
||
|
left[1] = 0;
|
||
|
left[2] = s;
|
||
|
up[0] = -s;
|
||
|
up[1] = 0;
|
||
|
up[2] = c;
|
||
|
} else {
|
||
|
// oriented in viewer space
|
||
|
idVec3 entityLeft, entityUp;
|
||
|
|
||
|
g->renderEnt->axis.ProjectVector( g->renderView->viewaxis[1], entityLeft );
|
||
|
g->renderEnt->axis.ProjectVector( g->renderView->viewaxis[2], entityUp );
|
||
|
|
||
|
left = entityLeft * c + entityUp * s;
|
||
|
up = entityUp * c - entityLeft * s;
|
||
|
}
|
||
|
|
||
|
left *= width;
|
||
|
up *= height;
|
||
|
|
||
|
verts[0].xyz = origin - left + up;
|
||
|
verts[1].xyz = origin + left + up;
|
||
|
verts[2].xyz = origin - left - up;
|
||
|
verts[3].xyz = origin + left - up;
|
||
|
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::ParticleTexCoords
|
||
|
==================
|
||
|
*/
|
||
|
void idParticleStage::ParticleTexCoords( particleGen_t *g, idDrawVert *verts ) const {
|
||
|
float s, width;
|
||
|
float t, height;
|
||
|
|
||
|
if ( animationFrames > 1 ) {
|
||
|
width = 1.0f / animationFrames;
|
||
|
float floatFrame;
|
||
|
if ( animationRate ) {
|
||
|
// explicit, cycling animation
|
||
|
floatFrame = g->age * animationRate;
|
||
|
} else {
|
||
|
// single animation cycle over the life of the particle
|
||
|
floatFrame = g->frac * animationFrames;
|
||
|
}
|
||
|
int intFrame = (int)floatFrame;
|
||
|
g->animationFrameFrac = floatFrame - intFrame;
|
||
|
s = width * intFrame;
|
||
|
} else {
|
||
|
s = 0.0f;
|
||
|
width = 1.0f;
|
||
|
}
|
||
|
|
||
|
t = 0.0f;
|
||
|
height = 1.0f;
|
||
|
|
||
|
verts[0].SetTexCoord( s, t );
|
||
|
verts[1].SetTexCoord( s+width, t );
|
||
|
verts[2].SetTexCoord( s, t+height );
|
||
|
verts[3].SetTexCoord( s+width, t+height );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::ParticleColors
|
||
|
==================
|
||
|
*/
|
||
|
void idParticleStage::ParticleColors( particleGen_t *g, idDrawVert *verts ) const {
|
||
|
float fadeFraction = 1.0f;
|
||
|
|
||
|
// most particles fade in at the beginning and fade out at the end
|
||
|
if ( g->frac < fadeInFraction ) {
|
||
|
fadeFraction *= ( g->frac / fadeInFraction );
|
||
|
}
|
||
|
if ( 1.0f - g->frac < fadeOutFraction ) {
|
||
|
fadeFraction *= ( ( 1.0f - g->frac ) / fadeOutFraction );
|
||
|
}
|
||
|
|
||
|
// individual gun smoke particles get more and more faded as the
|
||
|
// cycle goes on (note that totalParticles won't be correct for a surface-particle deform)
|
||
|
if ( fadeIndexFraction ) {
|
||
|
float indexFrac = ( totalParticles - g->index ) / (float)totalParticles;
|
||
|
if ( indexFrac < fadeIndexFraction ) {
|
||
|
fadeFraction *= indexFrac / fadeIndexFraction;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( int i = 0 ; i < 4 ; i++ ) {
|
||
|
float fcolor = ( ( entityColor ) ? g->renderEnt->shaderParms[i] : color[i] ) * fadeFraction + fadeColor[i] * ( 1.0f - fadeFraction );
|
||
|
int icolor = idMath::Ftoi( fcolor * 255.0f );
|
||
|
if ( icolor < 0 ) {
|
||
|
icolor = 0;
|
||
|
} else if ( icolor > 255 ) {
|
||
|
icolor = 255;
|
||
|
}
|
||
|
verts[0].color[i] =
|
||
|
verts[1].color[i] =
|
||
|
verts[2].color[i] =
|
||
|
verts[3].color[i] = icolor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idParticleStage::CreateParticle
|
||
|
|
||
|
Returns 0 if no particle is created because it is completely faded out
|
||
|
Returns 4 if a normal quad is created
|
||
|
Returns 8 if two cross faded quads are created
|
||
|
|
||
|
Vertex order is:
|
||
|
|
||
|
0 1
|
||
|
2 3
|
||
|
================
|
||
|
*/
|
||
|
int idParticleStage::CreateParticle( particleGen_t *g, idDrawVert *verts ) const {
|
||
|
idVec3 origin;
|
||
|
|
||
|
verts[0].Clear();
|
||
|
verts[1].Clear();
|
||
|
verts[2].Clear();
|
||
|
verts[3].Clear();
|
||
|
|
||
|
ParticleColors( g, verts );
|
||
|
|
||
|
// if we are completely faded out, kill the particle
|
||
|
if ( verts[0].color[0] == 0 && verts[0].color[1] == 0 && verts[0].color[2] == 0 && verts[0].color[3] == 0 ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ParticleOrigin( g, origin );
|
||
|
|
||
|
ParticleTexCoords( g, verts );
|
||
|
|
||
|
int numVerts = ParticleVerts( g, origin, verts );
|
||
|
|
||
|
if ( animationFrames <= 1 ) {
|
||
|
return numVerts;
|
||
|
}
|
||
|
|
||
|
// if we are doing strip-animation, we need to double the quad and cross fade it
|
||
|
float width = 1.0f / animationFrames;
|
||
|
float frac = g->animationFrameFrac;
|
||
|
float iFrac = 1.0f - frac;
|
||
|
|
||
|
idVec2 tempST;
|
||
|
for ( int i = 0 ; i < numVerts ; i++ ) {
|
||
|
verts[numVerts + i] = verts[i];
|
||
|
|
||
|
tempST = verts[numVerts + i].GetTexCoord();
|
||
|
verts[numVerts + i].SetTexCoord( tempST.x + width, tempST.y );
|
||
|
|
||
|
verts[numVerts + i].color[0] *= frac;
|
||
|
verts[numVerts + i].color[1] *= frac;
|
||
|
verts[numVerts + i].color[2] *= frac;
|
||
|
verts[numVerts + i].color[3] *= frac;
|
||
|
|
||
|
verts[i].color[0] *= iFrac;
|
||
|
verts[i].color[1] *= iFrac;
|
||
|
verts[i].color[2] *= iFrac;
|
||
|
verts[i].color[3] *= iFrac;
|
||
|
}
|
||
|
|
||
|
return numVerts * 2;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::GetCustomPathName
|
||
|
==================
|
||
|
*/
|
||
|
const char* idParticleStage::GetCustomPathName() {
|
||
|
int index = ( customPathType < CustomParticleCount ) ? customPathType : 0;
|
||
|
return ParticleCustomDesc[index].name;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::GetCustomPathDesc
|
||
|
==================
|
||
|
*/
|
||
|
const char* idParticleStage::GetCustomPathDesc() {
|
||
|
int index = ( customPathType < CustomParticleCount ) ? customPathType : 0;
|
||
|
return ParticleCustomDesc[index].desc;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::NumCustomPathParms
|
||
|
==================
|
||
|
*/
|
||
|
int idParticleStage::NumCustomPathParms() {
|
||
|
int index = ( customPathType < CustomParticleCount ) ? customPathType : 0;
|
||
|
return ParticleCustomDesc[index].count;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::SetCustomPathType
|
||
|
==================
|
||
|
*/
|
||
|
void idParticleStage::SetCustomPathType( const char *p ) {
|
||
|
customPathType = PPATH_STANDARD;
|
||
|
for ( int i = 0; i < CustomParticleCount; i ++ ) {
|
||
|
if ( idStr::Icmp( p, ParticleCustomDesc[i].name ) == 0 ) {
|
||
|
customPathType = static_cast<prtCustomPth_t>( i );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
idParticleStage::operator=
|
||
|
==================
|
||
|
*/
|
||
|
void idParticleStage::operator=( const idParticleStage &src ) {
|
||
|
material = src.material;
|
||
|
totalParticles = src.totalParticles;
|
||
|
cycles = src.cycles;
|
||
|
cycleMsec = src.cycleMsec;
|
||
|
spawnBunching = src.spawnBunching;
|
||
|
particleLife = src.particleLife;
|
||
|
timeOffset = src.timeOffset;
|
||
|
deadTime = src.deadTime;
|
||
|
distributionType = src.distributionType;
|
||
|
distributionParms[0] = src.distributionParms[0];
|
||
|
distributionParms[1] = src.distributionParms[1];
|
||
|
distributionParms[2] = src.distributionParms[2];
|
||
|
distributionParms[3] = src.distributionParms[3];
|
||
|
directionType = src.directionType;
|
||
|
directionParms[0] = src.directionParms[0];
|
||
|
directionParms[1] = src.directionParms[1];
|
||
|
directionParms[2] = src.directionParms[2];
|
||
|
directionParms[3] = src.directionParms[3];
|
||
|
speed = src.speed;
|
||
|
gravity = src.gravity;
|
||
|
worldGravity = src.worldGravity;
|
||
|
randomDistribution = src.randomDistribution;
|
||
|
entityColor = src.entityColor;
|
||
|
customPathType = src.customPathType;
|
||
|
customPathParms[0] = src.customPathParms[0];
|
||
|
customPathParms[1] = src.customPathParms[1];
|
||
|
customPathParms[2] = src.customPathParms[2];
|
||
|
customPathParms[3] = src.customPathParms[3];
|
||
|
customPathParms[4] = src.customPathParms[4];
|
||
|
customPathParms[5] = src.customPathParms[5];
|
||
|
customPathParms[6] = src.customPathParms[6];
|
||
|
customPathParms[7] = src.customPathParms[7];
|
||
|
offset = src.offset;
|
||
|
animationFrames = src.animationFrames;
|
||
|
animationRate = src.animationRate;
|
||
|
initialAngle = src.initialAngle;
|
||
|
rotationSpeed = src.rotationSpeed;
|
||
|
orientation = src.orientation;
|
||
|
orientationParms[0] = src.orientationParms[0];
|
||
|
orientationParms[1] = src.orientationParms[1];
|
||
|
orientationParms[2] = src.orientationParms[2];
|
||
|
orientationParms[3] = src.orientationParms[3];
|
||
|
size = src.size;
|
||
|
aspect = src.aspect;
|
||
|
color = src.color;
|
||
|
fadeColor = src.fadeColor;
|
||
|
fadeInFraction = src.fadeInFraction;
|
||
|
fadeOutFraction = src.fadeOutFraction;
|
||
|
fadeIndexFraction = src.fadeIndexFraction;
|
||
|
hidden = src.hidden;
|
||
|
boundsExpansion = src.boundsExpansion;
|
||
|
bounds = src.bounds;
|
||
|
}
|