12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643 |
- /*
- ===========================================================================
- 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;
- }
|