DeclParticle.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. struct ParticleParmDesc {
  23. const char *name;
  24. int count;
  25. const char *desc;
  26. };
  27. const ParticleParmDesc ParticleDistributionDesc[] = {
  28. { "rect", 3, "" },
  29. { "cylinder", 4, "" },
  30. { "sphere", 3, "" }
  31. };
  32. const ParticleParmDesc ParticleDirectionDesc[] = {
  33. { "cone", 1, "" },
  34. { "outward", 1, "" },
  35. };
  36. const ParticleParmDesc ParticleOrientationDesc[] = {
  37. { "view", 0, "" },
  38. { "aimed", 2, "" },
  39. { "x", 0, "" },
  40. { "y", 0, "" },
  41. { "z", 0, "" }
  42. };
  43. const ParticleParmDesc ParticleCustomDesc[] = {
  44. { "standard", 0, "Standard" },
  45. { "helix", 5, "sizeX Y Z radialSpeed axialSpeed" },
  46. { "flies", 3, "radialSpeed axialSpeed size" },
  47. { "orbit", 2, "radius speed"},
  48. { "drip", 2, "something something" }
  49. };
  50. const int CustomParticleCount = sizeof( ParticleCustomDesc ) / sizeof( const ParticleParmDesc );
  51. /*
  52. =================
  53. idDeclParticle::Size
  54. =================
  55. */
  56. size_t idDeclParticle::Size( void ) const {
  57. return sizeof( idDeclParticle );
  58. }
  59. /*
  60. =====================
  61. idDeclParticle::GetStageBounds
  62. =====================
  63. */
  64. void idDeclParticle::GetStageBounds( idParticleStage *stage ) {
  65. stage->bounds.Clear();
  66. // this isn't absolutely guaranteed, but it should be close
  67. particleGen_t g;
  68. renderEntity_t renderEntity;
  69. memset( &renderEntity, 0, sizeof( renderEntity ) );
  70. renderEntity.axis = mat3_identity;
  71. renderView_t renderView;
  72. memset( &renderView, 0, sizeof( renderView ) );
  73. renderView.viewaxis = mat3_identity;
  74. g.renderEnt = &renderEntity;
  75. g.renderView = &renderView;
  76. g.origin.Zero();
  77. g.axis = mat3_identity;
  78. idRandom steppingRandom;
  79. steppingRandom.SetSeed( 0 );
  80. // just step through a lot of possible particles as a representative sampling
  81. for ( int i = 0 ; i < 1000 ; i++ ) {
  82. g.random = g.originalRandom = steppingRandom;
  83. int maxMsec = stage->particleLife * 1000;
  84. for ( int inCycleTime = 0 ; inCycleTime < maxMsec ; inCycleTime += 16 ) {
  85. // make sure we get the very last tic, which may make up an extreme edge
  86. if ( inCycleTime + 16 > maxMsec ) {
  87. inCycleTime = maxMsec - 1;
  88. }
  89. g.frac = (float)inCycleTime / ( stage->particleLife * 1000 );
  90. g.age = inCycleTime * 0.001f;
  91. // if the particle doesn't get drawn because it is faded out or beyond a kill region,
  92. // don't increment the verts
  93. idVec3 origin;
  94. stage->ParticleOrigin( &g, origin );
  95. stage->bounds.AddPoint( origin );
  96. }
  97. }
  98. // find the max size
  99. float maxSize = 0;
  100. for ( float f = 0; f <= 1.0f; f += 1.0f / 64 ) {
  101. float size = stage->size.Eval( f, steppingRandom );
  102. float aspect = stage->aspect.Eval( f, steppingRandom );
  103. if ( aspect > 1 ) {
  104. size *= aspect;
  105. }
  106. if ( size > maxSize ) {
  107. maxSize = size;
  108. }
  109. }
  110. maxSize += 8; // just for good measure
  111. // users can specify a per-stage bounds expansion to handle odd cases
  112. stage->bounds.ExpandSelf( maxSize + stage->boundsExpansion );
  113. }
  114. /*
  115. ================
  116. idDeclParticle::ParseParms
  117. Parses a variable length list of parms on one line
  118. ================
  119. */
  120. void idDeclParticle::ParseParms( idLexer &src, float *parms, int maxParms ) {
  121. idToken token;
  122. memset( parms, 0, maxParms * sizeof( *parms ) );
  123. int count = 0;
  124. while( 1 ) {
  125. if ( !src.ReadTokenOnLine( &token ) ) {
  126. return;
  127. }
  128. if ( count == maxParms ) {
  129. src.Error( "too many parms on line" );
  130. return;
  131. }
  132. token.StripQuotes();
  133. parms[count] = atof( token );
  134. count++;
  135. }
  136. }
  137. /*
  138. ================
  139. idDeclParticle::ParseParametric
  140. ================
  141. */
  142. void idDeclParticle::ParseParametric( idLexer &src, idParticleParm *parm ) {
  143. idToken token;
  144. parm->table = NULL;
  145. parm->from = parm->to = 0.0f;
  146. if ( !src.ReadToken( &token ) ) {
  147. src.Error( "not enough parameters" );
  148. return;
  149. }
  150. if ( token.IsNumeric() ) {
  151. // can have a to + 2nd parm
  152. parm->from = parm->to = atof( token );
  153. if ( src.ReadToken( &token ) ) {
  154. if ( !token.Icmp( "to" ) ) {
  155. if ( !src.ReadToken( &token ) ) {
  156. src.Error( "missing second parameter" );
  157. return;
  158. }
  159. parm->to = atof( token );
  160. } else {
  161. src.UnreadToken( &token );
  162. }
  163. }
  164. } else {
  165. // table
  166. parm->table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token, false ) );
  167. }
  168. }
  169. /*
  170. ================
  171. idDeclParticle::ParseParticleStage
  172. ================
  173. */
  174. idParticleStage *idDeclParticle::ParseParticleStage( idLexer &src ) {
  175. idToken token;
  176. idParticleStage *stage = new idParticleStage;
  177. stage->Default();
  178. while (1) {
  179. if ( src.HadError() ) {
  180. break;
  181. }
  182. if ( !src.ReadToken( &token ) ) {
  183. break;
  184. }
  185. if ( !token.Icmp( "}" ) ) {
  186. break;
  187. }
  188. if ( !token.Icmp( "material" ) ) {
  189. src.ReadToken( &token );
  190. stage->material = declManager->FindMaterial( token.c_str() );
  191. continue;
  192. }
  193. if ( !token.Icmp( "count" ) ) {
  194. stage->totalParticles = src.ParseInt();
  195. continue;
  196. }
  197. if ( !token.Icmp( "time" ) ) {
  198. stage->particleLife = src.ParseFloat();
  199. continue;
  200. }
  201. if ( !token.Icmp( "cycles" ) ) {
  202. stage->cycles = src.ParseFloat();
  203. continue;
  204. }
  205. if ( !token.Icmp( "timeOffset" ) ) {
  206. stage->timeOffset = src.ParseFloat();
  207. continue;
  208. }
  209. if ( !token.Icmp( "deadTime" ) ) {
  210. stage->deadTime = src.ParseFloat();
  211. continue;
  212. }
  213. if ( !token.Icmp( "randomDistribution" ) ) {
  214. stage->randomDistribution = src.ParseBool();
  215. continue;
  216. }
  217. if ( !token.Icmp( "bunching" ) ) {
  218. stage->spawnBunching = src.ParseFloat();
  219. continue;
  220. }
  221. if ( !token.Icmp( "distribution" ) ) {
  222. src.ReadToken( &token );
  223. if ( !token.Icmp( "rect" ) ) {
  224. stage->distributionType = PDIST_RECT;
  225. } else if ( !token.Icmp( "cylinder" ) ) {
  226. stage->distributionType = PDIST_CYLINDER;
  227. } else if ( !token.Icmp( "sphere" ) ) {
  228. stage->distributionType = PDIST_SPHERE;
  229. } else {
  230. src.Error( "bad distribution type: %s\n", token.c_str() );
  231. }
  232. ParseParms( src, stage->distributionParms, sizeof( stage->distributionParms ) / sizeof( stage->distributionParms[0] ) );
  233. continue;
  234. }
  235. if ( !token.Icmp( "direction" ) ) {
  236. src.ReadToken( &token );
  237. if ( !token.Icmp( "cone" ) ) {
  238. stage->directionType = PDIR_CONE;
  239. } else if ( !token.Icmp( "outward" ) ) {
  240. stage->directionType = PDIR_OUTWARD;
  241. } else {
  242. src.Error( "bad direction type: %s\n", token.c_str() );
  243. }
  244. ParseParms( src, stage->directionParms, sizeof( stage->directionParms ) / sizeof( stage->directionParms[0] ) );
  245. continue;
  246. }
  247. if ( !token.Icmp( "orientation" ) ) {
  248. src.ReadToken( &token );
  249. if ( !token.Icmp( "view" ) ) {
  250. stage->orientation = POR_VIEW;
  251. } else if ( !token.Icmp( "aimed" ) ) {
  252. stage->orientation = POR_AIMED;
  253. } else if ( !token.Icmp( "x" ) ) {
  254. stage->orientation = POR_X;
  255. } else if ( !token.Icmp( "y" ) ) {
  256. stage->orientation = POR_Y;
  257. } else if ( !token.Icmp( "z" ) ) {
  258. stage->orientation = POR_Z;
  259. } else {
  260. src.Error( "bad orientation type: %s\n", token.c_str() );
  261. }
  262. ParseParms( src, stage->orientationParms, sizeof( stage->orientationParms ) / sizeof( stage->orientationParms[0] ) );
  263. continue;
  264. }
  265. if ( !token.Icmp( "customPath" ) ) {
  266. src.ReadToken( &token );
  267. if ( !token.Icmp( "standard" ) ) {
  268. stage->customPathType = PPATH_STANDARD;
  269. } else if ( !token.Icmp( "helix" ) ) {
  270. stage->customPathType = PPATH_HELIX;
  271. } else if ( !token.Icmp( "flies" ) ) {
  272. stage->customPathType = PPATH_FLIES;
  273. } else if ( !token.Icmp( "spherical" ) ) {
  274. stage->customPathType = PPATH_ORBIT;
  275. } else {
  276. src.Error( "bad path type: %s\n", token.c_str() );
  277. }
  278. ParseParms( src, stage->customPathParms, sizeof( stage->customPathParms ) / sizeof( stage->customPathParms[0] ) );
  279. continue;
  280. }
  281. if ( !token.Icmp( "speed" ) ) {
  282. ParseParametric( src, &stage->speed );
  283. continue;
  284. }
  285. if ( !token.Icmp( "rotation" ) ) {
  286. ParseParametric( src, &stage->rotationSpeed );
  287. continue;
  288. }
  289. if ( !token.Icmp( "angle" ) ) {
  290. stage->initialAngle = src.ParseFloat();
  291. continue;
  292. }
  293. if ( !token.Icmp( "entityColor" ) ) {
  294. stage->entityColor = src.ParseBool();
  295. continue;
  296. }
  297. if ( !token.Icmp( "size" ) ) {
  298. ParseParametric( src, &stage->size );
  299. continue;
  300. }
  301. if ( !token.Icmp( "aspect" ) ) {
  302. ParseParametric( src, &stage->aspect );
  303. continue;
  304. }
  305. if ( !token.Icmp( "fadeIn" ) ) {
  306. stage->fadeInFraction = src.ParseFloat();
  307. continue;
  308. }
  309. if ( !token.Icmp( "fadeOut" ) ) {
  310. stage->fadeOutFraction = src.ParseFloat();
  311. continue;
  312. }
  313. if ( !token.Icmp( "fadeIndex" ) ) {
  314. stage->fadeIndexFraction = src.ParseFloat();
  315. continue;
  316. }
  317. if ( !token.Icmp( "color" ) ) {
  318. stage->color[0] = src.ParseFloat();
  319. stage->color[1] = src.ParseFloat();
  320. stage->color[2] = src.ParseFloat();
  321. stage->color[3] = src.ParseFloat();
  322. continue;
  323. }
  324. if ( !token.Icmp( "fadeColor" ) ) {
  325. stage->fadeColor[0] = src.ParseFloat();
  326. stage->fadeColor[1] = src.ParseFloat();
  327. stage->fadeColor[2] = src.ParseFloat();
  328. stage->fadeColor[3] = src.ParseFloat();
  329. continue;
  330. }
  331. if ( !token.Icmp("offset" ) ) {
  332. stage->offset[0] = src.ParseFloat();
  333. stage->offset[1] = src.ParseFloat();
  334. stage->offset[2] = src.ParseFloat();
  335. continue;
  336. }
  337. if ( !token.Icmp( "animationFrames" ) ) {
  338. stage->animationFrames = src.ParseInt();
  339. continue;
  340. }
  341. if ( !token.Icmp( "animationRate" ) ) {
  342. stage->animationRate = src.ParseFloat();
  343. continue;
  344. }
  345. if ( !token.Icmp( "boundsExpansion" ) ) {
  346. stage->boundsExpansion = src.ParseFloat();
  347. continue;
  348. }
  349. if ( !token.Icmp( "gravity" ) ) {
  350. src.ReadToken( &token );
  351. if ( !token.Icmp( "world" ) ) {
  352. stage->worldGravity = true;
  353. } else {
  354. src.UnreadToken( &token );
  355. }
  356. stage->gravity = src.ParseFloat();
  357. continue;
  358. }
  359. src.Error( "unknown token %s\n", token.c_str() );
  360. }
  361. // derive values
  362. stage->cycleMsec = ( stage->particleLife + stage->deadTime ) * 1000;
  363. return stage;
  364. }
  365. /*
  366. ================
  367. idDeclParticle::Parse
  368. ================
  369. */
  370. bool idDeclParticle::Parse( const char *text, const int textLength ) {
  371. idLexer src;
  372. idToken token;
  373. src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
  374. src.SetFlags( DECL_LEXER_FLAGS );
  375. src.SkipUntilString( "{" );
  376. depthHack = 0.0f;
  377. while (1) {
  378. if ( !src.ReadToken( &token ) ) {
  379. break;
  380. }
  381. if ( !token.Icmp( "}" ) ) {
  382. break;
  383. }
  384. if ( !token.Icmp( "{" ) ) {
  385. idParticleStage *stage = ParseParticleStage( src );
  386. if ( !stage ) {
  387. src.Warning( "Particle stage parse failed" );
  388. MakeDefault();
  389. return false;
  390. }
  391. stages.Append( stage );
  392. continue;
  393. }
  394. if ( !token.Icmp( "depthHack" ) ) {
  395. depthHack = src.ParseFloat();
  396. continue;
  397. }
  398. src.Warning( "bad token %s", token.c_str() );
  399. MakeDefault();
  400. return false;
  401. }
  402. //
  403. // calculate the bounds
  404. //
  405. bounds.Clear();
  406. for( int i = 0; i < stages.Num(); i++ ) {
  407. GetStageBounds( stages[i] );
  408. bounds.AddBounds( stages[i]->bounds );
  409. }
  410. if ( bounds.GetVolume() <= 0.1f ) {
  411. bounds = idBounds( vec3_origin ).Expand( 8.0f );
  412. }
  413. return true;
  414. }
  415. /*
  416. ================
  417. idDeclParticle::FreeData
  418. ================
  419. */
  420. void idDeclParticle::FreeData( void ) {
  421. stages.DeleteContents( true );
  422. }
  423. /*
  424. ================
  425. idDeclParticle::DefaultDefinition
  426. ================
  427. */
  428. const char *idDeclParticle::DefaultDefinition( void ) const {
  429. return
  430. "{\n"
  431. "\t" "{\n"
  432. "\t\t" "material\t_default\n"
  433. "\t\t" "count\t20\n"
  434. "\t\t" "time\t\t1.0\n"
  435. "\t" "}\n"
  436. "}";
  437. }
  438. /*
  439. ================
  440. idDeclParticle::WriteParticleParm
  441. ================
  442. */
  443. void idDeclParticle::WriteParticleParm( idFile *f, idParticleParm *parm, const char *name ) {
  444. f->WriteFloatString( "\t\t%s\t\t\t\t ", name );
  445. if ( parm->table ) {
  446. f->WriteFloatString( "%s\n", parm->table->GetName() );
  447. } else {
  448. f->WriteFloatString( "\"%.3f\" ", parm->from );
  449. if ( parm->from == parm->to ) {
  450. f->WriteFloatString( "\n" );
  451. } else {
  452. f->WriteFloatString( " to \"%.3f\"\n", parm->to );
  453. }
  454. }
  455. }
  456. /*
  457. ================
  458. idDeclParticle::WriteStage
  459. ================
  460. */
  461. void idDeclParticle::WriteStage( idFile *f, idParticleStage *stage ) {
  462. int i;
  463. f->WriteFloatString( "\t{\n" );
  464. f->WriteFloatString( "\t\tcount\t\t\t\t%i\n", stage->totalParticles );
  465. f->WriteFloatString( "\t\tmaterial\t\t\t%s\n", stage->material->GetName() );
  466. if ( stage->animationFrames ) {
  467. f->WriteFloatString( "\t\tanimationFrames \t%i\n", stage->animationFrames );
  468. }
  469. if ( stage->animationRate ) {
  470. f->WriteFloatString( "\t\tanimationRate \t\t%.3f\n", stage->animationRate );
  471. }
  472. f->WriteFloatString( "\t\ttime\t\t\t\t%.3f\n", stage->particleLife );
  473. f->WriteFloatString( "\t\tcycles\t\t\t\t%.3f\n", stage->cycles );
  474. if ( stage->timeOffset ) {
  475. f->WriteFloatString( "\t\ttimeOffset\t\t\t%.3f\n", stage->timeOffset );
  476. }
  477. if ( stage->deadTime ) {
  478. f->WriteFloatString( "\t\tdeadTime\t\t\t%.3f\n", stage->deadTime );
  479. }
  480. f->WriteFloatString( "\t\tbunching\t\t\t%.3f\n", stage->spawnBunching );
  481. f->WriteFloatString( "\t\tdistribution\t\t%s ", ParticleDistributionDesc[stage->distributionType].name );
  482. for ( i = 0; i < ParticleDistributionDesc[stage->distributionType].count; i++ ) {
  483. f->WriteFloatString( "%.3f ", stage->distributionParms[i] );
  484. }
  485. f->WriteFloatString( "\n" );
  486. f->WriteFloatString( "\t\tdirection\t\t\t%s ", ParticleDirectionDesc[stage->directionType].name );
  487. for ( i = 0; i < ParticleDirectionDesc[stage->directionType].count; i++ ) {
  488. f->WriteFloatString( "\"%.3f\" ", stage->directionParms[i] );
  489. }
  490. f->WriteFloatString( "\n" );
  491. f->WriteFloatString( "\t\torientation\t\t\t%s ", ParticleOrientationDesc[stage->orientation].name );
  492. for ( i = 0; i < ParticleOrientationDesc[stage->orientation].count; i++ ) {
  493. f->WriteFloatString( "%.3f ", stage->orientationParms[i] );
  494. }
  495. f->WriteFloatString( "\n" );
  496. if ( stage->customPathType != PPATH_STANDARD ) {
  497. f->WriteFloatString( "\t\tcustomPath %s ", ParticleCustomDesc[stage->customPathType].name );
  498. for ( i = 0; i < ParticleCustomDesc[stage->customPathType].count; i++ ) {
  499. f->WriteFloatString( "%.3f ", stage->customPathParms[i] );
  500. }
  501. f->WriteFloatString( "\n" );
  502. }
  503. if ( stage->entityColor ) {
  504. f->WriteFloatString( "\t\tentityColor\t\t\t1\n" );
  505. }
  506. WriteParticleParm( f, &stage->speed, "speed" );
  507. WriteParticleParm( f, &stage->size, "size" );
  508. WriteParticleParm( f, &stage->aspect, "aspect" );
  509. if ( stage->rotationSpeed.from ) {
  510. WriteParticleParm( f, &stage->rotationSpeed, "rotation" );
  511. }
  512. if ( stage->initialAngle ) {
  513. f->WriteFloatString( "\t\tangle\t\t\t\t%.3f\n", stage->initialAngle );
  514. }
  515. f->WriteFloatString( "\t\trandomDistribution\t\t\t\t%i\n", static_cast<int>( stage->randomDistribution ) );
  516. f->WriteFloatString( "\t\tboundsExpansion\t\t\t\t%.3f\n", stage->boundsExpansion );
  517. f->WriteFloatString( "\t\tfadeIn\t\t\t\t%.3f\n", stage->fadeInFraction );
  518. f->WriteFloatString( "\t\tfadeOut\t\t\t\t%.3f\n", stage->fadeOutFraction );
  519. f->WriteFloatString( "\t\tfadeIndex\t\t\t\t%.3f\n", stage->fadeIndexFraction );
  520. 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 );
  521. f->WriteFloatString( "\t\tfadeColor \t\t\t%.3f %.3f %.3f %.3f\n", stage->fadeColor.x, stage->fadeColor.y, stage->fadeColor.z, stage->fadeColor.w );
  522. f->WriteFloatString( "\t\toffset \t\t\t\t%.3f %.3f %.3f\n", stage->offset.x, stage->offset.y, stage->offset.z );
  523. f->WriteFloatString( "\t\tgravity \t\t\t" );
  524. if ( stage->worldGravity ) {
  525. f->WriteFloatString( "world " );
  526. }
  527. f->WriteFloatString( "%.3f\n", stage->gravity );
  528. f->WriteFloatString( "\t}\n" );
  529. }
  530. /*
  531. ================
  532. idDeclParticle::RebuildTextSource
  533. ================
  534. */
  535. bool idDeclParticle::RebuildTextSource( void ) {
  536. idFile_Memory f;
  537. f.WriteFloatString("\n\n/*\n"
  538. "\tGenerated by the Particle Editor.\n"
  539. "\tTo use the particle editor, launch the game and type 'editParticles' on the console.\n"
  540. "*/\n" );
  541. f.WriteFloatString( "particle %s {\n", GetName() );
  542. if ( depthHack ) {
  543. f.WriteFloatString( "\tdepthHack\t%f\n", depthHack );
  544. }
  545. for ( int i = 0; i < stages.Num(); i++ ) {
  546. WriteStage( &f, stages[i] );
  547. }
  548. f.WriteFloatString( "}" );
  549. SetText( f.GetDataPtr() );
  550. return true;
  551. }
  552. /*
  553. ================
  554. idDeclParticle::Save
  555. ================
  556. */
  557. bool idDeclParticle::Save( const char *fileName ) {
  558. RebuildTextSource();
  559. if ( fileName ) {
  560. declManager->CreateNewDecl( DECL_PARTICLE, GetName(), fileName );
  561. }
  562. ReplaceSourceFileText();
  563. return true;
  564. }
  565. /*
  566. ====================================================================================
  567. idParticleParm
  568. ====================================================================================
  569. */
  570. float idParticleParm::Eval( float frac, idRandom &rand ) const {
  571. if ( table ) {
  572. return table->TableLookup( frac );
  573. }
  574. return from + frac * ( to - from );
  575. }
  576. float idParticleParm::Integrate( float frac, idRandom &rand ) const {
  577. if ( table ) {
  578. common->Printf( "idParticleParm::Integrate: can't integrate tables\n" );
  579. return 0;
  580. }
  581. return ( from + frac * ( to - from ) * 0.5f ) * frac;
  582. }
  583. /*
  584. ====================================================================================
  585. idParticleStage
  586. ====================================================================================
  587. */
  588. /*
  589. ================
  590. idParticleStage::idParticleStage
  591. ================
  592. */
  593. idParticleStage::idParticleStage( void ) {
  594. material = NULL;
  595. totalParticles = 0;
  596. cycles = 0.0f;
  597. cycleMsec = 0;
  598. spawnBunching = 0.0f;
  599. particleLife = 0.0f;
  600. timeOffset = 0.0f;
  601. deadTime = 0.0f;
  602. distributionType = PDIST_RECT;
  603. distributionParms[0] = distributionParms[1] = distributionParms[2] = distributionParms[3] = 0.0f;
  604. directionType = PDIR_CONE;
  605. directionParms[0] = directionParms[1] = directionParms[2] = directionParms[3] = 0.0f;
  606. // idParticleParm speed;
  607. gravity = 0.0f;
  608. worldGravity = false;
  609. customPathType = PPATH_STANDARD;
  610. customPathParms[0] = customPathParms[1] = customPathParms[2] = customPathParms[3] = 0.0f;
  611. customPathParms[4] = customPathParms[5] = customPathParms[6] = customPathParms[7] = 0.0f;
  612. offset.Zero();
  613. animationFrames = 0;
  614. animationRate = 0.0f;
  615. randomDistribution = true;
  616. entityColor = false;
  617. initialAngle = 0.0f;
  618. // idParticleParm rotationSpeed;
  619. orientation = POR_VIEW;
  620. orientationParms[0] = orientationParms[1] = orientationParms[2] = orientationParms[3] = 0.0f;
  621. // idParticleParm size
  622. // idParticleParm aspect
  623. color.Zero();
  624. fadeColor.Zero();
  625. fadeInFraction = 0.0f;
  626. fadeOutFraction = 0.0f;
  627. fadeIndexFraction = 0.0f;
  628. hidden = false;
  629. boundsExpansion = 0.0f;
  630. bounds.Clear();
  631. }
  632. /*
  633. ================
  634. idParticleStage::Default
  635. Sets the stage to a default state
  636. ================
  637. */
  638. void idParticleStage::Default() {
  639. material = declManager->FindMaterial( "_default" );
  640. totalParticles = 100;
  641. spawnBunching = 1.0f;
  642. particleLife = 1.5f;
  643. timeOffset = 0.0f;
  644. deadTime = 0.0f;
  645. distributionType = PDIST_RECT;
  646. distributionParms[0] = 8.0f;
  647. distributionParms[1] = 8.0f;
  648. distributionParms[2] = 8.0f;
  649. distributionParms[3] = 0.0f;
  650. directionType = PDIR_CONE;
  651. directionParms[0] = 90.0f;
  652. directionParms[1] = 0.0f;
  653. directionParms[2] = 0.0f;
  654. directionParms[3] = 0.0f;
  655. orientation = POR_VIEW;
  656. orientationParms[0] = 0.0f;
  657. orientationParms[1] = 0.0f;
  658. orientationParms[2] = 0.0f;
  659. orientationParms[3] = 0.0f;
  660. speed.from = 150.0f;
  661. speed.to = 150.0f;
  662. speed.table = NULL;
  663. gravity = 1.0f;
  664. worldGravity = false;
  665. customPathType = PPATH_STANDARD;
  666. customPathParms[0] = 0.0f;
  667. customPathParms[1] = 0.0f;
  668. customPathParms[2] = 0.0f;
  669. customPathParms[3] = 0.0f;
  670. customPathParms[4] = 0.0f;
  671. customPathParms[5] = 0.0f;
  672. customPathParms[6] = 0.0f;
  673. customPathParms[7] = 0.0f;
  674. offset.Zero();
  675. animationFrames = 0;
  676. animationRate = 0.0f;
  677. initialAngle = 0.0f;
  678. rotationSpeed.from = 0.0f;
  679. rotationSpeed.to = 0.0f;
  680. rotationSpeed.table = NULL;
  681. size.from = 4.0f;
  682. size.to = 4.0f;
  683. size.table = NULL;
  684. aspect.from = 1.0f;
  685. aspect.to = 1.0f;
  686. aspect.table = NULL;
  687. color.x = 1.0f;
  688. color.y = 1.0f;
  689. color.z = 1.0f;
  690. color.w = 1.0f;
  691. fadeColor.x = 0.0f;
  692. fadeColor.y = 0.0f;
  693. fadeColor.z = 0.0f;
  694. fadeColor.w = 0.0f;
  695. fadeInFraction = 0.1f;
  696. fadeOutFraction = 0.25f;
  697. fadeIndexFraction = 0.0f;
  698. boundsExpansion = 0.0f;
  699. randomDistribution = true;
  700. entityColor = false;
  701. cycleMsec = ( particleLife + deadTime ) * 1000;
  702. }
  703. /*
  704. ================
  705. idParticleStage::NumQuadsPerParticle
  706. includes trails and cross faded animations
  707. ================
  708. */
  709. int idParticleStage::NumQuadsPerParticle() const {
  710. int count = 1;
  711. if ( orientation == POR_AIMED ) {
  712. int trails = idMath::Ftoi( orientationParms[0] );
  713. // each trail stage will add an extra quad
  714. count *= ( 1 + trails );
  715. }
  716. // if we are doing strip-animation, we need to double the number and cross fade them
  717. if ( animationFrames > 1 ) {
  718. count *= 2;
  719. }
  720. return count;
  721. }
  722. /*
  723. ===============
  724. idParticleStage::ParticleOrigin
  725. ===============
  726. */
  727. void idParticleStage::ParticleOrigin( particleGen_t *g, idVec3 &origin ) const {
  728. if ( customPathType == PPATH_STANDARD ) {
  729. //
  730. // find intial origin distribution
  731. //
  732. float radiusSqr, angle1, angle2;
  733. switch( distributionType ) {
  734. case PDIST_RECT: { // ( sizeX sizeY sizeZ )
  735. origin[0] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[0];
  736. origin[1] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[1];
  737. origin[2] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[2];
  738. break;
  739. }
  740. case PDIST_CYLINDER: { // ( sizeX sizeY sizeZ ringFraction )
  741. angle1 = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * idMath::TWO_PI;
  742. idMath::SinCos16( angle1, origin[0], origin[1] );
  743. origin[2] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f );
  744. // reproject points that are inside the ringFraction to the outer band
  745. if ( distributionParms[3] > 0.0f ) {
  746. radiusSqr = origin[0] * origin[0] + origin[1] * origin[1];
  747. if ( radiusSqr < distributionParms[3] * distributionParms[3] ) {
  748. // if we are inside the inner reject zone, rescale to put it out into the good zone
  749. float f = sqrt( radiusSqr ) / distributionParms[3];
  750. float invf = 1.0f / f;
  751. float newRadius = distributionParms[3] + f * ( 1.0f - distributionParms[3] );
  752. float rescale = invf * newRadius;
  753. origin[0] *= rescale;
  754. origin[1] *= rescale;
  755. }
  756. }
  757. origin[0] *= distributionParms[0];
  758. origin[1] *= distributionParms[1];
  759. origin[2] *= distributionParms[2];
  760. break;
  761. }
  762. case PDIST_SPHERE: { // ( sizeX sizeY sizeZ ringFraction )
  763. // iterating with rejection is the only way to get an even distribution over a sphere
  764. if ( randomDistribution ) {
  765. do {
  766. origin[0] = g->random.CRandomFloat();
  767. origin[1] = g->random.CRandomFloat();
  768. origin[2] = g->random.CRandomFloat();
  769. radiusSqr = origin[0] * origin[0] + origin[1] * origin[1] + origin[2] * origin[2];
  770. } while( radiusSqr > 1.0f );
  771. } else {
  772. origin.Set( 1.0f, 1.0f, 1.0f );
  773. radiusSqr = 3.0f;
  774. }
  775. if ( distributionParms[3] > 0.0f ) {
  776. // we could iterate until we got something that also satisfied ringFraction,
  777. // but for narrow rings that could be a lot of work, so reproject inside points instead
  778. if ( radiusSqr < distributionParms[3] * distributionParms[3] ) {
  779. // if we are inside the inner reject zone, rescale to put it out into the good zone
  780. float f = sqrt( radiusSqr ) / distributionParms[3];
  781. float invf = 1.0f / f;
  782. float newRadius = distributionParms[3] + f * ( 1.0f - distributionParms[3] );
  783. float rescale = invf * newRadius;
  784. origin[0] *= rescale;
  785. origin[1] *= rescale;
  786. origin[2] *= rescale;
  787. }
  788. }
  789. origin[0] *= distributionParms[0];
  790. origin[1] *= distributionParms[1];
  791. origin[2] *= distributionParms[2];
  792. break;
  793. }
  794. }
  795. // offset will effect all particle origin types
  796. // add this before the velocity and gravity additions
  797. origin += offset;
  798. //
  799. // add the velocity over time
  800. //
  801. idVec3 dir;
  802. switch( directionType ) {
  803. case PDIR_CONE: {
  804. // angle is the full angle, so 360 degrees is any spherical direction
  805. angle1 = g->random.CRandomFloat() * directionParms[0] * idMath::M_DEG2RAD;
  806. angle2 = g->random.CRandomFloat() * idMath::PI;
  807. float s1, c1, s2, c2;
  808. idMath::SinCos16( angle1, s1, c1 );
  809. idMath::SinCos16( angle2, s2, c2 );
  810. dir[0] = s1 * c2;
  811. dir[1] = s1 * s2;
  812. dir[2] = c1;
  813. break;
  814. }
  815. case PDIR_OUTWARD: {
  816. dir = origin;
  817. dir.Normalize();
  818. dir[2] += directionParms[0];
  819. break;
  820. }
  821. }
  822. // add speed
  823. float iSpeed = speed.Integrate( g->frac, g->random );
  824. origin += dir * iSpeed * particleLife;
  825. } else {
  826. //
  827. // custom paths completely override both the origin and velocity calculations, but still
  828. // use the standard gravity
  829. //
  830. float angle1, angle2, speed1, speed2;
  831. switch( customPathType ) {
  832. case PPATH_HELIX: { // ( sizeX sizeY sizeZ radialSpeed axialSpeed )
  833. speed1 = g->random.CRandomFloat();
  834. speed2 = g->random.CRandomFloat();
  835. angle1 = g->random.RandomFloat() * idMath::TWO_PI + customPathParms[3] * speed1 * g->age;
  836. float s1, c1;
  837. idMath::SinCos16( angle1, s1, c1 );
  838. origin[0] = c1 * customPathParms[0];
  839. origin[1] = s1 * customPathParms[1];
  840. origin[2] = g->random.RandomFloat() * customPathParms[2] + customPathParms[4] * speed2 * g->age;
  841. break;
  842. }
  843. case PPATH_FLIES: { // ( radialSpeed axialSpeed size )
  844. speed1 = idMath::ClampFloat( 0.4f, 1.0f, g->random.CRandomFloat() );
  845. speed2 = idMath::ClampFloat( 0.4f, 1.0f, g->random.CRandomFloat() );
  846. angle1 = g->random.RandomFloat() * idMath::PI * 2 + customPathParms[0] * speed1 * g->age;
  847. angle2 = g->random.RandomFloat() * idMath::PI * 2 + customPathParms[1] * speed1 * g->age;
  848. float s1, c1, s2, c2;
  849. idMath::SinCos16( angle1, s1, c1 );
  850. idMath::SinCos16( angle2, s2, c2 );
  851. origin[0] = c1 * c2;
  852. origin[1] = s1 * c2;
  853. origin[2] = -s2;
  854. origin *= customPathParms[2];
  855. break;
  856. }
  857. case PPATH_ORBIT: { // ( radius speed axis )
  858. angle1 = g->random.RandomFloat() * idMath::TWO_PI + customPathParms[1] * g->age;
  859. float s1, c1;
  860. idMath::SinCos16( angle1, s1, c1 );
  861. origin[0] = c1 * customPathParms[0];
  862. origin[1] = s1 * customPathParms[0];
  863. origin.ProjectSelfOntoSphere( customPathParms[0] );
  864. break;
  865. }
  866. case PPATH_DRIP: { // ( speed )
  867. origin[0] = 0.0f;
  868. origin[1] = 0.0f;
  869. origin[2] = -( g->age * customPathParms[0] );
  870. break;
  871. }
  872. default: {
  873. common->Error( "idParticleStage::ParticleOrigin: bad customPathType" );
  874. }
  875. }
  876. origin += offset;
  877. }
  878. // adjust for the per-particle smoke offset
  879. origin *= g->axis;
  880. origin += g->origin;
  881. // add gravity after adjusting for axis
  882. if ( worldGravity ) {
  883. idVec3 gra( 0, 0, -gravity );
  884. gra *= g->renderEnt->axis.Transpose();
  885. origin += gra * g->age * g->age;
  886. } else {
  887. origin[2] -= gravity * g->age * g->age;
  888. }
  889. }
  890. /*
  891. ==================
  892. idParticleStage::ParticleVerts
  893. ==================
  894. */
  895. int idParticleStage::ParticleVerts( particleGen_t *g, idVec3 origin, idDrawVert *verts ) const {
  896. float psize = size.Eval( g->frac, g->random );
  897. float paspect = aspect.Eval( g->frac, g->random );
  898. float width = psize;
  899. float height = psize * paspect;
  900. idVec3 left, up;
  901. if ( orientation == POR_AIMED ) {
  902. // reset the values to an earlier time to get a previous origin
  903. idRandom currentRandom = g->random;
  904. float currentAge = g->age;
  905. float currentFrac = g->frac;
  906. idDrawVert *verts_p = verts;
  907. idVec3 stepOrigin = origin;
  908. idVec3 stepLeft;
  909. int numTrails = idMath::Ftoi( orientationParms[0] );
  910. float trailTime = orientationParms[1];
  911. if ( trailTime == 0 ) {
  912. trailTime = 0.5f;
  913. }
  914. float height = 1.0f / ( 1 + numTrails );
  915. float t = 0;
  916. for ( int i = 0 ; i <= numTrails ; i++ ) {
  917. g->random = g->originalRandom;
  918. g->age = currentAge - ( i + 1 ) * trailTime / ( numTrails + 1 ); // time to back up
  919. g->frac = g->age / particleLife;
  920. idVec3 oldOrigin;
  921. ParticleOrigin( g, oldOrigin );
  922. up = stepOrigin - oldOrigin; // along the direction of travel
  923. idVec3 forwardDir;
  924. g->renderEnt->axis.ProjectVector( g->renderView->viewaxis[0], forwardDir );
  925. up -= ( up * forwardDir ) * forwardDir;
  926. up.Normalize();
  927. left = up.Cross( forwardDir );
  928. left *= psize;
  929. verts_p[0] = verts[0];
  930. verts_p[1] = verts[1];
  931. verts_p[2] = verts[2];
  932. verts_p[3] = verts[3];
  933. if ( i == 0 ) {
  934. verts_p[0].xyz = stepOrigin - left;
  935. verts_p[1].xyz = stepOrigin + left;
  936. } else {
  937. verts_p[0].xyz = stepOrigin - stepLeft;
  938. verts_p[1].xyz = stepOrigin + stepLeft;
  939. }
  940. verts_p[2].xyz = oldOrigin - left;
  941. verts_p[3].xyz = oldOrigin + left;
  942. // modify texcoords
  943. verts_p[0].st[0] = verts[0].st[0];
  944. verts_p[0].st[1] = t;
  945. verts_p[1].st[0] = verts[1].st[0];
  946. verts_p[1].st[1] = t;
  947. verts_p[2].st[0] = verts[2].st[0];
  948. verts_p[2].st[1] = t+height;
  949. verts_p[3].st[0] = verts[3].st[0];
  950. verts_p[3].st[1] = t+height;
  951. t += height;
  952. verts_p += 4;
  953. stepOrigin = oldOrigin;
  954. stepLeft = left;
  955. }
  956. g->random = currentRandom;
  957. g->age = currentAge;
  958. g->frac = currentFrac;
  959. return 4 * (numTrails+1);
  960. }
  961. //
  962. // constant rotation
  963. //
  964. float angle;
  965. angle = ( initialAngle ) ? initialAngle : 360 * g->random.RandomFloat();
  966. float angleMove = rotationSpeed.Integrate( g->frac, g->random ) * particleLife;
  967. // have hald the particles rotate each way
  968. if ( g->index & 1 ) {
  969. angle += angleMove;
  970. } else {
  971. angle -= angleMove;
  972. }
  973. angle = angle / 180 * idMath::PI;
  974. float c = idMath::Cos16( angle );
  975. float s = idMath::Sin16( angle );
  976. if ( orientation == POR_Z ) {
  977. // oriented in entity space
  978. left[0] = s;
  979. left[1] = c;
  980. left[2] = 0;
  981. up[0] = c;
  982. up[1] = -s;
  983. up[2] = 0;
  984. } else if ( orientation == POR_X ) {
  985. // oriented in entity space
  986. left[0] = 0;
  987. left[1] = c;
  988. left[2] = s;
  989. up[0] = 0;
  990. up[1] = -s;
  991. up[2] = c;
  992. } else if ( orientation == POR_Y ) {
  993. // oriented in entity space
  994. left[0] = c;
  995. left[1] = 0;
  996. left[2] = s;
  997. up[0] = -s;
  998. up[1] = 0;
  999. up[2] = c;
  1000. } else {
  1001. // oriented in viewer space
  1002. idVec3 entityLeft, entityUp;
  1003. g->renderEnt->axis.ProjectVector( g->renderView->viewaxis[1], entityLeft );
  1004. g->renderEnt->axis.ProjectVector( g->renderView->viewaxis[2], entityUp );
  1005. left = entityLeft * c + entityUp * s;
  1006. up = entityUp * c - entityLeft * s;
  1007. }
  1008. left *= width;
  1009. up *= height;
  1010. verts[0].xyz = origin - left + up;
  1011. verts[1].xyz = origin + left + up;
  1012. verts[2].xyz = origin - left - up;
  1013. verts[3].xyz = origin + left - up;
  1014. return 4;
  1015. }
  1016. /*
  1017. ==================
  1018. idParticleStage::ParticleTexCoords
  1019. ==================
  1020. */
  1021. void idParticleStage::ParticleTexCoords( particleGen_t *g, idDrawVert *verts ) const {
  1022. float s, width;
  1023. float t, height;
  1024. if ( animationFrames > 1 ) {
  1025. width = 1.0f / animationFrames;
  1026. float floatFrame;
  1027. if ( animationRate ) {
  1028. // explicit, cycling animation
  1029. floatFrame = g->age * animationRate;
  1030. } else {
  1031. // single animation cycle over the life of the particle
  1032. floatFrame = g->frac * animationFrames;
  1033. }
  1034. int intFrame = (int)floatFrame;
  1035. g->animationFrameFrac = floatFrame - intFrame;
  1036. s = width * intFrame;
  1037. } else {
  1038. s = 0.0f;
  1039. width = 1.0f;
  1040. }
  1041. t = 0.0f;
  1042. height = 1.0f;
  1043. verts[0].st[0] = s;
  1044. verts[0].st[1] = t;
  1045. verts[1].st[0] = s+width;
  1046. verts[1].st[1] = t;
  1047. verts[2].st[0] = s;
  1048. verts[2].st[1] = t+height;
  1049. verts[3].st[0] = s+width;
  1050. verts[3].st[1] = t+height;
  1051. }
  1052. /*
  1053. ==================
  1054. idParticleStage::ParticleColors
  1055. ==================
  1056. */
  1057. void idParticleStage::ParticleColors( particleGen_t *g, idDrawVert *verts ) const {
  1058. float fadeFraction = 1.0f;
  1059. // most particles fade in at the beginning and fade out at the end
  1060. if ( g->frac < fadeInFraction ) {
  1061. fadeFraction *= ( g->frac / fadeInFraction );
  1062. }
  1063. if ( 1.0f - g->frac < fadeOutFraction ) {
  1064. fadeFraction *= ( ( 1.0f - g->frac ) / fadeOutFraction );
  1065. }
  1066. // individual gun smoke particles get more and more faded as the
  1067. // cycle goes on (note that totalParticles won't be correct for a surface-particle deform)
  1068. if ( fadeIndexFraction ) {
  1069. float indexFrac = ( totalParticles - g->index ) / (float)totalParticles;
  1070. if ( indexFrac < fadeIndexFraction ) {
  1071. fadeFraction *= indexFrac / fadeIndexFraction;
  1072. }
  1073. }
  1074. for ( int i = 0 ; i < 4 ; i++ ) {
  1075. float fcolor = ( ( entityColor ) ? g->renderEnt->shaderParms[i] : color[i] ) * fadeFraction + fadeColor[i] * ( 1.0f - fadeFraction );
  1076. int icolor = idMath::FtoiFast( fcolor * 255.0f );
  1077. if ( icolor < 0 ) {
  1078. icolor = 0;
  1079. } else if ( icolor > 255 ) {
  1080. icolor = 255;
  1081. }
  1082. verts[0].color[i] =
  1083. verts[1].color[i] =
  1084. verts[2].color[i] =
  1085. verts[3].color[i] = icolor;
  1086. }
  1087. }
  1088. /*
  1089. ================
  1090. idParticleStage::CreateParticle
  1091. Returns 0 if no particle is created because it is completely faded out
  1092. Returns 4 if a normal quad is created
  1093. Returns 8 if two cross faded quads are created
  1094. Vertex order is:
  1095. 0 1
  1096. 2 3
  1097. ================
  1098. */
  1099. int idParticleStage::CreateParticle( particleGen_t *g, idDrawVert *verts ) const {
  1100. idVec3 origin;
  1101. verts[0].Clear();
  1102. verts[1].Clear();
  1103. verts[2].Clear();
  1104. verts[3].Clear();
  1105. ParticleColors( g, verts );
  1106. // if we are completely faded out, kill the particle
  1107. if ( verts[0].color[0] == 0 && verts[0].color[1] == 0 && verts[0].color[2] == 0 && verts[0].color[3] == 0 ) {
  1108. return 0;
  1109. }
  1110. ParticleOrigin( g, origin );
  1111. ParticleTexCoords( g, verts );
  1112. int numVerts = ParticleVerts( g, origin, verts );
  1113. if ( animationFrames <= 1 ) {
  1114. return numVerts;
  1115. }
  1116. // if we are doing strip-animation, we need to double the quad and cross fade it
  1117. float width = 1.0f / animationFrames;
  1118. float frac = g->animationFrameFrac;
  1119. float iFrac = 1.0f - frac;
  1120. for ( int i = 0 ; i < numVerts ; i++ ) {
  1121. verts[numVerts + i] = verts[i];
  1122. verts[numVerts + i].st[0] += width;
  1123. verts[numVerts + i].color[0] *= frac;
  1124. verts[numVerts + i].color[1] *= frac;
  1125. verts[numVerts + i].color[2] *= frac;
  1126. verts[numVerts + i].color[3] *= frac;
  1127. verts[i].color[0] *= iFrac;
  1128. verts[i].color[1] *= iFrac;
  1129. verts[i].color[2] *= iFrac;
  1130. verts[i].color[3] *= iFrac;
  1131. }
  1132. return numVerts * 2;
  1133. }
  1134. /*
  1135. ==================
  1136. idParticleStage::GetCustomPathName
  1137. ==================
  1138. */
  1139. const char* idParticleStage::GetCustomPathName() {
  1140. int index = ( customPathType < CustomParticleCount ) ? customPathType : 0;
  1141. return ParticleCustomDesc[index].name;
  1142. }
  1143. /*
  1144. ==================
  1145. idParticleStage::GetCustomPathDesc
  1146. ==================
  1147. */
  1148. const char* idParticleStage::GetCustomPathDesc() {
  1149. int index = ( customPathType < CustomParticleCount ) ? customPathType : 0;
  1150. return ParticleCustomDesc[index].desc;
  1151. }
  1152. /*
  1153. ==================
  1154. idParticleStage::NumCustomPathParms
  1155. ==================
  1156. */
  1157. int idParticleStage::NumCustomPathParms() {
  1158. int index = ( customPathType < CustomParticleCount ) ? customPathType : 0;
  1159. return ParticleCustomDesc[index].count;
  1160. }
  1161. /*
  1162. ==================
  1163. idParticleStage::SetCustomPathType
  1164. ==================
  1165. */
  1166. void idParticleStage::SetCustomPathType( const char *p ) {
  1167. customPathType = PPATH_STANDARD;
  1168. for ( int i = 0; i < CustomParticleCount; i ++ ) {
  1169. if ( idStr::Icmp( p, ParticleCustomDesc[i].name ) == 0 ) {
  1170. customPathType = static_cast<prtCustomPth_t>( i );
  1171. break;
  1172. }
  1173. }
  1174. }
  1175. /*
  1176. ==================
  1177. idParticleStage::operator=
  1178. ==================
  1179. */
  1180. void idParticleStage::operator=( const idParticleStage &src ) {
  1181. material = src.material;
  1182. totalParticles = src.totalParticles;
  1183. cycles = src.cycles;
  1184. cycleMsec = src.cycleMsec;
  1185. spawnBunching = src.spawnBunching;
  1186. particleLife = src.particleLife;
  1187. timeOffset = src.timeOffset;
  1188. deadTime = src.deadTime;
  1189. distributionType = src.distributionType;
  1190. distributionParms[0] = src.distributionParms[0];
  1191. distributionParms[1] = src.distributionParms[1];
  1192. distributionParms[2] = src.distributionParms[2];
  1193. distributionParms[3] = src.distributionParms[3];
  1194. directionType = src.directionType;
  1195. directionParms[0] = src.directionParms[0];
  1196. directionParms[1] = src.directionParms[1];
  1197. directionParms[2] = src.directionParms[2];
  1198. directionParms[3] = src.directionParms[3];
  1199. speed = src.speed;
  1200. gravity = src.gravity;
  1201. worldGravity = src.worldGravity;
  1202. randomDistribution = src.randomDistribution;
  1203. entityColor = src.entityColor;
  1204. customPathType = src.customPathType;
  1205. customPathParms[0] = src.customPathParms[0];
  1206. customPathParms[1] = src.customPathParms[1];
  1207. customPathParms[2] = src.customPathParms[2];
  1208. customPathParms[3] = src.customPathParms[3];
  1209. customPathParms[4] = src.customPathParms[4];
  1210. customPathParms[5] = src.customPathParms[5];
  1211. customPathParms[6] = src.customPathParms[6];
  1212. customPathParms[7] = src.customPathParms[7];
  1213. offset = src.offset;
  1214. animationFrames = src.animationFrames;
  1215. animationRate = src.animationRate;
  1216. initialAngle = src.initialAngle;
  1217. rotationSpeed = src.rotationSpeed;
  1218. orientation = src.orientation;
  1219. orientationParms[0] = src.orientationParms[0];
  1220. orientationParms[1] = src.orientationParms[1];
  1221. orientationParms[2] = src.orientationParms[2];
  1222. orientationParms[3] = src.orientationParms[3];
  1223. size = src.size;
  1224. aspect = src.aspect;
  1225. color = src.color;
  1226. fadeColor = src.fadeColor;
  1227. fadeInFraction = src.fadeInFraction;
  1228. fadeOutFraction = src.fadeOutFraction;
  1229. fadeIndexFraction = src.fadeIndexFraction;
  1230. hidden = src.hidden;
  1231. boundsExpansion = src.boundsExpansion;
  1232. bounds = src.bounds;
  1233. }