particle.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "particle.h"
  23. #include "console/consoleTypes.h"
  24. #include "console/typeValidators.h"
  25. #include "core/stream/bitStream.h"
  26. #include "math/mRandom.h"
  27. #include "math/mathIO.h"
  28. #include "console/engineAPI.h"
  29. IMPLEMENT_CO_DATABLOCK_V1( ParticleData );
  30. ConsoleDocClass( ParticleData,
  31. "@brief Contains information for how specific particles should look and react "
  32. "including particle colors, particle imagemap, acceleration value for individual "
  33. "particles and spin information.\n"
  34. "@tsexample\n"
  35. "datablock ParticleData( GLWaterExpSmoke )\n"
  36. "{\n"
  37. " textureName = \"art/shapes/particles/smoke\";\n"
  38. " dragCoefficient = 0.4;\n"
  39. " gravityCoefficient = -0.25;\n"
  40. " inheritedVelFactor = 0.025;\n"
  41. " constantAcceleration = -1.1;\n"
  42. " lifetimeMS = 1250;\n"
  43. " lifetimeVarianceMS = 0;\n"
  44. " useInvAlpha = false;\n"
  45. " spinSpeed = 1;\n"
  46. " spinRandomMin = -200.0;\n"
  47. " spinRandomMax = 200.0;\n\n"
  48. " colors[0] = \"0.1 0.1 1.0 1.0\";\n"
  49. " colors[1] = \"0.4 0.4 1.0 1.0\";\n"
  50. " colors[2] = \"0.4 0.4 1.0 0.0\";\n\n"
  51. " sizes[0] = 2.0;\n"
  52. " sizes[1] = 6.0;\n"
  53. " sizes[2] = 2.0;\n\n"
  54. " times[0] = 0.0;\n"
  55. " times[1] = 0.5;\n"
  56. " times[2] = 1.0;\n"
  57. "};\n"
  58. "@endtsexample\n"
  59. "@ingroup FX\n"
  60. "@see ParticleEmitter\n"
  61. "@see ParticleEmitterData\n"
  62. "@see ParticleEmitterNode\n"
  63. );
  64. static const F32 sgDefaultWindCoefficient = 0.0f;
  65. static const F32 sgDefaultConstantAcceleration = 0.f;
  66. static const F32 sgDefaultSpinSpeed = 1.f;
  67. static const F32 sgDefaultSpinRandomMin = 0.f;
  68. static const F32 sgDefaultSpinRandomMax = 0.f;
  69. //-----------------------------------------------------------------------------
  70. // Constructor
  71. //-----------------------------------------------------------------------------
  72. ParticleData::ParticleData()
  73. {
  74. dragCoefficient = 0.0f;
  75. windCoefficient = sgDefaultWindCoefficient;
  76. gravityCoefficient = 0.0f;
  77. inheritedVelFactor = 0.0f;
  78. constantAcceleration = sgDefaultConstantAcceleration;
  79. lifetimeMS = 1000;
  80. lifetimeVarianceMS = 0;
  81. spinSpeed = sgDefaultSpinSpeed;
  82. spinRandomMin = sgDefaultSpinRandomMin;
  83. spinRandomMax = sgDefaultSpinRandomMax;
  84. useInvAlpha = false;
  85. animateTexture = false;
  86. numFrames = 1;
  87. framesPerSec = numFrames;
  88. S32 i;
  89. for( i=0; i<PDC_NUM_KEYS; i++ )
  90. {
  91. colors[i].set( 1.0, 1.0, 1.0, 1.0 );
  92. sizes[i] = 1.0;
  93. }
  94. times[0] = 0.0f;
  95. times[1] = 0.33f;
  96. times[2] = 0.66f;
  97. times[3] = 1.0f;
  98. texCoords[0].set(0.0,0.0); // texture coords at 4 corners
  99. texCoords[1].set(0.0,1.0); // of particle quad
  100. texCoords[2].set(1.0,1.0); // (defaults to entire particle)
  101. texCoords[3].set(1.0,0.0);
  102. animTexTiling.set(0,0); // tiling dimensions
  103. animTexFramesString = NULL; // string of animation frame indices
  104. animTexUVs = NULL; // array of tile vertex UVs
  105. textureName = NULL; // texture filename
  106. textureHandle = NULL; // loaded texture handle
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Destructor
  110. //-----------------------------------------------------------------------------
  111. ParticleData::~ParticleData()
  112. {
  113. if (animTexUVs)
  114. {
  115. delete [] animTexUVs;
  116. }
  117. }
  118. FRangeValidator dragCoefFValidator(0.f, 5.f);
  119. FRangeValidator gravCoefFValidator(-10.f, 10.f);
  120. FRangeValidator spinRandFValidator(-1000.f, 1000.f);
  121. //-----------------------------------------------------------------------------
  122. // initPersistFields
  123. //-----------------------------------------------------------------------------
  124. void ParticleData::initPersistFields()
  125. {
  126. addFieldV( "dragCoefficient", TYPEID< F32 >(), Offset(dragCoefficient, ParticleData), &dragCoefFValidator,
  127. "Particle physics drag amount." );
  128. addField( "windCoefficient", TYPEID< F32 >(), Offset(windCoefficient, ParticleData),
  129. "Strength of wind on the particles." );
  130. addFieldV( "gravityCoefficient", TYPEID< F32 >(), Offset(gravityCoefficient, ParticleData), &gravCoefFValidator,
  131. "Strength of gravity on the particles." );
  132. addFieldV( "inheritedVelFactor", TYPEID< F32 >(), Offset(inheritedVelFactor, ParticleData), &CommonValidators::NormalizedFloat,
  133. "Amount of emitter velocity to add to particle initial velocity." );
  134. addField( "constantAcceleration", TYPEID< F32 >(), Offset(constantAcceleration, ParticleData),
  135. "Constant acceleration to apply to this particle." );
  136. addField( "lifetimeMS", TYPEID< S32 >(), Offset(lifetimeMS, ParticleData),
  137. "Time in milliseconds before this particle is destroyed." );
  138. addField( "lifetimeVarianceMS", TYPEID< S32 >(), Offset(lifetimeVarianceMS, ParticleData),
  139. "Variance in lifetime of particle, from 0 - lifetimeMS." );
  140. addField( "spinSpeed", TYPEID< F32 >(), Offset(spinSpeed, ParticleData),
  141. "Speed at which to spin the particle." );
  142. addFieldV( "spinRandomMin", TYPEID< F32 >(), Offset(spinRandomMin, ParticleData), &spinRandFValidator,
  143. "Minimum allowed spin speed of this particle, between -1000 and spinRandomMax." );
  144. addFieldV( "spinRandomMax", TYPEID< F32 >(), Offset(spinRandomMax, ParticleData), &spinRandFValidator,
  145. "Maximum allowed spin speed of this particle, between spinRandomMin and 1000." );
  146. addField( "useInvAlpha", TYPEID< bool >(), Offset(useInvAlpha, ParticleData),
  147. "@brief Controls how particles blend with the scene.\n\n"
  148. "If true, particles blend like ParticleBlendStyle NORMAL, if false, "
  149. "blend like ParticleBlendStyle ADDITIVE.\n"
  150. "@note If ParticleEmitterData::blendStyle is set, it will override this value." );
  151. addField( "animateTexture", TYPEID< bool >(), Offset(animateTexture, ParticleData),
  152. "If true, allow the particle texture to be an animated sprite." );
  153. addField( "framesPerSec", TYPEID< S32 >(), Offset(framesPerSec, ParticleData),
  154. "If animateTexture is true, this defines the frames per second of the "
  155. "sprite animation." );
  156. addField( "textureCoords", TYPEID< Point2F >(), Offset(texCoords, ParticleData), 4,
  157. "@brief 4 element array defining the UV coords into textureName to use "
  158. "for this particle.\n\n"
  159. "Coords should be set for the first tile only when using animTexTiling; "
  160. "coordinates for other tiles will be calculated automatically. \"0 0\" is "
  161. "top left and \"1 1\" is bottom right." );
  162. addField( "animTexTiling", TYPEID< Point2I >(), Offset(animTexTiling, ParticleData),
  163. "@brief The number of frames, in rows and columns stored in textureName "
  164. "(when animateTexture is true).\n\n"
  165. "A maximum of 256 frames can be stored in a single texture when using "
  166. "animTexTiling. Value should be \"NumColumns NumRows\", for example \"4 4\"." );
  167. addField( "animTexFrames", TYPEID< StringTableEntry >(), Offset(animTexFramesString,ParticleData),
  168. "@brief A list of frames and/or frame ranges to use for particle "
  169. "animation if animateTexture is true.\n\n"
  170. "Each frame token must be separated by whitespace. A frame token must be "
  171. "a positive integer frame number or a range of frame numbers separated "
  172. "with a '-'. The range separator, '-', cannot have any whitspace around "
  173. "it.\n\n"
  174. "Ranges can be specified to move through the frames in reverse as well "
  175. "as forward (eg. 19-14). Frame numbers exceeding the number of tiles will "
  176. "wrap.\n"
  177. "@tsexample\n"
  178. "animTexFrames = \"0-16 20 19 18 17 31-21\";\n"
  179. "@endtsexample\n" );
  180. addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
  181. "Texture file to use for this particle." );
  182. addField( "animTexName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
  183. "@brief Texture file to use for this particle if animateTexture is true.\n\n"
  184. "Deprecated. Use textureName instead." );
  185. // Interpolation variables
  186. addField( "colors", TYPEID< ColorF >(), Offset(colors, ParticleData), PDC_NUM_KEYS,
  187. "@brief Particle RGBA color keyframe values.\n\n"
  188. "The particle color will linearly interpolate between the color/time keys "
  189. "over the lifetime of the particle." );
  190. addProtectedField( "sizes", TYPEID< F32 >(), Offset(sizes, ParticleData), &protectedSetSizes,
  191. &defaultProtectedGetFn, PDC_NUM_KEYS,
  192. "@brief Particle size keyframe values.\n\n"
  193. "The particle size will linearly interpolate between the size/time keys "
  194. "over the lifetime of the particle." );
  195. addProtectedField( "times", TYPEID< F32 >(), Offset(times, ParticleData), &protectedSetTimes,
  196. &defaultProtectedGetFn, PDC_NUM_KEYS,
  197. "@brief Time keys used with the colors and sizes keyframes.\n\n"
  198. "Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." );
  199. Parent::initPersistFields();
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Pack data
  203. //-----------------------------------------------------------------------------
  204. void ParticleData::packData(BitStream* stream)
  205. {
  206. Parent::packData(stream);
  207. stream->writeFloat(dragCoefficient / 5, 10);
  208. if( stream->writeFlag(windCoefficient != sgDefaultWindCoefficient ) )
  209. stream->write(windCoefficient);
  210. if (stream->writeFlag(gravityCoefficient != 0.0f))
  211. stream->writeSignedFloat(gravityCoefficient / 10, 12);
  212. stream->writeFloat(inheritedVelFactor, 9);
  213. if( stream->writeFlag( constantAcceleration != sgDefaultConstantAcceleration ) )
  214. stream->write(constantAcceleration);
  215. stream->write( lifetimeMS );
  216. stream->write( lifetimeVarianceMS );
  217. if( stream->writeFlag( spinSpeed != sgDefaultSpinSpeed ) )
  218. stream->write(spinSpeed);
  219. if(stream->writeFlag(spinRandomMin != sgDefaultSpinRandomMin || spinRandomMax != sgDefaultSpinRandomMax))
  220. {
  221. stream->writeInt((S32)(spinRandomMin + 1000), 11);
  222. stream->writeInt((S32)(spinRandomMax + 1000), 11);
  223. }
  224. stream->writeFlag(useInvAlpha);
  225. S32 i, count;
  226. // see how many frames there are:
  227. for(count = 0; count < 3; count++)
  228. if(times[count] >= 1)
  229. break;
  230. count++;
  231. stream->writeInt(count-1, 2);
  232. for( i=0; i<count; i++ )
  233. {
  234. stream->writeFloat( colors[i].red, 7);
  235. stream->writeFloat( colors[i].green, 7);
  236. stream->writeFloat( colors[i].blue, 7);
  237. stream->writeFloat( colors[i].alpha, 7);
  238. stream->writeFloat( sizes[i]/MaxParticleSize, 14);
  239. stream->writeFloat( times[i], 8);
  240. }
  241. if (stream->writeFlag(textureName && textureName[0]))
  242. stream->writeString(textureName);
  243. for (i = 0; i < 4; i++)
  244. mathWrite(*stream, texCoords[i]);
  245. if (stream->writeFlag(animateTexture))
  246. {
  247. if (stream->writeFlag(animTexFramesString && animTexFramesString[0]))
  248. {
  249. stream->writeString(animTexFramesString);
  250. }
  251. mathWrite(*stream, animTexTiling);
  252. stream->writeInt(framesPerSec, 8);
  253. }
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Unpack data
  257. //-----------------------------------------------------------------------------
  258. void ParticleData::unpackData(BitStream* stream)
  259. {
  260. Parent::unpackData(stream);
  261. dragCoefficient = stream->readFloat(10) * 5;
  262. if(stream->readFlag())
  263. stream->read(&windCoefficient);
  264. else
  265. windCoefficient = sgDefaultWindCoefficient;
  266. if (stream->readFlag())
  267. gravityCoefficient = stream->readSignedFloat(12)*10;
  268. else
  269. gravityCoefficient = 0.0f;
  270. inheritedVelFactor = stream->readFloat(9);
  271. if(stream->readFlag())
  272. stream->read(&constantAcceleration);
  273. else
  274. constantAcceleration = sgDefaultConstantAcceleration;
  275. stream->read( &lifetimeMS );
  276. stream->read( &lifetimeVarianceMS );
  277. if(stream->readFlag())
  278. stream->read(&spinSpeed);
  279. else
  280. spinSpeed = sgDefaultSpinSpeed;
  281. if(stream->readFlag())
  282. {
  283. spinRandomMin = (F32)(stream->readInt(11) - 1000);
  284. spinRandomMax = (F32)(stream->readInt(11) - 1000);
  285. }
  286. else
  287. {
  288. spinRandomMin = sgDefaultSpinRandomMin;
  289. spinRandomMax = sgDefaultSpinRandomMax;
  290. }
  291. useInvAlpha = stream->readFlag();
  292. S32 i;
  293. S32 count = stream->readInt(2) + 1;
  294. for(i = 0;i < count; i++)
  295. {
  296. colors[i].red = stream->readFloat(7);
  297. colors[i].green = stream->readFloat(7);
  298. colors[i].blue = stream->readFloat(7);
  299. colors[i].alpha = stream->readFloat(7);
  300. sizes[i] = stream->readFloat(14) * MaxParticleSize;
  301. times[i] = stream->readFloat(8);
  302. }
  303. textureName = (stream->readFlag()) ? stream->readSTString() : 0;
  304. for (i = 0; i < 4; i++)
  305. mathRead(*stream, &texCoords[i]);
  306. animateTexture = stream->readFlag();
  307. if (animateTexture)
  308. {
  309. animTexFramesString = (stream->readFlag()) ? stream->readSTString() : 0;
  310. mathRead(*stream, &animTexTiling);
  311. framesPerSec = stream->readInt(8);
  312. }
  313. }
  314. bool ParticleData::protectedSetSizes( void *object, const char *index, const char *data)
  315. {
  316. ParticleData *pData = static_cast<ParticleData*>( object );
  317. F32 val = dAtof(data);
  318. U32 i;
  319. if (!index)
  320. return (val >= 0.f && val <= MaxParticleSize);
  321. else
  322. i = dAtoui(index);
  323. pData->sizes[i] = mClampF( val, 0.f, MaxParticleSize );
  324. return false;
  325. }
  326. bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data)
  327. {
  328. ParticleData *pData = static_cast<ParticleData*>( object );
  329. F32 val = dAtof(data);
  330. U32 i;
  331. if (!index)
  332. return (val >= 0.f && val <= 1.f);
  333. else
  334. i = dAtoui(index);
  335. pData->times[i] = mClampF( val, 0.f, 1.f );
  336. return false;
  337. }
  338. //-----------------------------------------------------------------------------
  339. // onAdd
  340. //-----------------------------------------------------------------------------
  341. bool ParticleData::onAdd()
  342. {
  343. if (Parent::onAdd() == false)
  344. return false;
  345. if (dragCoefficient < 0.0) {
  346. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) drag coeff less than 0", getName());
  347. dragCoefficient = 0.0f;
  348. }
  349. if (lifetimeMS < 1) {
  350. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetime < 1 ms", getName());
  351. lifetimeMS = 1;
  352. }
  353. if (lifetimeVarianceMS >= lifetimeMS) {
  354. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetimeVariance >= lifetime", getName());
  355. lifetimeVarianceMS = lifetimeMS - 1;
  356. }
  357. if (spinSpeed > 1000.f || spinSpeed < -1000.f) {
  358. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinSpeed invalid", getName());
  359. return false;
  360. }
  361. if (spinRandomMin > 1000.f || spinRandomMin < -1000.f) {
  362. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin invalid", getName());
  363. spinRandomMin = -360.0;
  364. return false;
  365. }
  366. if (spinRandomMin > spinRandomMax) {
  367. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin greater than spinRandomMax", getName());
  368. spinRandomMin = spinRandomMax - (spinRandomMin - spinRandomMax );
  369. return false;
  370. }
  371. if (spinRandomMax > 1000.f || spinRandomMax < -1000.f) {
  372. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMax invalid", getName());
  373. spinRandomMax = 360.0;
  374. return false;
  375. }
  376. if (framesPerSec > 255)
  377. {
  378. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) framesPerSec > 255, too high", getName());
  379. framesPerSec = 255;
  380. return false;
  381. }
  382. times[0] = 0.0f;
  383. for (U32 i = 1; i < 4; i++) {
  384. if (times[i] < times[i-1]) {
  385. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1);
  386. times[i] = times[i-1];
  387. }
  388. }
  389. // Here we validate parameters
  390. if (animateTexture)
  391. {
  392. // Tiling dimensions must be positive and non-zero
  393. if (animTexTiling.x <= 0 || animTexTiling.y <= 0)
  394. {
  395. Con::warnf(ConsoleLogEntry::General,
  396. "ParticleData(%s) bad value(s) for animTexTiling [%d or %d <= 0], invalid datablock",
  397. animTexTiling.x, animTexTiling.y, getName());
  398. return false;
  399. }
  400. // Indices must fit into a byte so these are also bad
  401. if (animTexTiling.x * animTexTiling.y > 256)
  402. {
  403. Con::warnf(ConsoleLogEntry::General,
  404. "ParticleData(%s) bad values for animTexTiling [%d*%d > %d], invalid datablock",
  405. animTexTiling.x, animTexTiling.y, 256, getName());
  406. return false;
  407. }
  408. // A list of frames is required
  409. if (!animTexFramesString || !animTexFramesString[0])
  410. {
  411. Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) no animTexFrames, invalid datablock", getName());
  412. return false;
  413. }
  414. // The frame list cannot be too long.
  415. if (animTexFramesString && dStrlen(animTexFramesString) > 255)
  416. {
  417. Con::errorf(ConsoleLogEntry::General, "ParticleData(%s) animTexFrames string too long [> 255 chars]", getName());
  418. return false;
  419. }
  420. }
  421. return true;
  422. }
  423. //-----------------------------------------------------------------------------
  424. // preload
  425. //-----------------------------------------------------------------------------
  426. bool ParticleData::preload(bool server, String &errorStr)
  427. {
  428. if (Parent::preload(server, errorStr) == false)
  429. return false;
  430. bool error = false;
  431. if(!server)
  432. {
  433. // Here we attempt to load the particle's texture if specified. An undefined
  434. // texture is *not* an error since the emitter may provide one.
  435. if (textureName && textureName[0])
  436. {
  437. textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
  438. if (!textureHandle)
  439. {
  440. errorStr = String::ToString("Missing particle texture: %s", textureName);
  441. error = true;
  442. }
  443. }
  444. if (animateTexture)
  445. {
  446. // Here we parse animTexFramesString into byte-size frame numbers in animTexFrames.
  447. // Each frame token must be separated by whitespace.
  448. // A frame token must be a positive integer frame number or a range of frame numbers
  449. // separated with a '-'.
  450. // The range separator, '-', cannot have any whitspace around it.
  451. // Ranges can be specified to move through the frames in reverse as well as forward.
  452. // Frame numbers exceeding the number of tiles will wrap.
  453. // example:
  454. // "0-16 20 19 18 17 31-21"
  455. S32 n_tiles = animTexTiling.x * animTexTiling.y;
  456. AssertFatal(n_tiles > 0 && n_tiles <= 256, "Error, bad animTexTiling setting." );
  457. animTexFrames.clear();
  458. char* tokCopy = new char[dStrlen(animTexFramesString) + 1];
  459. dStrcpy(tokCopy, animTexFramesString);
  460. char* currTok = dStrtok(tokCopy, " \t");
  461. while (currTok != NULL)
  462. {
  463. char* minus = dStrchr(currTok, '-');
  464. if (minus)
  465. {
  466. // add a range of frames
  467. *minus = '\0';
  468. S32 range_a = dAtoi(currTok);
  469. S32 range_b = dAtoi(minus+1);
  470. if (range_b < range_a)
  471. {
  472. // reverse frame range
  473. for (S32 i = range_a; i >= range_b; i--)
  474. animTexFrames.push_back((U8)(i % n_tiles));
  475. }
  476. else
  477. {
  478. // forward frame range
  479. for (S32 i = range_a; i <= range_b; i++)
  480. animTexFrames.push_back((U8)(i % n_tiles));
  481. }
  482. }
  483. else
  484. {
  485. // add one frame
  486. animTexFrames.push_back((U8)(dAtoi(currTok) % n_tiles));
  487. }
  488. currTok = dStrtok(NULL, " \t");
  489. }
  490. // Here we pre-calculate the UVs for each frame tile, which are
  491. // tiled inside the UV region specified by texCoords. Since the
  492. // UVs are calculated using bilinear interpolation, the texCoords
  493. // region does *not* have to be an axis-aligned rectangle.
  494. if (animTexUVs)
  495. delete [] animTexUVs;
  496. animTexUVs = new Point2F[(animTexTiling.x+1)*(animTexTiling.y+1)];
  497. // interpolate points on the left and right edge of the uv quadrangle
  498. Point2F lf_pt = texCoords[0];
  499. Point2F rt_pt = texCoords[3];
  500. // per-row delta for left and right interpolated points
  501. Point2F lf_d = (texCoords[1] - texCoords[0])/(F32)animTexTiling.y;
  502. Point2F rt_d = (texCoords[2] - texCoords[3])/(F32)animTexTiling.y;
  503. S32 idx = 0;
  504. for (S32 yy = 0; yy <= animTexTiling.y; yy++)
  505. {
  506. Point2F p = lf_pt;
  507. Point2F dp = (rt_pt - lf_pt)/(F32)animTexTiling.x;
  508. for (S32 xx = 0; xx <= animTexTiling.x; xx++)
  509. {
  510. animTexUVs[idx++] = p;
  511. p += dp;
  512. }
  513. lf_pt += lf_d;
  514. rt_pt += rt_d;
  515. }
  516. // cleanup
  517. delete [] tokCopy;
  518. numFrames = animTexFrames.size();
  519. }
  520. }
  521. return !error;
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Initialize particle
  525. //-----------------------------------------------------------------------------
  526. void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelocity)
  527. {
  528. init->dataBlock = this;
  529. // Calculate the constant accleration...
  530. init->vel += inheritVelocity * inheritedVelFactor;
  531. init->acc = init->vel * constantAcceleration;
  532. // Calculate this instance's lifetime...
  533. init->totalLifetime = lifetimeMS;
  534. if (lifetimeVarianceMS != 0)
  535. init->totalLifetime += S32(gRandGen.randI() % (2 * lifetimeVarianceMS + 1)) - S32(lifetimeVarianceMS);
  536. // assign spin amount
  537. init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax );
  538. }
  539. bool ParticleData::reload(char errorBuffer[256])
  540. {
  541. bool error = false;
  542. if (textureName && textureName[0])
  543. {
  544. textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
  545. if (!textureHandle)
  546. {
  547. dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureName);
  548. error = true;
  549. }
  550. }
  551. /*
  552. numFrames = 0;
  553. for( S32 i=0; i<PDC_MAX_TEX; i++ )
  554. {
  555. if( textureNameList[i] && textureNameList[i][0] )
  556. {
  557. textureList[i] = TextureHandle( textureNameList[i], MeshTexture );
  558. if (!textureList[i].getName())
  559. {
  560. dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureNameList[i]);
  561. error = true;
  562. }
  563. numFrames++;
  564. }
  565. }
  566. */
  567. return !error;
  568. }
  569. DefineEngineMethod(ParticleData, reload, void, (),,
  570. "Reloads this particle.\n"
  571. "@tsexample\n"
  572. "// Get the editor's current particle\n"
  573. "%particle = PE_ParticleEditor.currParticle\n\n"
  574. "// Change a particle value\n"
  575. "%particle.setFieldValue( %propertyField, %value );\n\n"
  576. "// Reload it\n"
  577. "%particle.reload();\n"
  578. "@endtsexample\n" )
  579. {
  580. char errorBuffer[256];
  581. object->reload(errorBuffer);
  582. }