part_buf.cpp 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***************************************************************************
  21. * *
  22. * Project Name : G *
  23. * *
  24. * $Archive:: /VSS_Sync/ww3d2/part_buf.cpp $*
  25. * *
  26. * $Author:: Vss_sync $*
  27. * *
  28. * $Modtime:: 8/30/01 1:38a $*
  29. * *
  30. * $Revision:: 19 $*
  31. * *
  32. *-------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "part_buf.h"
  36. #include "part_emt.h"
  37. #include "ww3d.h"
  38. #include "rinfo.h"
  39. #include "scene.h"
  40. #include "camera.h"
  41. #include "predlod.h"
  42. #include "pot.h"
  43. #include "bound.h"
  44. #include "simplevec.h"
  45. #include "sphere.h"
  46. #include "wwprofile.h"
  47. #include <limits.h>
  48. #include "vp.h"
  49. #include "texture.h"
  50. #include "dx8wrapper.h"
  51. #include "vector3.h"
  52. // A random permutation of the numbers 0 to 15 - used for LOD particle decimation.
  53. // It was generated by the amazingly high-tech method of pulling numbers out of a hat.
  54. const unsigned int ParticleBufferClass::PermutationArray[16] = {
  55. 11, 3, 7, 14, 0, 13, 1, 2, 5, 12, 15, 6, 9, 8, 4, 10
  56. };
  57. // Maximum size of randomizer tables
  58. const static unsigned int MAX_RANDOM_ENTRIES = 32; // MUST be power of two!
  59. // Total Active Particle Buffer Count
  60. unsigned int ParticleBufferClass::TotalActiveCount = 0;
  61. // Static array of screen-size clamps for the 17 possible LOD levels a particle buffer can have.
  62. // We can change these from being global to being per-buffer later if we wish. Default is
  63. // NO_MAX_SCREEN_SIZE.
  64. float ParticleBufferClass::LODMaxScreenSizes[17] = {
  65. NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE,
  66. NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE,
  67. NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE,
  68. NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE, NO_MAX_SCREEN_SIZE,
  69. NO_MAX_SCREEN_SIZE
  70. };
  71. static Random4Class rand_gen;
  72. const float oo_intmax = 1.0f / (float)INT_MAX;
  73. ParticleBufferClass::ParticleBufferClass
  74. (
  75. ParticleEmitterClass *emitter,
  76. unsigned int buffer_size,
  77. ParticlePropertyStruct<Vector3> &color,
  78. ParticlePropertyStruct<float> &opacity,
  79. ParticlePropertyStruct<float> &size,
  80. ParticlePropertyStruct<float> &rotation,
  81. float orient_rnd,
  82. ParticlePropertyStruct<float> &frame,
  83. Vector3 accel,
  84. float max_age,
  85. TextureClass *tex,
  86. ShaderClass shader,
  87. bool pingpong,
  88. int render_mode,
  89. int frame_mode,
  90. const W3dEmitterLinePropertiesStruct * line_props
  91. ) :
  92. NewParticleQueue(NULL),
  93. NewParticleQueueStart(0U),
  94. NewParticleQueueEnd(0U),
  95. NewParticleQueueCount(0U),
  96. RenderMode(render_mode),
  97. FrameMode(frame_mode),
  98. MaxAge(1000.0f * max_age),
  99. LastUpdateTime(WW3D::Get_Sync_Time()),
  100. IsEmitterDead(false),
  101. MaxSize(0.0f),
  102. MaxNum(buffer_size),
  103. Start(0U),
  104. End(0U),
  105. NewEnd(0U),
  106. NonNewNum(0),
  107. NewNum(0),
  108. BoundingBox(Vector3(0,0,0),Vector3(0,0,0)),
  109. BoundingBoxDirty(true),
  110. NumColorKeyFrames(0),
  111. ColorKeyFrameTimes(NULL),
  112. ColorKeyFrameValues(NULL),
  113. ColorKeyFrameDeltas(NULL),
  114. NumAlphaKeyFrames(0),
  115. AlphaKeyFrameTimes(NULL),
  116. AlphaKeyFrameValues(NULL),
  117. AlphaKeyFrameDeltas(NULL),
  118. NumSizeKeyFrames(0),
  119. SizeKeyFrameTimes(NULL),
  120. SizeKeyFrameValues(NULL),
  121. SizeKeyFrameDeltas(NULL),
  122. NumRotationKeyFrames(0),
  123. RotationKeyFrameTimes(NULL),
  124. RotationKeyFrameValues(NULL),
  125. HalfRotationKeyFrameDeltas(NULL),
  126. OrientationKeyFrameValues(NULL),
  127. NumFrameKeyFrames(0),
  128. FrameKeyFrameTimes(NULL),
  129. FrameKeyFrameValues(NULL),
  130. FrameKeyFrameDeltas(NULL),
  131. NumRandomColorEntriesMinus1(0),
  132. RandomColorEntries(NULL),
  133. NumRandomAlphaEntriesMinus1(0),
  134. RandomAlphaEntries(NULL),
  135. NumRandomSizeEntriesMinus1(0),
  136. RandomSizeEntries(NULL),
  137. ColorRandom(0, 0, 0),
  138. OpacityRandom(0),
  139. SizeRandom(0),
  140. RotationRandom(0),
  141. FrameRandom(0),
  142. InitialOrientationRandom(0),
  143. NumRandomRotationEntriesMinus1(0),
  144. RandomRotationEntries(NULL),
  145. NumRandomOrientationEntriesMinus1(0),
  146. RandomOrientationEntries(NULL),
  147. NumRandomFrameEntriesMinus1(0),
  148. RandomFrameEntries(NULL),
  149. PointGroup(NULL),
  150. LineRenderer(NULL),
  151. Diffuse(NULL),
  152. Color(NULL),
  153. Alpha(NULL),
  154. Size(NULL),
  155. Orientation(NULL),
  156. Frame(NULL),
  157. APT(NULL),
  158. PingPongPosition(pingpong),
  159. Velocity(NULL),
  160. TimeStamp(NULL),
  161. Emitter(emitter),
  162. DecimationThreshold(0U),
  163. ProjectedArea(0.0f)
  164. {
  165. LodCount = 17;
  166. LodBias = 1.0f;
  167. Position[0] = NULL;
  168. Position[1] = NULL;
  169. // Create color array, keyframes and randomizer table (if needed)
  170. Reset_Colors(color);
  171. // Create alpha array, keyframes and randomizer table (if needed)
  172. Reset_Opacity(opacity);
  173. // Create size array, keyframes and randomizer table (if needed)
  174. Reset_Size(size);
  175. // Create the rotation array, keyframes, and randomizer table (if needed)
  176. Reset_Rotations(rotation, orient_rnd);
  177. // Create the frame array, keyframes, and randomizer table (if needed)
  178. Reset_Frames(frame);
  179. // We do not add a ref for the emitter (see DTor for detailed explanation)
  180. // if (Emitter) Emitter->Add_Ref();
  181. // Set up new particle queue:
  182. NewParticleQueue = W3DNEWARRAY NewParticleStruct[MaxNum];
  183. // These inputs don't need to be range-checked (emitter did that).
  184. Accel = accel;
  185. HasAccel = (accel.X != 0.0f) || (accel.Y != 0.0f) || (accel.Z != 0.0f);
  186. // Set up worldspace point group:
  187. PointGroup = W3DNEW PointGroupClass();
  188. PointGroup->Set_Flag(PointGroupClass::TRANSFORM, true);
  189. PointGroup->Set_Texture(tex);
  190. shader.Enable_Fog ("ParticleBufferClass");
  191. PointGroup->Set_Shader(shader);
  192. if (RenderMode == W3D_EMITTER_RENDER_MODE_QUAD_PARTICLES) {
  193. PointGroup->Set_Point_Mode(PointGroupClass::QUADS);
  194. } else {
  195. PointGroup->Set_Point_Mode(PointGroupClass::TRIS);
  196. }
  197. PointGroup->Set_Frame_Row_Column_Count_Log2(frame_mode);
  198. // Set up circular buffer. Contents are not initialized because the
  199. // start/end indices currently indicate the buffer is empty.
  200. Position[0] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
  201. if (PingPongPosition) {
  202. Position[1] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
  203. }
  204. APT = NEW_REF( ShareBufferClass<unsigned int> , (MaxNum, "ParticleBufferClass::APT") );
  205. Velocity = W3DNEWARRAY Vector3[MaxNum];
  206. TimeStamp = W3DNEWARRAY unsigned int[MaxNum];
  207. // So that the object is ready for use after construction, we will
  208. // complete its initialization by initializing its cost and value arrays
  209. // according to a screen area of 1.
  210. int minlod = Calculate_Cost_Value_Arrays(1.0f, Value, Cost);
  211. // Ensure lod is no less than minimum allowed
  212. if (Get_LOD_Level() < minlod) Set_LOD_Level(minlod);
  213. // Update Global Count
  214. TotalActiveCount++;
  215. //lorenzen
  216. // If the render mode is W3D_EMITTER_RENDER_MODE_LINE and we are supplied with
  217. // a line properties structure, set up a line renderer
  218. if (RenderMode == W3D_EMITTER_RENDER_MODE_LINE) {
  219. if (line_props != NULL) {
  220. LineRenderer = W3DNEW SegLineRendererClass;
  221. LineRenderer->Init(*line_props);
  222. LineRenderer->Set_Texture(tex);
  223. LineRenderer->Set_Shader(shader);
  224. LineRenderer->Set_Width(Get_Particle_Size());
  225. } else {
  226. // We were in line mode but didn't get any line properties, drop back to triangles
  227. RenderMode = W3D_EMITTER_RENDER_MODE_TRI_PARTICLES;
  228. }
  229. }
  230. }
  231. ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) :
  232. RenderObjClass(src),
  233. NewParticleQueue(NULL),
  234. NewParticleQueueStart(0U),
  235. NewParticleQueueEnd(0U),
  236. NewParticleQueueCount(0U),
  237. RenderMode(src.RenderMode),
  238. FrameMode(src.FrameMode),
  239. MaxAge(src.MaxAge),
  240. LastUpdateTime(WW3D::Get_Sync_Time()),
  241. IsEmitterDead(false),
  242. MaxSize(src.MaxSize),
  243. MaxNum(src.MaxNum),
  244. Start(0U),
  245. End(0U),
  246. NewEnd(0U),
  247. NonNewNum(0),
  248. NewNum(0),
  249. BoundingBox(Vector3(0,0,0),Vector3(0,0,0)),
  250. BoundingBoxDirty(true),
  251. NumColorKeyFrames(src.NumColorKeyFrames),
  252. ColorKeyFrameTimes(NULL),
  253. ColorKeyFrameValues(NULL),
  254. ColorKeyFrameDeltas(NULL),
  255. NumAlphaKeyFrames(src.NumAlphaKeyFrames),
  256. AlphaKeyFrameTimes(NULL),
  257. AlphaKeyFrameValues(NULL),
  258. AlphaKeyFrameDeltas(NULL),
  259. NumSizeKeyFrames(src.NumSizeKeyFrames),
  260. SizeKeyFrameTimes(NULL),
  261. SizeKeyFrameValues(NULL),
  262. SizeKeyFrameDeltas(NULL),
  263. NumRotationKeyFrames(src.NumRotationKeyFrames),
  264. RotationKeyFrameTimes(NULL),
  265. RotationKeyFrameValues(NULL),
  266. HalfRotationKeyFrameDeltas(NULL),
  267. OrientationKeyFrameValues(NULL),
  268. NumFrameKeyFrames(src.NumFrameKeyFrames),
  269. FrameKeyFrameTimes(NULL),
  270. FrameKeyFrameValues(NULL),
  271. FrameKeyFrameDeltas(NULL),
  272. RandomColorEntries(NULL),
  273. RandomAlphaEntries(NULL),
  274. RandomSizeEntries(NULL),
  275. ColorRandom(src.ColorRandom),
  276. OpacityRandom(src.OpacityRandom),
  277. SizeRandom(src.SizeRandom),
  278. RotationRandom(src.RotationRandom),
  279. FrameRandom(src.FrameRandom),
  280. InitialOrientationRandom(src.InitialOrientationRandom),
  281. NumRandomRotationEntriesMinus1(0),
  282. RandomRotationEntries(NULL),
  283. NumRandomOrientationEntriesMinus1(0),
  284. RandomOrientationEntries(NULL),
  285. NumRandomFrameEntriesMinus1(0),
  286. RandomFrameEntries(NULL),
  287. PointGroup(NULL),
  288. LineRenderer(NULL),
  289. Diffuse(NULL),
  290. Color(NULL),
  291. Alpha(NULL),
  292. Size(NULL),
  293. Orientation(NULL),
  294. Frame(NULL),
  295. APT(NULL),
  296. PingPongPosition(src.PingPongPosition),
  297. Velocity(NULL),
  298. TimeStamp(NULL),
  299. Emitter(src.Emitter),
  300. DecimationThreshold(src.DecimationThreshold),
  301. ProjectedArea(0.0f)
  302. {
  303. Position[0] = NULL;
  304. Position[1] = NULL;
  305. unsigned int i;
  306. LodCount = MIN(MaxNum, 17);
  307. LodBias = src.LodBias;
  308. /*
  309. ** Create visual state arrays, copy keyframes and randomizer tables.
  310. */
  311. NumRandomColorEntriesMinus1 = src.NumRandomColorEntriesMinus1;
  312. if (src.Color) {
  313. // Create color array
  314. Color = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Color") );
  315. // Copy color keyframes
  316. ColorKeyFrameTimes = W3DNEWARRAY unsigned int [NumColorKeyFrames];
  317. ColorKeyFrameValues = W3DNEWARRAY Vector3 [NumColorKeyFrames];
  318. ColorKeyFrameDeltas = W3DNEWARRAY Vector3 [NumColorKeyFrames];
  319. for (i = 0; i < NumColorKeyFrames; i++) {
  320. ColorKeyFrameTimes[i] = src.ColorKeyFrameTimes[i];
  321. ColorKeyFrameValues[i] = src.ColorKeyFrameValues[i];
  322. ColorKeyFrameDeltas[i] = src.ColorKeyFrameDeltas[i];
  323. }
  324. // Copy color randomizer table
  325. if (src.RandomColorEntries) {
  326. RandomColorEntries = W3DNEWARRAY Vector3 [NumRandomColorEntriesMinus1 + 1];
  327. for (unsigned int j = 0; j <= NumRandomColorEntriesMinus1; j++) {
  328. RandomColorEntries[j] = src.RandomColorEntries[j];
  329. }
  330. }
  331. } else {
  332. ColorKeyFrameValues = W3DNEWARRAY Vector3 [1];
  333. ColorKeyFrameValues[0] = src.ColorKeyFrameValues[0];
  334. }
  335. NumRandomAlphaEntriesMinus1 = src.NumRandomAlphaEntriesMinus1;
  336. if (src.Alpha) {
  337. // Create alpha array
  338. Alpha = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Alpha") );
  339. // Copy alpha keyframes
  340. AlphaKeyFrameTimes = W3DNEWARRAY unsigned int [NumAlphaKeyFrames];
  341. AlphaKeyFrameValues = W3DNEWARRAY float [NumAlphaKeyFrames];
  342. AlphaKeyFrameDeltas = W3DNEWARRAY float [NumAlphaKeyFrames];
  343. for (i = 0; i < NumAlphaKeyFrames; i++) {
  344. AlphaKeyFrameTimes[i] = src.AlphaKeyFrameTimes[i];
  345. AlphaKeyFrameValues[i] = src.AlphaKeyFrameValues[i];
  346. AlphaKeyFrameDeltas[i] = src.AlphaKeyFrameDeltas[i];
  347. }
  348. // Copy alpha randomizer table
  349. if (src.RandomAlphaEntries) {
  350. RandomAlphaEntries = W3DNEWARRAY float [NumRandomAlphaEntriesMinus1 + 1];
  351. for (unsigned int j = 0; j <= NumRandomAlphaEntriesMinus1; j++) {
  352. RandomAlphaEntries[j] = src.RandomAlphaEntries[j];
  353. }
  354. }
  355. } else {
  356. AlphaKeyFrameValues = W3DNEWARRAY float [1];
  357. AlphaKeyFrameValues[0] = src.AlphaKeyFrameValues[0];
  358. }
  359. NumRandomSizeEntriesMinus1 = src.NumRandomSizeEntriesMinus1;
  360. if (src.Size) {
  361. // Create size array
  362. Size = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Size") );
  363. // Copy size keyframes
  364. SizeKeyFrameTimes = W3DNEWARRAY unsigned int [NumSizeKeyFrames];
  365. SizeKeyFrameValues = W3DNEWARRAY float [NumSizeKeyFrames];
  366. SizeKeyFrameDeltas = W3DNEWARRAY float [NumSizeKeyFrames];
  367. for (i = 0; i < NumSizeKeyFrames; i++) {
  368. SizeKeyFrameTimes[i] = src.SizeKeyFrameTimes[i];
  369. SizeKeyFrameValues[i] = src.SizeKeyFrameValues[i];
  370. SizeKeyFrameDeltas[i] = src.SizeKeyFrameDeltas[i];
  371. }
  372. // Copy size randomizer table
  373. if (src.RandomSizeEntries) {
  374. RandomSizeEntries = W3DNEWARRAY float [NumRandomSizeEntriesMinus1 + 1];
  375. for (unsigned int j = 0; j <= NumRandomSizeEntriesMinus1; j++) {
  376. RandomSizeEntries[j] = src.RandomSizeEntries[j];
  377. }
  378. }
  379. } else {
  380. SizeKeyFrameValues = W3DNEWARRAY float [1];
  381. SizeKeyFrameValues[0] = src.SizeKeyFrameValues[0];
  382. }
  383. // Set up the rotation / orientation keyframes
  384. NumRandomRotationEntriesMinus1 = src.NumRandomRotationEntriesMinus1;
  385. NumRandomOrientationEntriesMinus1 = src.NumRandomOrientationEntriesMinus1;
  386. if (src.Orientation) {
  387. // Create orientation array
  388. Orientation = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Orientation") );
  389. // Copy rotation / orientation keyframes
  390. RotationKeyFrameTimes = W3DNEWARRAY unsigned int [NumRotationKeyFrames];
  391. RotationKeyFrameValues = W3DNEWARRAY float [NumRotationKeyFrames];
  392. HalfRotationKeyFrameDeltas = W3DNEWARRAY float [NumRotationKeyFrames];
  393. OrientationKeyFrameValues = W3DNEWARRAY float [NumRotationKeyFrames];
  394. for (i = 0; i < NumRotationKeyFrames; i++) {
  395. RotationKeyFrameTimes[i] = src.RotationKeyFrameTimes[i];
  396. RotationKeyFrameValues[i] = src.RotationKeyFrameValues[i];
  397. HalfRotationKeyFrameDeltas[i] = src.HalfRotationKeyFrameDeltas[i];
  398. OrientationKeyFrameValues[i] = src.OrientationKeyFrameValues[i];
  399. }
  400. // Copy rotation randomizer table
  401. if (src.RandomRotationEntries) {
  402. RandomRotationEntries = W3DNEWARRAY float [NumRandomRotationEntriesMinus1 + 1];
  403. for (unsigned int j = 0; j <= NumRandomRotationEntriesMinus1; j++) {
  404. RandomRotationEntries[j] = src.RandomRotationEntries[j];
  405. }
  406. }
  407. // Copy starting orientation randomizer table
  408. if (src.RandomOrientationEntries) {
  409. RandomOrientationEntries = W3DNEWARRAY float [NumRandomOrientationEntriesMinus1 + 1];
  410. for (unsigned int j = 0; j <= NumRandomOrientationEntriesMinus1; j++) {
  411. RandomOrientationEntries[j] = src.RandomOrientationEntries[j];
  412. }
  413. }
  414. } else {
  415. // Unlike other properties, if there is no Orientation array then all the arrays are NULL
  416. // (including the Values array) - there is an implicit starting value of 0.
  417. }
  418. // Set up the frame keyframes
  419. NumRandomFrameEntriesMinus1 = src.NumRandomFrameEntriesMinus1;
  420. if (src.Frame) {
  421. // Create frame array
  422. Frame = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Frame") );
  423. // Copy frame keyframes
  424. FrameKeyFrameTimes = W3DNEWARRAY unsigned int [NumFrameKeyFrames];
  425. FrameKeyFrameValues = W3DNEWARRAY float [NumFrameKeyFrames];
  426. FrameKeyFrameDeltas = W3DNEWARRAY float [NumFrameKeyFrames];
  427. for (i = 0; i < NumFrameKeyFrames; i++) {
  428. FrameKeyFrameTimes[i] = src.FrameKeyFrameTimes[i];
  429. FrameKeyFrameValues[i] = src.FrameKeyFrameValues[i];
  430. FrameKeyFrameDeltas[i] = src.FrameKeyFrameDeltas[i];
  431. }
  432. // Copy frame randomizer table
  433. if (src.RandomFrameEntries) {
  434. RandomFrameEntries = W3DNEWARRAY float [NumRandomFrameEntriesMinus1 + 1];
  435. for (unsigned int j = 0; j <= NumRandomFrameEntriesMinus1; j++) {
  436. RandomFrameEntries[j] = src.RandomFrameEntries[j];
  437. }
  438. }
  439. } else {
  440. FrameKeyFrameValues = W3DNEWARRAY float [1];
  441. FrameKeyFrameValues[0] = src.FrameKeyFrameValues[0];
  442. }
  443. // We do not add a ref for the emitter (see DTor for detailed explanation)
  444. // if (Emitter) Emitter->Add_Ref();
  445. // Set up new particle queue:
  446. NewParticleQueue = W3DNEWARRAY NewParticleStruct[MaxNum];
  447. // Inputs don't need to be range-checked (emitter did that).
  448. Accel = src.Accel;
  449. HasAccel = src.HasAccel;
  450. // Set up worldspace point group:
  451. PointGroup = W3DNEW PointGroupClass();
  452. PointGroup->Set_Flag(PointGroupClass::TRANSFORM, true);
  453. PointGroup->Set_Texture(src.PointGroup->Peek_Texture());
  454. PointGroup->Set_Shader(src.PointGroup->Get_Shader());
  455. // Set up the point group render mode
  456. if (RenderMode == W3D_EMITTER_RENDER_MODE_QUAD_PARTICLES) {
  457. PointGroup->Set_Point_Mode(PointGroupClass::QUADS);
  458. } else {
  459. PointGroup->Set_Point_Mode(PointGroupClass::TRIS);
  460. }
  461. // Set up circular buffer. Contents are not initialized because the
  462. // start/end indices currently indicate the buffer is empty.
  463. Position[0] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
  464. if (PingPongPosition) {
  465. Position[1] = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Position") );
  466. }
  467. APT = NEW_REF( ShareBufferClass<unsigned int> , (MaxNum, "ParticleBufferClass::APT") );
  468. Velocity = W3DNEWARRAY Vector3[MaxNum];
  469. TimeStamp = W3DNEWARRAY unsigned int[MaxNum];
  470. // So that the object is ready for use after construction, we will
  471. // complete its initialization by initializing its cost and value arrays
  472. // according to a screen area of 1.
  473. int minlod = Calculate_Cost_Value_Arrays(1.0f, Value, Cost);
  474. // Ensure lod is no less than minimum allowed
  475. if (Get_LOD_Level() < minlod) Set_LOD_Level(minlod);
  476. // Update Global Count
  477. TotalActiveCount++;
  478. // if the source object has a line renderer, set up a copy.
  479. if (src.LineRenderer) {
  480. assert(RenderMode == W3D_EMITTER_RENDER_MODE_LINE);
  481. LineRenderer = W3DNEW SegLineRendererClass(*src.LineRenderer);
  482. } else {
  483. assert(RenderMode != W3D_EMITTER_RENDER_MODE_LINE);
  484. }
  485. }
  486. ParticleBufferClass & ParticleBufferClass::operator = (const ParticleBufferClass & that)
  487. {
  488. RenderObjClass::operator = (that);
  489. if (this != &that) {
  490. assert(0); // TODO: if you hit this assert, please implement me !!!;-)
  491. }
  492. return * this;
  493. }
  494. ParticleBufferClass::~ParticleBufferClass(void)
  495. {
  496. if (NewParticleQueue) delete [] NewParticleQueue;
  497. if (ColorKeyFrameTimes) delete [] ColorKeyFrameTimes;
  498. if (ColorKeyFrameValues) delete [] ColorKeyFrameValues;
  499. if (ColorKeyFrameDeltas) delete [] ColorKeyFrameDeltas;
  500. if (AlphaKeyFrameTimes) delete [] AlphaKeyFrameTimes;
  501. if (AlphaKeyFrameValues) delete [] AlphaKeyFrameValues;
  502. if (AlphaKeyFrameDeltas) delete [] AlphaKeyFrameDeltas;
  503. if (SizeKeyFrameTimes) delete [] SizeKeyFrameTimes;
  504. if (SizeKeyFrameValues) delete [] SizeKeyFrameValues;
  505. if (SizeKeyFrameDeltas) delete [] SizeKeyFrameDeltas;
  506. if (RotationKeyFrameTimes) delete [] RotationKeyFrameTimes;
  507. if (RotationKeyFrameValues) delete [] RotationKeyFrameValues;
  508. if (HalfRotationKeyFrameDeltas) delete [] HalfRotationKeyFrameDeltas;
  509. if (OrientationKeyFrameValues) delete [] OrientationKeyFrameValues;
  510. if (FrameKeyFrameTimes) delete [] FrameKeyFrameTimes;
  511. if (FrameKeyFrameValues) delete [] FrameKeyFrameValues;
  512. if (FrameKeyFrameDeltas) delete [] FrameKeyFrameDeltas;
  513. if (RandomColorEntries) delete [] RandomColorEntries;
  514. if (RandomAlphaEntries) delete [] RandomAlphaEntries;
  515. if (RandomSizeEntries) delete [] RandomSizeEntries;
  516. if (RandomRotationEntries) delete [] RandomRotationEntries;
  517. if (RandomOrientationEntries) delete [] RandomOrientationEntries;
  518. if (RandomFrameEntries) delete [] RandomFrameEntries;
  519. if (PointGroup) delete PointGroup;
  520. REF_PTR_RELEASE(Position[0]);
  521. REF_PTR_RELEASE(Position[1]);
  522. REF_PTR_RELEASE(Diffuse);
  523. REF_PTR_RELEASE(Color);
  524. REF_PTR_RELEASE(Alpha);
  525. REF_PTR_RELEASE(Size);
  526. REF_PTR_RELEASE(Orientation);
  527. REF_PTR_RELEASE(Frame);
  528. REF_PTR_RELEASE(APT);
  529. if (Velocity) delete [] Velocity;
  530. if (TimeStamp) delete [] TimeStamp;
  531. if (Emitter) {
  532. // We should not have an emitter at this point, since the emitter
  533. // should still have a live ref to us if it still exists which would
  534. // prevent us from getting killed.
  535. assert(0);
  536. // We do not release-ref the emitter pointer because we did not add a
  537. // ref for it to begin with; the ref is not needed (if the emitter gets
  538. // deleted it will tell us to clear our emitter pointer) and actually
  539. // harmful (if emitter and buffer each have refcounted pointers to the
  540. // other neither would ever get deleted).
  541. // Emitter->Release_Ref();
  542. Emitter = NULL;
  543. }
  544. if (LineRenderer) {
  545. delete LineRenderer;
  546. }
  547. // Update Global Count
  548. TotalActiveCount--;
  549. }
  550. RenderObjClass * ParticleBufferClass::Clone(void) const
  551. {
  552. return W3DNEW ParticleBufferClass(*this);
  553. }
  554. int ParticleBufferClass::Get_Num_Polys(void) const
  555. {
  556. // Currently in particle buffers, the cost happens to be equal to thwe polygon count.
  557. return (int)Get_Cost();
  558. }
  559. int ParticleBufferClass::Get_Particle_Count(void) const
  560. {
  561. return NonNewNum + NewNum;
  562. }
  563. void ParticleBufferClass::Render(RenderInfoClass & rinfo)
  564. {
  565. WWPROFILE("ParticleBuffer::Render");
  566. unsigned int sort_level = SORT_LEVEL_NONE;
  567. if (!WW3D::Is_Sorting_Enabled())
  568. sort_level=Get_Shader().Guess_Sort_Level();
  569. if (WW3D::Are_Static_Sort_Lists_Enabled() && sort_level!=SORT_LEVEL_NONE) {
  570. WW3D::Add_To_Static_Sort_List(this, sort_level);
  571. } else {
  572. // Ensure particles' kinematic state is updated
  573. Update_Kinematic_Particle_State();
  574. // Since we are rendering the particles, visual state needs to be updated (but not if the
  575. // entire particle buffer is decimated away)
  576. if (DecimationThreshold < LodCount - 1) {
  577. Update_Visual_Particle_State();
  578. }
  579. }
  580. switch( RenderMode )
  581. {
  582. case W3D_EMITTER_RENDER_MODE_TRI_PARTICLES:
  583. case W3D_EMITTER_RENDER_MODE_QUAD_PARTICLES:
  584. Render_Particles(rinfo);
  585. break;
  586. case W3D_EMITTER_RENDER_MODE_LINE:
  587. Render_Line(rinfo);
  588. break;
  589. }
  590. }
  591. void ParticleBufferClass::Render_Particles(RenderInfoClass & rinfo)
  592. {
  593. // If the number of active points is less than the maximum or we need to decimate particles
  594. // (for LOD purposes), build the active point table:
  595. ShareBufferClass<unsigned int> *apt = NULL;
  596. unsigned int active_point_count = 0;
  597. if (NonNewNum < (int)MaxNum || DecimationThreshold > 0) {
  598. // In the general case, a range in a circular buffer can be composed of up
  599. // to two subranges. Find the Start - End subranges.
  600. // This differs from other similar code segments because we want to access
  601. // the subranges in memory order (rather than in queue order) this time.
  602. unsigned int sub1_start; // Start of subrange 1.
  603. unsigned int sub1_end; // End of subrange 1.
  604. unsigned int sub2_start; // Start of subrange 2.
  605. unsigned int sub2_end; // End of subrange 2.
  606. unsigned int i; // Loop index.
  607. if ((Start < End) || ((Start == End) && NonNewNum == 0)) {
  608. sub1_start = Start;
  609. sub1_end = End;
  610. sub2_start = End;
  611. sub2_end = End;
  612. } else {
  613. sub1_start = 0;
  614. sub1_end = End;
  615. sub2_start = Start;
  616. sub2_end = MaxNum;
  617. }
  618. // Generate APT:
  619. unsigned int *apt_ptr = APT->Get_Array();
  620. for (i = sub1_start; i < sub1_end; i++) {
  621. if (PermutationArray[i & 0xF] >= DecimationThreshold) {
  622. apt_ptr[active_point_count++] = i;
  623. }
  624. }
  625. for (i = sub2_start; i < sub2_end; i++) {
  626. if (PermutationArray[i & 0xF] >= DecimationThreshold) {
  627. apt_ptr[active_point_count++] = i;
  628. }
  629. }
  630. apt = APT;
  631. } else {
  632. active_point_count = NonNewNum;
  633. }
  634. // Set color, alpha, size defaults if array not present:
  635. if (!Color) {
  636. PointGroup->Set_Point_Color(ColorKeyFrameValues[0]);
  637. }
  638. if (!Alpha) {
  639. PointGroup->Set_Point_Alpha(AlphaKeyFrameValues[0]);
  640. }
  641. if (!Size) {
  642. PointGroup->Set_Point_Size(SizeKeyFrameValues[0]);
  643. }
  644. if (!Orientation) {
  645. // The rotation keyframes are used to derive the orientation indirectly, as well as the
  646. // starting orientation randomizer. If there is no Orientation array that means both are
  647. // absent so the orientation should just be set to 0.
  648. PointGroup->Set_Point_Orientation(0);
  649. }
  650. if (!Frame) {
  651. PointGroup->Set_Point_Frame(((int)(FrameKeyFrameValues[0])) & 0xFF);
  652. }
  653. // Pass the point buffer to the point group and render it.
  654. // If we are using pingpong position buffers pass the right one
  655. int pingpong = 0;
  656. if (PingPongPosition) {
  657. pingpong = WW3D::Get_Frame_Count() & 0x1;
  658. }
  659. // Temporary array copying to combine diffuse and alpha to one array.
  660. if (Color || Alpha) {
  661. unsigned cnt=MaxNum;
  662. if (!Diffuse) {
  663. Diffuse = NEW_REF( ShareBufferClass<Vector4> , (MaxNum, "ParticleBufferClass::Diffuse") );
  664. }
  665. if (Color && Alpha) {
  666. VectorProcessorClass::Copy(
  667. Diffuse->Get_Array(),
  668. Color->Get_Array(),
  669. Alpha->Get_Array(),
  670. cnt);
  671. }
  672. else if (Color) {
  673. VectorProcessorClass::Copy(
  674. Diffuse->Get_Array(),
  675. Color->Get_Array(),
  676. 1.0f,
  677. cnt);
  678. }
  679. else {
  680. VectorProcessorClass::Copy(
  681. Diffuse->Get_Array(),
  682. Vector3(1.0f,1.0f,1.0f),
  683. Alpha->Get_Array(),
  684. cnt);
  685. }
  686. VectorProcessorClass::Clamp(
  687. Diffuse->Get_Array(),
  688. Diffuse->Get_Array(),
  689. 0.0f,
  690. 1.0f,
  691. cnt);
  692. }
  693. else if (Diffuse) {
  694. Diffuse->Release_Ref();
  695. Diffuse=NULL;
  696. }
  697. PointGroup->Set_Arrays(Position[pingpong], Diffuse, apt, Size, Orientation, Frame, active_point_count);
  698. Update_Bounding_Box();
  699. PointGroup->Render(rinfo);
  700. }
  701. void ParticleBufferClass::Render_Line(RenderInfoClass & rinfo)
  702. {
  703. // Look up the array to use
  704. int pingpong = 0;
  705. if (PingPongPosition) {
  706. pingpong = WW3D::Get_Frame_Count() & 0x1;
  707. }
  708. // Unroll the circular buffer while skipping LOD'd particles
  709. static SimpleDynVecClass<Vector3> tmp_points;
  710. Vector3 * positions = Position[pingpong]->Get_Array();
  711. unsigned int sub1_end; // End of subrange 1.
  712. unsigned int sub2_start; // Start of subrange 2.
  713. unsigned int i; // Loop index.
  714. if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
  715. sub1_end = End;
  716. sub2_start = End;
  717. } else {
  718. sub1_end = MaxNum;
  719. sub2_start = 0;
  720. }
  721. tmp_points.Delete_All(false);
  722. for (i = Start; i < sub1_end; i++) {
  723. if (PermutationArray[i & 0xF] >= DecimationThreshold) {
  724. tmp_points.Add(positions[i]);
  725. }
  726. }
  727. for (i = sub2_start; i < End; i++) {
  728. if (PermutationArray[i & 0xF] >= DecimationThreshold) {
  729. tmp_points.Add(positions[i]);
  730. }
  731. }
  732. // If we got any points, render them
  733. if (tmp_points.Count() > 0) {
  734. SphereClass bounding_sphere;
  735. Get_Obj_Space_Bounding_Sphere(bounding_sphere);
  736. TextureClass * tex = PointGroup->Get_Texture();
  737. //LineRenderer.Set_Texture(tex);
  738. REF_PTR_RELEASE(tex);
  739. LineRenderer->Set_Shader(PointGroup->Get_Shader());
  740. LineRenderer->Render(rinfo,
  741. Transform,
  742. tmp_points.Count(),
  743. &(tmp_points[0]),
  744. bounding_sphere);
  745. }
  746. }
  747. // Scales the size of the individual particles but doesn't affect their
  748. // position (and therefore the size of the particle system as a whole)
  749. void ParticleBufferClass::Scale(float scale)
  750. {
  751. // Scale all size keyframes, keyframe deltas, random size entries,
  752. // MaxSize and SizeRandom.
  753. unsigned int i;
  754. for (i = 0; i < NumSizeKeyFrames; i++) {
  755. SizeKeyFrameValues[i] *= scale;
  756. SizeKeyFrameDeltas[i] *= scale;
  757. }
  758. if (RandomSizeEntries) {
  759. for (i = 0; i <= NumRandomSizeEntriesMinus1; i++) {
  760. RandomSizeEntries[i] *= scale;
  761. }
  762. }
  763. MaxSize *= scale;
  764. SizeRandom *= scale;
  765. }
  766. // The particle buffer never receives a Set_Transform/Position call,
  767. // evem though its bounding volume changes. Since bounding volume
  768. // invalidations ordinarily occur when these functions are called,
  769. // the cached bounding volumes will not be invalidated unless we do
  770. // it elsewhere (such as here). We also need to call the particle
  771. // emitter's Emit() function (done here to avoid order dependence).
  772. void ParticleBufferClass::On_Frame_Update(void)
  773. {
  774. Invalidate_Cached_Bounding_Volumes();
  775. if (Emitter) {
  776. Emitter->Emit();
  777. }
  778. if (Is_Complete()) {
  779. WWASSERT(Scene);
  780. Scene->Register(this,SceneClass::RELEASE);
  781. }
  782. }
  783. void ParticleBufferClass::Notify_Added(SceneClass * scene)
  784. {
  785. RenderObjClass::Notify_Added(scene);
  786. scene->Register(this,SceneClass::ON_FRAME_UPDATE);
  787. }
  788. void ParticleBufferClass::Notify_Removed(SceneClass * scene)
  789. {
  790. scene->Unregister(this,SceneClass::ON_FRAME_UPDATE);
  791. RenderObjClass::Notify_Removed(scene);
  792. }
  793. void ParticleBufferClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
  794. {
  795. // This ugly cast is done because the alternative is to make everything
  796. // in the class mutable, which does not seem like a good solution
  797. // (Update_Bounding_Box can potentially update the particle state)
  798. ((ParticleBufferClass *)this)->Update_Bounding_Box();
  799. // The particle buffer's transform is always identity, so
  800. // objspace == worldspace.
  801. // Wrap sphere outside bounding box:
  802. sphere.Center = BoundingBox.Center;
  803. sphere.Radius = BoundingBox.Extent.Length();
  804. }
  805. void ParticleBufferClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
  806. {
  807. // This ugly cast is done because the alternative is to make everything
  808. // in the class mutable, which does not seem like a good solution
  809. // (Update_Bounding_Box can potentially update the particle state).
  810. ((ParticleBufferClass *)this)->Update_Bounding_Box();
  811. // The particle buffer's transform is always identity, so
  812. // objspace == worldspace.
  813. box = BoundingBox;
  814. }
  815. void ParticleBufferClass::Prepare_LOD(CameraClass &camera)
  816. {
  817. if (Is_Not_Hidden_At_All() == false) {
  818. return;
  819. }
  820. // Estimate the screen area of the particle buffer. We shall take the lesser of two
  821. // metrics: the standard bounding-sphere projection (which for many particle systems may
  822. // grossly overestimate the actual screen area), and a measurement based on the screen area of
  823. // individual particles times the maximum number of particles (in the case of densely
  824. // overlapping particles this metric can also give numbers which are too high, which is why we
  825. // use the bounding sphere as backup). Note - to find the area of individual particles we
  826. // treat them as all being the maximum size and being in the center of the bounding sphere).
  827. Vector3 cam = camera.Get_Position();
  828. ViewportClass viewport = camera.Get_Viewport();
  829. Vector2 vpr_min, vpr_max;
  830. camera.Get_View_Plane(vpr_min, vpr_max);
  831. float width_factor = viewport.Width() / (vpr_max.X - vpr_min.X);
  832. float height_factor = viewport.Height() / (vpr_max.Y - vpr_min.Y);
  833. const SphereClass & sphere = Get_Bounding_Sphere();
  834. float dist = (sphere.Center - cam).Length();
  835. float bounding_sphere_projected_radius = 0.0f;
  836. float particle_projected_radius = 0.0f;
  837. if (dist) {
  838. float oo_dist = 1.0f / dist;
  839. bounding_sphere_projected_radius = sphere.Radius * oo_dist;
  840. particle_projected_radius = MaxSize * oo_dist;
  841. }
  842. float bs_rad_sq = bounding_sphere_projected_radius * bounding_sphere_projected_radius;
  843. float p_rad_sq = particle_projected_radius * particle_projected_radius * MaxNum;
  844. float proj_area = WWMATH_PI * MIN(bs_rad_sq, p_rad_sq) * width_factor * height_factor;
  845. // Filter the area over time so we don't get as many pops in the LOD algorithm
  846. ProjectedArea = 0.9f * ProjectedArea + 0.1f * proj_area;
  847. int minlod = Calculate_Cost_Value_Arrays(ProjectedArea, Value, Cost);
  848. // Ensure lod is no less than minimum allowed
  849. if (Get_LOD_Level() < minlod) Set_LOD_Level(minlod);
  850. PredictiveLODOptimizerClass::Add_Object(this);
  851. }
  852. void ParticleBufferClass::Increment_LOD(void)
  853. {
  854. if (DecimationThreshold > 0) DecimationThreshold--;
  855. }
  856. void ParticleBufferClass::Decrement_LOD(void)
  857. {
  858. if (DecimationThreshold < LodCount) DecimationThreshold++;
  859. }
  860. float ParticleBufferClass::Get_Cost(void) const
  861. {
  862. return(Cost[(LodCount - 1) - DecimationThreshold]);
  863. }
  864. float ParticleBufferClass::Get_Value(void) const
  865. {
  866. return(Value[(LodCount - 1) - DecimationThreshold]);
  867. }
  868. float ParticleBufferClass::Get_Post_Increment_Value(void) const
  869. {
  870. return(Value[LodCount - DecimationThreshold]);
  871. }
  872. void ParticleBufferClass::Set_LOD_Level(int lod)
  873. {
  874. lod = Bound(lod, 0, (int)LodCount);
  875. DecimationThreshold = (LodCount - 1) - lod;
  876. }
  877. int ParticleBufferClass::Get_LOD_Level(void) const
  878. {
  879. return((LodCount - 1) - DecimationThreshold);
  880. }
  881. int ParticleBufferClass::Get_LOD_Count(void) const
  882. {
  883. return LodCount;
  884. }
  885. int ParticleBufferClass::Calculate_Cost_Value_Arrays(float screen_area, float *values, float *costs) const
  886. {
  887. unsigned int lod = 0;
  888. // Calculate Cost heuristic for each LOD (we currently ignore pixel costs for particle systems)
  889. float tris_per_particle = PointGroup->Get_Point_Mode() == PointGroupClass::QUADS ? 2.0f : 1.0f;
  890. float cost_factor = (float)MaxNum * tris_per_particle * 0.0625f; // 1/16
  891. for (lod = 0; lod < LodCount; lod++) {
  892. costs[lod] = cost_factor * (float)lod;
  893. // If cost is zero set it to a small nonzero amount to avoid divisions by zero.
  894. costs[lod] = (costs[lod] != 0) ? costs[lod] : 0.000001f;
  895. }
  896. // Calculate Value heuristic. First, all LOD levels for which
  897. // MaxScreenSize is smaller than screen_area have their Value set to
  898. // AT_MIN_LOD, as well as the first LOD after that (unless there are no
  899. // other LODs):
  900. for (lod = 0; lod < LodCount && LODMaxScreenSizes[lod] < screen_area; lod++) {
  901. values[lod] = AT_MIN_LOD;
  902. }
  903. if (lod >= LodCount) {
  904. lod = LodCount - 1;
  905. } else {
  906. values[lod] = AT_MIN_LOD;
  907. }
  908. // Now lod is the lowest allowed - return this value.
  909. int minlod = lod;
  910. // Calculate Value heuristic for any remaining LODs based on normalized screen area:
  911. lod++;
  912. for (; lod < LodCount; lod++) {
  913. // Currently the cost happens to be equal to the poly count. We use a floating-
  914. // point poly count since costs[] contains an approximation to the true polycount which may
  915. // be less than one in some cases (we want to avoid 0 polycounts except for true null LODs)
  916. float polycount = costs[lod];
  917. float benefit_factor = (polycount > WWMATH_EPSILON) ? (1 - (0.5f / (polycount * polycount))) : 0.0f;
  918. values[lod] = (benefit_factor * screen_area * LodBias) / costs[lod];
  919. }
  920. values[LodCount] = AT_MAX_LOD; // Post-inc value will flag max LOD.
  921. return minlod;
  922. }
  923. void ParticleBufferClass::Reset_Colors(ParticlePropertyStruct<Vector3> &new_props)
  924. {
  925. unsigned int i; // Used in loops
  926. unsigned int ui_previous_key_time = 0;
  927. unsigned int ui_current_key_time = 0;
  928. ColorRandom = new_props.Rand;
  929. // If the randomizer is effectively zero and there are no keyframes, then we just create a
  930. // values array with one entry and store the starting value in it (the keyframes and random
  931. // table will not be used in this case).
  932. static const float eps_byte = 0.0038f; // Epsilon value - less than 1/255
  933. bool color_rand_zero = (fabs(new_props.Rand.X) < eps_byte && fabs(new_props.Rand.Y) < eps_byte && fabs(new_props.Rand.Z) < eps_byte);
  934. if (color_rand_zero && new_props.NumKeyFrames == 0) {
  935. // Release Color, ColorKeyFrameTimes and ColorKeyFrameDeltas if present. Reuse
  936. // ColorKeyFrameValues if the right size, otherwise release and reallocate.
  937. if (Color) {
  938. Color->Release_Ref();
  939. Color = NULL;
  940. }
  941. if (ColorKeyFrameTimes) {
  942. delete [] ColorKeyFrameTimes;
  943. ColorKeyFrameTimes = NULL;
  944. }
  945. if (ColorKeyFrameDeltas) {
  946. delete [] ColorKeyFrameDeltas;
  947. ColorKeyFrameDeltas = NULL;
  948. }
  949. if (ColorKeyFrameValues) {
  950. if (NumColorKeyFrames > 1) {
  951. delete [] ColorKeyFrameValues;
  952. ColorKeyFrameValues = W3DNEWARRAY Vector3 [1];
  953. }
  954. } else {
  955. ColorKeyFrameValues = W3DNEWARRAY Vector3 [1];
  956. }
  957. NumColorKeyFrames = 0;
  958. NumRandomColorEntriesMinus1 = 0;
  959. ColorKeyFrameValues[0] = new_props.Start;
  960. } else {
  961. // Create the color array if not present
  962. if (!Color) {
  963. Color = NEW_REF( ShareBufferClass<Vector3> , (MaxNum, "ParticleBufferClass::Color") );
  964. }
  965. // Check times of color keyframes (each keytime must be larger than the
  966. // previous one by at least a millisecond, and we stop at the first
  967. // keytime of MaxAge or larger. (If all keyframes below MaxAge, color is
  968. // constant during the last segment between last keyframe and MaxAge).
  969. ui_previous_key_time = 0;
  970. for (unsigned int ckey = 0; ckey < new_props.NumKeyFrames; ckey++) {
  971. ui_current_key_time = (unsigned int)(new_props.KeyTimes[ckey] * 1000.0f);
  972. WWASSERT(ui_current_key_time > ui_previous_key_time);
  973. if (ui_current_key_time >= MaxAge) break;
  974. ui_previous_key_time = ui_current_key_time;
  975. }
  976. bool color_constant_at_end = (ckey == new_props.NumKeyFrames);
  977. // Reuse ColorKeyFrameValues, ColorKeyFrameTimes and ColorKeyFrameDeltas if the right size,
  978. // otherwise release and reallocate.
  979. unsigned int new_num_color_key_frames = ckey + 1;// Includes start keyframe (keytime == 0).
  980. if (new_num_color_key_frames != NumColorKeyFrames) {
  981. if (ColorKeyFrameTimes) {
  982. delete [] ColorKeyFrameTimes;
  983. ColorKeyFrameTimes = NULL;
  984. }
  985. if (ColorKeyFrameValues) {
  986. delete [] ColorKeyFrameValues;
  987. ColorKeyFrameValues = NULL;
  988. }
  989. if (ColorKeyFrameDeltas) {
  990. delete [] ColorKeyFrameDeltas;
  991. ColorKeyFrameDeltas = NULL;
  992. }
  993. NumColorKeyFrames = new_num_color_key_frames;
  994. ColorKeyFrameTimes = W3DNEWARRAY unsigned int [NumColorKeyFrames];
  995. ColorKeyFrameValues = W3DNEWARRAY Vector3 [NumColorKeyFrames];
  996. ColorKeyFrameDeltas = W3DNEWARRAY Vector3 [NumColorKeyFrames];
  997. }
  998. // Set color keyframes (deltas will be set later)
  999. ColorKeyFrameTimes[0] = 0;
  1000. ColorKeyFrameValues[0] = new_props.Start;
  1001. for (i = 1; i < NumColorKeyFrames; i++) {
  1002. unsigned int im1 = i - 1;
  1003. ColorKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
  1004. ColorKeyFrameValues[i] = new_props.Values[im1];
  1005. }
  1006. // Do deltas for all color keyframes except last
  1007. for (i = 0; i < NumColorKeyFrames - 1; i++) {
  1008. ColorKeyFrameDeltas[i] = (ColorKeyFrameValues[i + 1] - ColorKeyFrameValues[i]) /
  1009. (float)(ColorKeyFrameTimes[i + 1] - ColorKeyFrameTimes[i]);
  1010. }
  1011. // Do delta for last color keyframe (i is NumColorKeyFrames - 1)
  1012. if (color_constant_at_end) {
  1013. ColorKeyFrameDeltas[i].Set(0.0, 0.0, 0.0);
  1014. } else {
  1015. // This is OK because if color_constant_at_end is false, NumColorKeyFrames is equal or
  1016. // smaller than color.NumKeyFrames so color.Values[NumColorKeyFrames - 1] and
  1017. // color.KeyTimes[NumColorKeyFrames - 1] exist.
  1018. ColorKeyFrameDeltas[i] = (new_props.Values[i] - ColorKeyFrameValues[i]) /
  1019. (new_props.KeyTimes[i] * 1000.0f - (float)ColorKeyFrameTimes[i]);
  1020. }
  1021. // Set up color randomizer table
  1022. if (color_rand_zero) {
  1023. if (RandomColorEntries) {
  1024. // Reuse RandomColorEntries if the right size, otherwise release and reallocate.
  1025. if (NumRandomColorEntriesMinus1 != 0) {
  1026. delete [] RandomColorEntries;
  1027. RandomColorEntries = W3DNEWARRAY Vector3 [1];
  1028. }
  1029. } else {
  1030. RandomColorEntries = W3DNEWARRAY Vector3 [1];
  1031. }
  1032. NumRandomColorEntriesMinus1 = 0;
  1033. RandomColorEntries[0].X = 0.0f;
  1034. RandomColorEntries[0].Y = 0.0f;
  1035. RandomColorEntries[0].Z = 0.0f;
  1036. } else {
  1037. // Default size of randomizer tables (tables for non-zero randomizers will be this size)
  1038. unsigned int pot_num = Find_POT(MaxNum);
  1039. unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
  1040. if (RandomColorEntries) {
  1041. // Reuse RandomColorEntries if the right size, otherwise release and reallocate.
  1042. if (NumRandomColorEntriesMinus1 != (default_randomizer_entries - 1)) {
  1043. delete [] RandomColorEntries;
  1044. RandomColorEntries = W3DNEWARRAY Vector3 [default_randomizer_entries];
  1045. }
  1046. } else {
  1047. RandomColorEntries = W3DNEWARRAY Vector3 [default_randomizer_entries];
  1048. }
  1049. NumRandomColorEntriesMinus1 = default_randomizer_entries - 1;
  1050. float rscale = new_props.Rand.X * oo_intmax;
  1051. float gscale = new_props.Rand.Y * oo_intmax;
  1052. float bscale = new_props.Rand.Z * oo_intmax;
  1053. for (unsigned int j = 0; j <= NumRandomColorEntriesMinus1; j++) {
  1054. RandomColorEntries[j] = Vector3(rand_gen * rscale, rand_gen * gscale, rand_gen * bscale);
  1055. }
  1056. }
  1057. }
  1058. }
  1059. void ParticleBufferClass::Reset_Opacity(ParticlePropertyStruct<float> &new_props)
  1060. {
  1061. unsigned int i; // Used in loops
  1062. unsigned int ui_previous_key_time = 0;
  1063. unsigned int ui_current_key_time = 0;
  1064. OpacityRandom = new_props.Rand;
  1065. // If the randomizer is effectively zero and there are no keyframes, then we just create a
  1066. // values array with one entry and store the starting value in it (the keyframes and random
  1067. // table will not be used in this case).
  1068. static const float eps_byte = 0.0038f; // Epsilon value - less than 1/255
  1069. bool alpha_rand_zero = (fabs(new_props.Rand) < eps_byte);
  1070. if (alpha_rand_zero && new_props.NumKeyFrames == 0) {
  1071. // Release Alpha, AlphaKeyFrameTimes and AlphaKeyFrameDeltas if present. Reuse
  1072. // AlphaKeyFrameValues if the right size, otherwise release and reallocate.
  1073. if (Alpha) {
  1074. Alpha->Release_Ref();
  1075. Alpha = NULL;
  1076. }
  1077. if (AlphaKeyFrameTimes) {
  1078. delete [] AlphaKeyFrameTimes;
  1079. AlphaKeyFrameTimes = NULL;
  1080. }
  1081. if (AlphaKeyFrameDeltas) {
  1082. delete [] AlphaKeyFrameDeltas;
  1083. AlphaKeyFrameDeltas = NULL;
  1084. }
  1085. if (AlphaKeyFrameValues) {
  1086. if (NumAlphaKeyFrames > 1) {
  1087. delete [] AlphaKeyFrameValues;
  1088. AlphaKeyFrameValues = W3DNEWARRAY float [1];
  1089. }
  1090. } else {
  1091. AlphaKeyFrameValues = W3DNEWARRAY float [1];
  1092. }
  1093. NumAlphaKeyFrames = 0;
  1094. NumRandomAlphaEntriesMinus1 = 0;
  1095. AlphaKeyFrameValues[0] = new_props.Start;
  1096. } else {
  1097. // Create the alpha array if not present
  1098. if (!Alpha) {
  1099. Alpha = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Alpha") );
  1100. }
  1101. // Check times of opacity keyframes (each keytime must be larger than the
  1102. // previous one by at least a millisecond, and we stop at the first
  1103. // keytime of MaxAge or larger. (If all keyframes below MaxAge, alpha is
  1104. // constant during the last segment between last keyframe and MaxAge).
  1105. ui_previous_key_time = 0;
  1106. for (unsigned int akey = 0; akey < new_props.NumKeyFrames; akey++) {
  1107. ui_current_key_time = (unsigned int)(new_props.KeyTimes[akey] * 1000.0f);
  1108. WWASSERT(ui_current_key_time > ui_previous_key_time);
  1109. if (ui_current_key_time >= MaxAge) break;
  1110. ui_previous_key_time = ui_current_key_time;
  1111. }
  1112. bool alpha_constant_at_end = (akey == new_props.NumKeyFrames);
  1113. // Reuse AlphaKeyFrameValues, AlphaKeyFrameTimes and AlphaKeyFrameDeltas if the right size,
  1114. // otherwise release and reallocate.
  1115. unsigned int new_num_alpha_key_frames = akey + 1;// Includes start keyframe (keytime == 0).
  1116. if (new_num_alpha_key_frames != NumAlphaKeyFrames) {
  1117. if (AlphaKeyFrameTimes) {
  1118. delete [] AlphaKeyFrameTimes;
  1119. AlphaKeyFrameTimes = NULL;
  1120. }
  1121. if (AlphaKeyFrameValues) {
  1122. delete [] AlphaKeyFrameValues;
  1123. AlphaKeyFrameValues = NULL;
  1124. }
  1125. if (AlphaKeyFrameDeltas) {
  1126. delete [] AlphaKeyFrameDeltas;
  1127. AlphaKeyFrameDeltas = NULL;
  1128. }
  1129. NumAlphaKeyFrames = new_num_alpha_key_frames;
  1130. AlphaKeyFrameTimes = W3DNEWARRAY unsigned int [NumAlphaKeyFrames];
  1131. AlphaKeyFrameValues = W3DNEWARRAY float [NumAlphaKeyFrames];
  1132. AlphaKeyFrameDeltas = W3DNEWARRAY float [NumAlphaKeyFrames];
  1133. }
  1134. // Set alpha keyframes (deltas will be set later)
  1135. AlphaKeyFrameTimes[0] = 0;
  1136. AlphaKeyFrameValues[0] = new_props.Start;
  1137. for (i = 1; i < NumAlphaKeyFrames; i++) {
  1138. unsigned int im1 = i - 1;
  1139. AlphaKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
  1140. AlphaKeyFrameValues[i] = new_props.Values[im1];
  1141. }
  1142. // Do deltas for all alpha keyframes except last
  1143. for (i = 0; i < NumAlphaKeyFrames - 1; i++) {
  1144. AlphaKeyFrameDeltas[i] = (AlphaKeyFrameValues[i + 1] - AlphaKeyFrameValues[i]) /
  1145. (float)(AlphaKeyFrameTimes[i + 1] - AlphaKeyFrameTimes[i]);
  1146. }
  1147. // Do delta for last alpha keyframe (i is NumAlphaKeyFrames - 1)
  1148. if (alpha_constant_at_end) {
  1149. AlphaKeyFrameDeltas[i] = 0.0f;
  1150. } else {
  1151. // This is OK because if alpha_constant_at_end is false, NumAlphaKeyFrames is equal or
  1152. // smaller than opacity.NumKeyFrames so opacity.Values[NumAlphaKeyFrames - 1] and
  1153. // opacity.KeyTimes[NumAlphaKeyFrames - 1] exist.
  1154. AlphaKeyFrameDeltas[i] = (new_props.Values[i] - AlphaKeyFrameValues[i]) /
  1155. (new_props.KeyTimes[i] * 1000.0f - (float)AlphaKeyFrameTimes[i]);
  1156. }
  1157. // Set up alpha randomizer table
  1158. if (alpha_rand_zero) {
  1159. if (RandomAlphaEntries) {
  1160. // Reuse RandomAlphaEntries if the right size, otherwise release and reallocate.
  1161. if (NumRandomAlphaEntriesMinus1 != 0) {
  1162. delete [] RandomAlphaEntries;
  1163. RandomAlphaEntries = W3DNEWARRAY float [1];
  1164. }
  1165. } else {
  1166. RandomAlphaEntries = W3DNEWARRAY float [1];
  1167. }
  1168. NumRandomAlphaEntriesMinus1 = 0;
  1169. RandomAlphaEntries[0] = 0.0f;
  1170. } else {
  1171. // Default size of randomizer tables (tables for non-zero randomizers will be this size)
  1172. unsigned int pot_num = Find_POT(MaxNum);
  1173. unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
  1174. if (RandomAlphaEntries) {
  1175. // Reuse RandomAlphaEntries if the right size, otherwise release and reallocate.
  1176. if (NumRandomAlphaEntriesMinus1 != (default_randomizer_entries - 1)) {
  1177. delete [] RandomAlphaEntries;
  1178. RandomAlphaEntries = W3DNEWARRAY float [default_randomizer_entries];
  1179. }
  1180. } else {
  1181. RandomAlphaEntries = W3DNEWARRAY float [default_randomizer_entries];
  1182. }
  1183. NumRandomAlphaEntriesMinus1 = default_randomizer_entries - 1;
  1184. float ascale = new_props.Rand * oo_intmax;
  1185. for (unsigned int j = 0; j <= NumRandomAlphaEntriesMinus1; j++) {
  1186. RandomAlphaEntries[j] = rand_gen * ascale;
  1187. }
  1188. }
  1189. }
  1190. }
  1191. void ParticleBufferClass::Reset_Size(ParticlePropertyStruct<float> &new_props)
  1192. {
  1193. unsigned int i; // Used in loops
  1194. unsigned int ui_previous_key_time = 0;
  1195. unsigned int ui_current_key_time = 0;
  1196. SizeRandom = new_props.Rand;
  1197. // If the randomizer is effectively zero and there are no keyframes, then we just create a
  1198. // values array with one entry and store the starting value in it (the keyframes and random
  1199. // table will not be used in this case).
  1200. static const float eps_size = 1.0e-12f; // Size scale unknown so must use very small epsilon
  1201. bool size_rand_zero = (fabs(new_props.Rand) < eps_size);
  1202. if (size_rand_zero && new_props.NumKeyFrames == 0) {
  1203. // Release Size, SizeKeyFrameTimes and SizeaKeyFrameDeltas if present. Reuse
  1204. // SizeKeyFrameValues if the right size, otherwise release and reallocate.
  1205. if (Size) {
  1206. Size->Release_Ref();
  1207. Size = NULL;
  1208. }
  1209. if (SizeKeyFrameTimes) {
  1210. delete [] SizeKeyFrameTimes;
  1211. SizeKeyFrameTimes = NULL;
  1212. }
  1213. if (SizeKeyFrameDeltas) {
  1214. delete [] SizeKeyFrameDeltas;
  1215. SizeKeyFrameDeltas = NULL;
  1216. }
  1217. if (SizeKeyFrameValues) {
  1218. if (NumSizeKeyFrames > 1) {
  1219. delete [] SizeKeyFrameValues;
  1220. SizeKeyFrameValues = W3DNEWARRAY float [1];
  1221. }
  1222. } else {
  1223. SizeKeyFrameValues = W3DNEWARRAY float [1];
  1224. }
  1225. NumSizeKeyFrames = 0;
  1226. NumRandomSizeEntriesMinus1 = 0;
  1227. SizeKeyFrameValues[0] = new_props.Start;
  1228. MaxSize = SizeKeyFrameValues[0];
  1229. } else {
  1230. // Create the size array if not present
  1231. if (!Size) {
  1232. Size = NEW_REF( ShareBufferClass<float> , (MaxNum, "ParticleBufferClass::Size") );
  1233. }
  1234. // Check times of size keyframes (each keytime must be larger than the
  1235. // previous one by at least a millisecond, and we stop at the first
  1236. // keytime of MaxAge or larger. (If all keyframes below MaxAge, size is
  1237. // constant during the last segment between last keyframe and MaxAge).
  1238. ui_previous_key_time = 0;
  1239. for (unsigned int skey = 0; skey < new_props.NumKeyFrames; skey++) {
  1240. ui_current_key_time = (unsigned int)(new_props.KeyTimes[skey] * 1000.0f);
  1241. WWASSERT(ui_current_key_time > ui_previous_key_time);
  1242. if (ui_current_key_time >= MaxAge) break;
  1243. ui_previous_key_time = ui_current_key_time;
  1244. }
  1245. bool size_constant_at_end = (skey == new_props.NumKeyFrames);
  1246. // Reuse SizeKeyFrameValues, SizeKeyFrameTimes and SizeKeyFrameDeltas if the right size,
  1247. // otherwise release and reallocate.
  1248. unsigned int new_num_size_key_frames = skey + 1;// Includes start keyframe (keytime == 0).
  1249. if (new_num_size_key_frames != NumSizeKeyFrames) {
  1250. if (SizeKeyFrameTimes) {
  1251. delete [] SizeKeyFrameTimes;
  1252. SizeKeyFrameTimes = NULL;
  1253. }
  1254. if (SizeKeyFrameValues) {
  1255. delete [] SizeKeyFrameValues;
  1256. SizeKeyFrameValues = NULL;
  1257. }
  1258. if (SizeKeyFrameDeltas) {
  1259. delete [] SizeKeyFrameDeltas;
  1260. SizeKeyFrameDeltas = NULL;
  1261. }
  1262. NumSizeKeyFrames = new_num_size_key_frames;
  1263. SizeKeyFrameTimes = W3DNEWARRAY unsigned int [NumSizeKeyFrames];
  1264. SizeKeyFrameValues = W3DNEWARRAY float [NumSizeKeyFrames];
  1265. SizeKeyFrameDeltas = W3DNEWARRAY float [NumSizeKeyFrames];
  1266. }
  1267. // Set size keyframes (deltas will be set later)
  1268. SizeKeyFrameTimes[0] = 0;
  1269. SizeKeyFrameValues[0] = new_props.Start;
  1270. for (i = 1; i < NumSizeKeyFrames; i++) {
  1271. unsigned int im1 = i - 1;
  1272. SizeKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
  1273. SizeKeyFrameValues[i] = new_props.Values[im1];
  1274. }
  1275. // Do deltas for all size keyframes except last
  1276. for (i = 0; i < NumSizeKeyFrames - 1; i++) {
  1277. SizeKeyFrameDeltas[i] = (SizeKeyFrameValues[i + 1] - SizeKeyFrameValues[i]) /
  1278. (float)(SizeKeyFrameTimes[i + 1] - SizeKeyFrameTimes[i]);
  1279. }
  1280. // Do delta for last size keyframe (i is NumSizeKeyFrames - 1)
  1281. if (size_constant_at_end) {
  1282. SizeKeyFrameDeltas[i] = 0.0f;
  1283. } else {
  1284. // This is OK because if size_constant_at_end is false, NumSizeKeyFrames is equal or
  1285. // smaller than new_props.NumKeyFrames so new_props.Values[NumSizeKeyFrames - 1] and
  1286. // new_props.KeyTimes[NumSizeKeyFrames - 1] exist.
  1287. SizeKeyFrameDeltas[i] = (new_props.Values[i] - SizeKeyFrameValues[i]) /
  1288. (new_props.KeyTimes[i] * 1000.0f - (float)SizeKeyFrameTimes[i]);
  1289. }
  1290. // Find maximum size (for BBox updates)
  1291. MaxSize = SizeKeyFrameValues[0];
  1292. for (i = 1; i < NumSizeKeyFrames; i++) {
  1293. MaxSize = MAX(MaxSize, SizeKeyFrameValues[i]);
  1294. }
  1295. // If last delta is positive, there may be a larger size keyframe:
  1296. float last_size = SizeKeyFrameValues[NumSizeKeyFrames - 1] + SizeKeyFrameDeltas[NumSizeKeyFrames - 1] *
  1297. (float)(MaxAge - SizeKeyFrameTimes[NumSizeKeyFrames - 1]);
  1298. MaxSize = MAX(MaxSize, last_size);
  1299. MaxSize += fabs(new_props.Rand);
  1300. // Set up size randomizer table
  1301. if (size_rand_zero) {
  1302. if (RandomSizeEntries) {
  1303. // Reuse RandomSizeEntries if the right size, otherwise release and reallocate.
  1304. if (NumRandomSizeEntriesMinus1 != 0) {
  1305. delete [] RandomSizeEntries;
  1306. RandomSizeEntries = W3DNEWARRAY float [1];
  1307. }
  1308. } else {
  1309. RandomSizeEntries = W3DNEWARRAY float [1];
  1310. }
  1311. NumRandomSizeEntriesMinus1 = 0;
  1312. RandomSizeEntries[0] = 0.0f;
  1313. } else {
  1314. // Default size of randomizer tables (tables for non-zero randomizers will be this size)
  1315. unsigned int pot_num = Find_POT(MaxNum);
  1316. unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
  1317. if (RandomSizeEntries) {
  1318. // Reuse RandomSizeEntries if the right size, otherwise release and reallocate.
  1319. if (NumRandomSizeEntriesMinus1 != (default_randomizer_entries - 1)) {
  1320. delete [] RandomSizeEntries;
  1321. RandomSizeEntries = W3DNEWARRAY float [default_randomizer_entries];
  1322. }
  1323. } else {
  1324. RandomSizeEntries = W3DNEWARRAY float [default_randomizer_entries];
  1325. }
  1326. NumRandomSizeEntriesMinus1 = default_randomizer_entries - 1;
  1327. float sscale = new_props.Rand * oo_intmax;
  1328. for (unsigned int j = 0; j <= NumRandomSizeEntriesMinus1; j++) {
  1329. RandomSizeEntries[j] = rand_gen * sscale;
  1330. }
  1331. }
  1332. }
  1333. }
  1334. void ParticleBufferClass::Reset_Rotations(ParticlePropertyStruct<float> &new_props, float orient_rnd)
  1335. {
  1336. unsigned int i; // Used in loops
  1337. static Random3Class rand_gen;
  1338. float oo_intmax = 1.0f / (float)INT_MAX;
  1339. unsigned int ui_previous_key_time = 0;
  1340. unsigned int ui_current_key_time = 0;
  1341. /*
  1342. ** NOTE: Input rotations are in rotations per second. These will be converted to rotations per millisecond.
  1343. */
  1344. RotationRandom = new_props.Rand * 0.001f;
  1345. InitialOrientationRandom = orient_rnd;
  1346. // If both randomizers are effectively zero and rotation is constant zero, then all arrays are NULL.
  1347. static const float eps_orientation = 2.77777778e-4f; // Epsilon is equivalent to 0.1 degree
  1348. static const float eps_rotation = 2.77777778e-4f; // Epsilon is equivalent to one rotation per hour (in rotations / second)
  1349. bool orientation_rand_zero = fabs(orient_rnd) < eps_orientation;
  1350. bool rotation_rand_zero = fabs(new_props.Rand) < eps_rotation;
  1351. if (orientation_rand_zero && rotation_rand_zero && new_props.NumKeyFrames == 0 && fabs(new_props.Start) < eps_rotation) {
  1352. // Release Arrays,
  1353. REF_PTR_RELEASE(Orientation);
  1354. if (RotationKeyFrameTimes) {
  1355. delete [] RotationKeyFrameTimes;
  1356. RotationKeyFrameTimes = NULL;
  1357. }
  1358. if (HalfRotationKeyFrameDeltas) {
  1359. delete [] HalfRotationKeyFrameDeltas;
  1360. HalfRotationKeyFrameDeltas = NULL;
  1361. }
  1362. if (RotationKeyFrameValues) {
  1363. delete [] RotationKeyFrameValues;
  1364. RotationKeyFrameValues = NULL;
  1365. }
  1366. if (OrientationKeyFrameValues) {
  1367. delete [] OrientationKeyFrameValues;
  1368. OrientationKeyFrameValues = NULL;
  1369. }
  1370. NumRotationKeyFrames = 0;
  1371. NumRandomRotationEntriesMinus1 = 0;
  1372. NumRandomOrientationEntriesMinus1 = 0;
  1373. } else {
  1374. // Create the array if not present
  1375. if (!Orientation) {
  1376. Orientation = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Orientation") );
  1377. }
  1378. // Check times of the keyframes (each keytime must be larger than the
  1379. // previous one by at least a millisecond, and we stop at the first
  1380. // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is
  1381. // constant during the last segment between last keyframe and MaxAge).
  1382. ui_previous_key_time = 0;
  1383. for (unsigned int key = 0; key < new_props.NumKeyFrames; key++) {
  1384. ui_current_key_time = (unsigned int)(new_props.KeyTimes[key] * 1000.0f);
  1385. WWASSERT(ui_current_key_time > ui_previous_key_time);
  1386. if (ui_current_key_time >= MaxAge) break;
  1387. ui_previous_key_time = ui_current_key_time;
  1388. }
  1389. bool rotation_constant_at_end = (key == new_props.NumKeyFrames);
  1390. // Reuse RotationKeyFrameValues, RotationKeyFrameTimes, RotationKeyFrameDeltas and
  1391. // OrientationKeyFrameValues if the right size, otherwise release and reallocate.
  1392. unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0).
  1393. if (new_num_key_frames != NumRotationKeyFrames) {
  1394. if (RotationKeyFrameTimes) {
  1395. delete [] RotationKeyFrameTimes;
  1396. RotationKeyFrameTimes = NULL;
  1397. }
  1398. if (RotationKeyFrameValues) {
  1399. delete [] RotationKeyFrameValues;
  1400. RotationKeyFrameValues = NULL;
  1401. }
  1402. if (HalfRotationKeyFrameDeltas) {
  1403. delete [] HalfRotationKeyFrameDeltas;
  1404. HalfRotationKeyFrameDeltas = NULL;
  1405. }
  1406. if (OrientationKeyFrameValues) {
  1407. delete [] OrientationKeyFrameValues;
  1408. OrientationKeyFrameValues = NULL;
  1409. }
  1410. NumRotationKeyFrames = new_num_key_frames;
  1411. RotationKeyFrameTimes = W3DNEWARRAY unsigned int [NumRotationKeyFrames];
  1412. RotationKeyFrameValues = W3DNEWARRAY float [NumRotationKeyFrames];
  1413. HalfRotationKeyFrameDeltas = W3DNEWARRAY float [NumRotationKeyFrames];
  1414. OrientationKeyFrameValues = W3DNEWARRAY float [NumRotationKeyFrames];
  1415. }
  1416. // Set rotation keyframes (deltas will be set later)
  1417. RotationKeyFrameTimes[0] = 0;
  1418. RotationKeyFrameValues[0] = new_props.Start * 0.001f;
  1419. for (i = 1; i < NumRotationKeyFrames; i++) {
  1420. unsigned int im1 = i - 1;
  1421. RotationKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
  1422. RotationKeyFrameValues[i] = new_props.Values[im1] * 0.001f;
  1423. }
  1424. // Do deltas for all rotation keyframes except last
  1425. for (i = 0; i < NumRotationKeyFrames - 1; i++) {
  1426. HalfRotationKeyFrameDeltas[i] = 0.5f * ( (RotationKeyFrameValues[i + 1] - RotationKeyFrameValues[i]) /
  1427. (float)(RotationKeyFrameTimes[i + 1] - RotationKeyFrameTimes[i]) );
  1428. }
  1429. // Do delta for last rotation keyframe (i is NumRotationKeyFrames - 1)
  1430. if (rotation_constant_at_end) {
  1431. HalfRotationKeyFrameDeltas[i] = 0.0f;
  1432. } else {
  1433. // This is OK because if rotation_constant_at_end is false, NumRotationKeyFrames is equal or
  1434. // smaller than new_props.NumKeyFrames so new_props.Values[NumRotationKeyFrames - 1] and
  1435. // new_props.KeyTimes[NumRotationKeyFrames - 1] exist.
  1436. HalfRotationKeyFrameDeltas[i] = 0.5f * (new_props.Values[i] * 0.001f - RotationKeyFrameValues[i]) /
  1437. (new_props.KeyTimes[i] * 1000.0f - (float)RotationKeyFrameTimes[i]);
  1438. }
  1439. // Calculate orientation keyframes by integrating the rotation at each keyframe
  1440. OrientationKeyFrameValues[0] = 0.0f;
  1441. for (i = 1; i < NumRotationKeyFrames; i++) {
  1442. float delta_t = (float)(RotationKeyFrameTimes[i] - RotationKeyFrameTimes[i - 1]);
  1443. OrientationKeyFrameValues[i] = OrientationKeyFrameValues[i - 1] + delta_t *
  1444. (RotationKeyFrameValues[i - 1] + HalfRotationKeyFrameDeltas[i - 1] * delta_t);
  1445. }
  1446. // Set up rotation randomizer table
  1447. if (rotation_rand_zero) {
  1448. if (RandomRotationEntries) {
  1449. // Reuse RandomRotationEntries if the right size, otherwise release and reallocate.
  1450. if (NumRandomRotationEntriesMinus1 != 0) {
  1451. delete [] RandomRotationEntries;
  1452. RandomRotationEntries = W3DNEWARRAY float [1];
  1453. }
  1454. } else {
  1455. RandomRotationEntries = W3DNEWARRAY float [1];
  1456. }
  1457. NumRandomRotationEntriesMinus1 = 0;
  1458. RandomRotationEntries[0] = 0.0f;
  1459. } else {
  1460. // Default size of randomizer tables (tables for non-zero randomizers will be this size)
  1461. unsigned int pot_num = Find_POT(MaxNum);
  1462. unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
  1463. if (RandomRotationEntries) {
  1464. // Reuse RandomRotationEntries if the right size, otherwise release and reallocate.
  1465. if (NumRandomRotationEntriesMinus1 != (default_randomizer_entries - 1)) {
  1466. delete [] RandomRotationEntries;
  1467. RandomRotationEntries = W3DNEWARRAY float [default_randomizer_entries];
  1468. }
  1469. } else {
  1470. RandomRotationEntries = W3DNEWARRAY float [default_randomizer_entries];
  1471. }
  1472. NumRandomRotationEntriesMinus1 = default_randomizer_entries - 1;
  1473. float scale = new_props.Rand * 0.001f * oo_intmax;
  1474. for (unsigned int j = 0; j <= NumRandomRotationEntriesMinus1; j++) {
  1475. RandomRotationEntries[j] = rand_gen * scale;
  1476. }
  1477. }
  1478. // Set up orientation randomizer table
  1479. if (orientation_rand_zero) {
  1480. if (RandomOrientationEntries) {
  1481. // Reuse RandomOrientationEntries if the right size, otherwise release and reallocate.
  1482. if (NumRandomOrientationEntriesMinus1 != 0) {
  1483. delete [] RandomOrientationEntries;
  1484. RandomOrientationEntries = W3DNEWARRAY float [1];
  1485. }
  1486. } else {
  1487. RandomOrientationEntries = W3DNEWARRAY float [1];
  1488. }
  1489. NumRandomOrientationEntriesMinus1 = 0;
  1490. RandomOrientationEntries[0] = 0.0f;
  1491. } else {
  1492. // Default size of randomizer tables (tables for non-zero randomizers will be this size)
  1493. unsigned int pot_num = Find_POT(MaxNum);
  1494. unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
  1495. if (RandomOrientationEntries) {
  1496. // Reuse RandomOrientationEntries if the right size, otherwise release and reallocate.
  1497. if (NumRandomOrientationEntriesMinus1 != (default_randomizer_entries - 1)) {
  1498. delete [] RandomOrientationEntries;
  1499. RandomOrientationEntries = W3DNEWARRAY float [default_randomizer_entries];
  1500. }
  1501. } else {
  1502. RandomOrientationEntries = W3DNEWARRAY float [default_randomizer_entries];
  1503. }
  1504. NumRandomOrientationEntriesMinus1 = default_randomizer_entries - 1;
  1505. float scale = orient_rnd * oo_intmax;
  1506. for (unsigned int j = 0; j <= NumRandomOrientationEntriesMinus1; j++) {
  1507. RandomOrientationEntries[j] = rand_gen * scale;
  1508. }
  1509. }
  1510. }
  1511. }
  1512. void ParticleBufferClass::Reset_Frames(ParticlePropertyStruct<float> &new_props)
  1513. {
  1514. unsigned int i; // Used in loops
  1515. static Random3Class rand_gen;
  1516. float oo_intmax = 1.0f / (float)INT_MAX;
  1517. unsigned int ui_previous_key_time = 0;
  1518. unsigned int ui_current_key_time = 0;
  1519. FrameRandom = new_props.Rand;
  1520. // If the randomizer is effectively zero and there are no keyframes, then we just create a
  1521. // values array with one entry and store the starting value in it (the keyframes and random
  1522. // table will not be used in this case).
  1523. static const float eps_frame = 0.1f; // Epsilon is equivalent to 0.1 frame
  1524. bool frame_rand_zero = (fabs(new_props.Rand) < eps_frame);
  1525. if (frame_rand_zero && new_props.NumKeyFrames == 0) {
  1526. // Release Arrays, Reuse KeyFrameValues if the right size,
  1527. // otherwise release and reallocate.
  1528. REF_PTR_RELEASE(Frame);
  1529. if (FrameKeyFrameTimes) {
  1530. delete [] FrameKeyFrameTimes;
  1531. FrameKeyFrameTimes = NULL;
  1532. }
  1533. if (FrameKeyFrameDeltas) {
  1534. delete [] FrameKeyFrameDeltas;
  1535. FrameKeyFrameDeltas = NULL;
  1536. }
  1537. if (FrameKeyFrameValues) {
  1538. if (NumFrameKeyFrames > 1) {
  1539. delete [] FrameKeyFrameValues;
  1540. FrameKeyFrameValues = W3DNEWARRAY float [1];
  1541. }
  1542. } else {
  1543. FrameKeyFrameValues = W3DNEWARRAY float [1];
  1544. }
  1545. NumFrameKeyFrames = 0;
  1546. NumRandomFrameEntriesMinus1 = 0;
  1547. FrameKeyFrameValues[0] = new_props.Start;
  1548. } else {
  1549. // Create the array if not present
  1550. if (!Frame) {
  1551. Frame = NEW_REF( ShareBufferClass<uint8> , (MaxNum, "ParticleBufferClass::Frame") );
  1552. }
  1553. // Check times of the keyframes (each keytime must be larger than the
  1554. // previous one by at least a millisecond, and we stop at the first
  1555. // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is
  1556. // constant during the last segment between last keyframe and MaxAge).
  1557. ui_previous_key_time = 0;
  1558. for (unsigned int key = 0; key < new_props.NumKeyFrames; key++) {
  1559. ui_current_key_time = (unsigned int)(new_props.KeyTimes[key] * 1000.0f);
  1560. WWASSERT(ui_current_key_time > ui_previous_key_time);
  1561. if (ui_current_key_time >= MaxAge) break;
  1562. ui_previous_key_time = ui_current_key_time;
  1563. }
  1564. bool frame_constant_at_end = (key == new_props.NumKeyFrames);
  1565. // Reuse FrameKeyFrameValues, FrameKeyFrameTimes and FrameKeyFrameDeltas if the right size,
  1566. // otherwise release and reallocate.
  1567. unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0).
  1568. if (new_num_key_frames != NumFrameKeyFrames) {
  1569. if (FrameKeyFrameTimes) {
  1570. delete [] FrameKeyFrameTimes;
  1571. FrameKeyFrameTimes = NULL;
  1572. }
  1573. if (FrameKeyFrameValues) {
  1574. delete [] FrameKeyFrameValues;
  1575. FrameKeyFrameValues = NULL;
  1576. }
  1577. if (FrameKeyFrameDeltas) {
  1578. delete [] FrameKeyFrameDeltas;
  1579. FrameKeyFrameDeltas = NULL;
  1580. }
  1581. NumFrameKeyFrames = new_num_key_frames;
  1582. FrameKeyFrameTimes = W3DNEWARRAY unsigned int [NumFrameKeyFrames];
  1583. FrameKeyFrameValues = W3DNEWARRAY float [NumFrameKeyFrames];
  1584. FrameKeyFrameDeltas = W3DNEWARRAY float [NumFrameKeyFrames];
  1585. }
  1586. // Set keyframes (deltas will be set later)
  1587. FrameKeyFrameTimes[0] = 0;
  1588. FrameKeyFrameValues[0] = new_props.Start;
  1589. for (i = 1; i < NumFrameKeyFrames; i++) {
  1590. unsigned int im1 = i - 1;
  1591. FrameKeyFrameTimes[i] = (unsigned int)(new_props.KeyTimes[im1] * 1000.0f);
  1592. FrameKeyFrameValues[i] = new_props.Values[im1];
  1593. }
  1594. // Do deltas for all frame keyframes except last
  1595. for (i = 0; i < NumFrameKeyFrames - 1; i++) {
  1596. FrameKeyFrameDeltas[i] = (FrameKeyFrameValues[i + 1] - FrameKeyFrameValues[i]) /
  1597. (float)(FrameKeyFrameTimes[i + 1] - FrameKeyFrameTimes[i]);
  1598. }
  1599. // Do delta for last frame keyframe (i is NumFrameKeyFrames - 1)
  1600. if (frame_constant_at_end) {
  1601. FrameKeyFrameDeltas[i] = 0.0f;
  1602. } else {
  1603. // This is OK because if frame_constant_at_end is false, NumFrameKeyFrames is equal or
  1604. // smaller than new_props.NumKeyFrames so new_props.Values[NumFrameKeyFrames - 1] and
  1605. // new_props.KeyTimes[NumFrameKeyFrames - 1] exist.
  1606. FrameKeyFrameDeltas[i] = (new_props.Values[i] - FrameKeyFrameValues[i]) /
  1607. (new_props.KeyTimes[i] * 1000.0f - (float)FrameKeyFrameTimes[i]);
  1608. }
  1609. // Set up frame randomizer table
  1610. if (frame_rand_zero) {
  1611. if (RandomFrameEntries) {
  1612. // Reuse RandomFrameEntries if the right size, otherwise release and reallocate.
  1613. if (NumRandomFrameEntriesMinus1 != 0) {
  1614. delete [] RandomFrameEntries;
  1615. RandomFrameEntries = W3DNEWARRAY float [1];
  1616. }
  1617. } else {
  1618. RandomFrameEntries = W3DNEWARRAY float [1];
  1619. }
  1620. NumRandomFrameEntriesMinus1 = 0;
  1621. RandomFrameEntries[0] = 0.0f;
  1622. } else {
  1623. // Default size of randomizer tables (tables for non-zero randomizers will be this size)
  1624. unsigned int pot_num = Find_POT(MaxNum);
  1625. unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES);
  1626. if (RandomFrameEntries) {
  1627. // Reuse RandomFrameEntries if the right size, otherwise release and reallocate.
  1628. if (NumRandomFrameEntriesMinus1 != (default_randomizer_entries - 1)) {
  1629. delete [] RandomFrameEntries;
  1630. RandomFrameEntries = W3DNEWARRAY float [default_randomizer_entries];
  1631. }
  1632. } else {
  1633. RandomFrameEntries = W3DNEWARRAY float [default_randomizer_entries];
  1634. }
  1635. NumRandomFrameEntriesMinus1 = default_randomizer_entries - 1;
  1636. float scale = new_props.Rand * oo_intmax;
  1637. for (unsigned int j = 0; j <= NumRandomFrameEntriesMinus1; j++) {
  1638. RandomFrameEntries[j] = rand_gen * scale;
  1639. }
  1640. }
  1641. }
  1642. }
  1643. // This informs the buffer that the emitter is dead, so it can release
  1644. // its pointer to it and be removed itself after all its particles dies
  1645. // out.
  1646. void ParticleBufferClass::Emitter_Is_Dead(void)
  1647. {
  1648. IsEmitterDead = true;
  1649. // We do not have a ref for the emitter (see DTor for detailed explanation)
  1650. // Emitter->Release_Ref();
  1651. Emitter = NULL;
  1652. }
  1653. // This set's the buffer's current emitter - this should usually be
  1654. // called only by the emitter's copy constructor after it clones a
  1655. // buffer.
  1656. void ParticleBufferClass::Set_Emitter(ParticleEmitterClass *emitter)
  1657. {
  1658. if (Emitter) {
  1659. // We do not have a ref for the emitter (see DTor for detailed explanation)
  1660. // Emitter->Release_Ref();
  1661. Emitter = NULL;
  1662. }
  1663. Emitter = emitter;
  1664. if (Emitter) {
  1665. // We do not add a ref for the emitter (see DTor for detailed explanation)
  1666. // Emitter->Add_Ref();
  1667. }
  1668. }
  1669. NewParticleStruct * ParticleBufferClass::Add_Uninitialized_New_Particle(void)
  1670. {
  1671. // Note that this function does not initialize the new particle - it
  1672. // returns its address to a different function which performs the actual
  1673. // initialization.
  1674. // Push new particle on new particle queue. If it overflows, just adjust
  1675. // queue to remove oldest member (which is the one which was overwritten).
  1676. NewParticleStruct *ptr = &(NewParticleQueue[NewParticleQueueEnd]);
  1677. if (++NewParticleQueueEnd == MaxNum) NewParticleQueueEnd = 0;
  1678. if (++NewParticleQueueCount == (signed)(MaxNum + 1)) {
  1679. // Overflow - advance queue start:
  1680. if (++NewParticleQueueStart == MaxNum) NewParticleQueueStart = 0;
  1681. NewParticleQueueCount--;
  1682. }
  1683. return ptr;
  1684. }
  1685. void ParticleBufferClass::Update_Cached_Bounding_Volumes(void) const
  1686. {
  1687. // This ugly cast is done because the alternative is to make everything
  1688. // in the class mutable, which does not seem like a good solution
  1689. // (Update_Bounding_Box can potentially update the particle state).
  1690. ((ParticleBufferClass *)this)->Update_Bounding_Box();
  1691. // Update cached bounding box and sphere according to the bounding box:
  1692. CachedBoundingSphere.Init(BoundingBox.Center, BoundingBox.Extent.Length());
  1693. CachedBoundingBox = BoundingBox;
  1694. Validate_Cached_Bounding_Volumes();
  1695. }
  1696. void ParticleBufferClass::Update_Kinematic_Particle_State(void)
  1697. {
  1698. // Note: elapsed may be very large indeed the first time the object is
  1699. // updated, but this doesn't matter, since it is actually only used in
  1700. // Update_Non_New_Particles(), which is never called on the first update.
  1701. unsigned int elapsed = WW3D::Get_Sync_Time() - LastUpdateTime;
  1702. if (elapsed == 0U) return;
  1703. // Get new particles from the input buffer and write them into the circular
  1704. // particle buffer, possibly overwriting older particles. Update each
  1705. // according to its age.
  1706. Get_New_Particles();
  1707. // Kill all remaining particles which will pass their max age this update.
  1708. Kill_Old_Particles();
  1709. // Update all living, non-new particles by a uniform time interval.
  1710. if (NonNewNum > 0) Update_Non_New_Particles(elapsed);
  1711. // Mark all new particles as non-new.
  1712. End = NewEnd;
  1713. NonNewNum += NewNum;
  1714. NewNum = 0;
  1715. LastUpdateTime = WW3D::Get_Sync_Time();
  1716. BoundingBoxDirty = true;
  1717. }
  1718. void ParticleBufferClass::Update_Visual_Particle_State(void)
  1719. {
  1720. // NOTE: The visual state (color/alpha/size) is "stateless" in that each time it is calculated
  1721. // without referring to what it was before. This is important for when we optimize the particle
  1722. // systems/pointgroups in the future to chunk triangles into reusable small buffers.
  1723. // If all visual state is constant do nothing.
  1724. if (!Color && !Alpha && !Size && !Orientation && !Frame) return;
  1725. // In the general case, a range in a circular buffer can be composed of up
  1726. // to two subranges. Find the Start - End subranges.
  1727. unsigned int sub1_end; // End of subrange 1.
  1728. unsigned int sub2_start; // Start of subrange 2.
  1729. if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
  1730. sub1_end = End;
  1731. sub2_start = End;
  1732. } else {
  1733. sub1_end = MaxNum;
  1734. sub2_start = 0;
  1735. }
  1736. unsigned int current_time = WW3D::Get_Sync_Time();
  1737. // The following back-to-back pair of "for" loops traverses the circular
  1738. // buffer subranges in proper order.
  1739. unsigned int ckey = NumColorKeyFrames - 1;
  1740. unsigned int akey = NumAlphaKeyFrames - 1;
  1741. unsigned int skey = NumSizeKeyFrames - 1;
  1742. unsigned int rkey = NumRotationKeyFrames - 1;
  1743. unsigned int fkey = NumFrameKeyFrames - 1;
  1744. unsigned int part;
  1745. Vector3 *color = Color ? Color->Get_Array(): NULL;
  1746. float *alpha = Alpha ? Alpha->Get_Array(): NULL;
  1747. float *size = Size ? Size->Get_Array(): NULL;
  1748. uint8 *orientation = Orientation ? Orientation->Get_Array(): NULL;
  1749. uint8 *frame = Frame ? Frame->Get_Array(): NULL;
  1750. for (part = Start; part < sub1_end; part++) {
  1751. unsigned int part_age = current_time - TimeStamp[part];
  1752. // Ensure the current color keyframe is correct, and calculate color state
  1753. if (color) {
  1754. // We go from older to younger particles, so we go backwards from the last keyframe until
  1755. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1756. for (; part_age < ColorKeyFrameTimes[ckey]; ckey--);
  1757. color[part] = ColorKeyFrameValues[ckey] +
  1758. ColorKeyFrameDeltas[ckey] * (float)(part_age - ColorKeyFrameTimes[ckey]) +
  1759. RandomColorEntries[part & NumRandomColorEntriesMinus1];
  1760. }
  1761. // Ensure the current alpha keyframe is correct, and calculate alpha state
  1762. if (alpha) {
  1763. // We go from older to younger particles, so we go backwards from the last keyframe until
  1764. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1765. for (; part_age < AlphaKeyFrameTimes[akey]; akey--);
  1766. alpha[part] = AlphaKeyFrameValues[akey] +
  1767. AlphaKeyFrameDeltas[akey] * (float)(part_age - AlphaKeyFrameTimes[akey]) +
  1768. RandomAlphaEntries[part & NumRandomAlphaEntriesMinus1];
  1769. }
  1770. // Ensure the current size keyframe is correct, and calculate size state
  1771. if (size) {
  1772. // We go from older to younger particles, so we go backwards from the last keyframe until
  1773. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1774. for (; part_age < SizeKeyFrameTimes[skey]; skey--);
  1775. size[part] = SizeKeyFrameValues[skey] +
  1776. SizeKeyFrameDeltas[skey] * (float)(part_age - SizeKeyFrameTimes[skey]) +
  1777. RandomSizeEntries[part & NumRandomSizeEntriesMinus1];
  1778. // Size (unlike color and alpha) isn't clamped in the engine, so we need to clamp
  1779. // negative values to zero here:
  1780. size[part] = (size[part] >= 0.0f) ? size[part] : 0.0f;
  1781. }
  1782. // Ensure the current rotation keyframe is correct, and calculate orientation state
  1783. if (orientation) {
  1784. // We go from older to younger particles, so we go backwards from the last keyframe until
  1785. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1786. for (; part_age < RotationKeyFrameTimes[rkey]; rkey--);
  1787. float f_delta_t = (float)(part_age - RotationKeyFrameTimes[rkey]);
  1788. float tmp_orient = OrientationKeyFrameValues[rkey] +
  1789. (RotationKeyFrameValues[rkey] + HalfRotationKeyFrameDeltas[rkey] * f_delta_t) * f_delta_t +
  1790. RandomRotationEntries[part & NumRandomRotationEntriesMinus1] * (float)part_age +
  1791. RandomOrientationEntries[part & NumRandomOrientationEntriesMinus1];
  1792. orientation[part] = (uint)(((int)(tmp_orient * 256.0f)) & 0xFF);
  1793. }
  1794. // Ensure the current frame keyframe is correct, and calculate frame state
  1795. if (frame) {
  1796. // We go from older to younger particles, so we go backwards from the last keyframe until
  1797. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1798. for (; part_age < FrameKeyFrameTimes[fkey]; fkey--);
  1799. float tmp_frame = FrameKeyFrameValues[fkey] +
  1800. FrameKeyFrameDeltas[fkey] * (float)(part_age - FrameKeyFrameTimes[fkey]) +
  1801. RandomFrameEntries[part & NumRandomFrameEntriesMinus1];
  1802. frame[part] = (uint)(((int)(tmp_frame)) & 0xFF);
  1803. }
  1804. }
  1805. for (part = sub2_start; part < End; part++) {
  1806. unsigned int part_age = current_time - TimeStamp[part];
  1807. // Ensure the current color keyframe is correct, and calculate color state
  1808. if (color) {
  1809. // We go from older to younger particles, so we go backwards from the last keyframe until
  1810. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1811. for (; part_age < ColorKeyFrameTimes[ckey]; ckey--);
  1812. color[part] =
  1813. ColorKeyFrameValues[ckey] +
  1814. ColorKeyFrameDeltas[ckey] * (float)(part_age - ColorKeyFrameTimes[ckey]) +
  1815. RandomColorEntries[part & NumRandomColorEntriesMinus1];
  1816. }
  1817. // Ensure the current alpha keyframe is correct, and calculate alpha state
  1818. if (alpha) {
  1819. // We go from older to younger particles, so we go backwards from the last keyframe until
  1820. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1821. for (; part_age < AlphaKeyFrameTimes[akey]; akey--);
  1822. alpha[part] = AlphaKeyFrameValues[akey] +
  1823. AlphaKeyFrameDeltas[akey] * (float)(part_age - AlphaKeyFrameTimes[akey]) +
  1824. RandomAlphaEntries[part & NumRandomAlphaEntriesMinus1];
  1825. }
  1826. // Ensure the current size keyframe is correct, and calculate size state
  1827. if (size) {
  1828. // We go from older to younger particles, so we go backwards from the last keyframe until
  1829. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1830. for (; part_age < SizeKeyFrameTimes[skey]; skey--);
  1831. size[part] = SizeKeyFrameValues[skey] +
  1832. SizeKeyFrameDeltas[skey] * (float)(part_age - SizeKeyFrameTimes[skey]) +
  1833. RandomSizeEntries[part & NumRandomSizeEntriesMinus1];
  1834. // Size (unlike color) isn't clamped in the engine, so we need to
  1835. // clamp negative values to zero here:
  1836. size[part] = (size[part] >= 0.0f) ? size[part] : 0.0f;
  1837. }
  1838. // Ensure the current rotation keyframe is correct, and calculate orientation state
  1839. if (orientation) {
  1840. // We go from older to younger particles, so we go backwards from the last keyframe until
  1841. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1842. for (; part_age < RotationKeyFrameTimes[rkey]; rkey--);
  1843. float f_delta_t = (float)(part_age - RotationKeyFrameTimes[rkey]);
  1844. float tmp_orient = OrientationKeyFrameValues[rkey] +
  1845. (RotationKeyFrameValues[rkey] + HalfRotationKeyFrameDeltas[rkey] * f_delta_t) * f_delta_t +
  1846. RandomRotationEntries[part & NumRandomRotationEntriesMinus1] * (float)part_age +
  1847. RandomOrientationEntries[part & NumRandomOrientationEntriesMinus1];
  1848. orientation[part] = (uint)(((int)(tmp_orient * 256.0f)) & 0xFF);
  1849. }
  1850. // Ensure the current frame keyframe is correct, and calculate frame state
  1851. if (frame) {
  1852. // We go from older to younger particles, so we go backwards from the last keyframe until
  1853. // age >= keytime. This loop must terminate because the 0th keytime is 0.
  1854. for (; part_age < FrameKeyFrameTimes[fkey]; fkey--);
  1855. float tmp_frame = FrameKeyFrameValues[fkey] +
  1856. FrameKeyFrameDeltas[fkey] * (float)(part_age - FrameKeyFrameTimes[fkey]) +
  1857. RandomFrameEntries[part & NumRandomFrameEntriesMinus1];
  1858. frame[part] = (uint)(((int)(tmp_frame)) & 0xFF);
  1859. }
  1860. }
  1861. }
  1862. void ParticleBufferClass::Update_Bounding_Box(void)
  1863. {
  1864. // Ensure all particle positions are updated. If bounding box still not
  1865. // dirty, return.
  1866. Update_Kinematic_Particle_State();
  1867. if (!BoundingBoxDirty) return;
  1868. // If there are no particles, generate a dummy bounding box:
  1869. if (NonNewNum == 0U) {
  1870. BoundingBox.Init(Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0));
  1871. BoundingBoxDirty = false;
  1872. return;
  1873. }
  1874. // Find min/max coord values for all points:
  1875. int pingpong = 0;
  1876. if (PingPongPosition) {
  1877. pingpong = WW3D::Get_Frame_Count() & 0x1;
  1878. }
  1879. Vector3 *position = Position[pingpong]->Get_Array();
  1880. Vector3 max_coords = position[Start];
  1881. Vector3 min_coords = position[Start];
  1882. // In the general case, a range in a circular buffer can be composed of up
  1883. // to two subranges. Find the Start - End subranges.
  1884. unsigned int sub1_end; // End of subrange 1.
  1885. unsigned int sub2_start; // Start of subrange 2.
  1886. unsigned int i; // Loop index.
  1887. if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
  1888. sub1_end = End;
  1889. sub2_start = End;
  1890. } else {
  1891. sub1_end = MaxNum;
  1892. sub2_start = 0;
  1893. }
  1894. for (i = Start; i < sub1_end; i++) {
  1895. max_coords.X = max_coords.X >= position[i].X ? max_coords.X : position[i].X;
  1896. max_coords.Y = max_coords.Y >= position[i].Y ? max_coords.Y : position[i].Y;
  1897. max_coords.Z = max_coords.Z >= position[i].Z ? max_coords.Z : position[i].Z;
  1898. min_coords.X = min_coords.X <= position[i].X ? min_coords.X : position[i].X;
  1899. min_coords.Y = min_coords.Y <= position[i].Y ? min_coords.Y : position[i].Y;
  1900. min_coords.Z = min_coords.Z <= position[i].Z ? min_coords.Z : position[i].Z;
  1901. }
  1902. for (i = sub2_start; i < End; i++) {
  1903. max_coords.X = max_coords.X >= position[i].X ? max_coords.X : position[i].X;
  1904. max_coords.Y = max_coords.Y >= position[i].Y ? max_coords.Y : position[i].Y;
  1905. max_coords.Z = max_coords.Z >= position[i].Z ? max_coords.Z : position[i].Z;
  1906. min_coords.X = min_coords.X <= position[i].X ? min_coords.X : position[i].X;
  1907. min_coords.Y = min_coords.Y <= position[i].Y ? min_coords.Y : position[i].Y;
  1908. min_coords.Z = min_coords.Z <= position[i].Z ? min_coords.Z : position[i].Z;
  1909. }
  1910. // Extend by maximum possible particle size:
  1911. Vector3 size(MaxSize, MaxSize, MaxSize);
  1912. max_coords += size;
  1913. min_coords -= size;
  1914. // Update bounding box:
  1915. BoundingBox.Init(MinMaxAABoxClass(min_coords,max_coords));
  1916. BoundingBoxDirty = false;
  1917. }
  1918. // NOTE: typically, the number of new particles created in a frame is small
  1919. // relative to the total number of particles, so this is not the most
  1920. // performance-critical particle function. New particles are copied from the
  1921. // new particle vector into the circular buffer, overwriting any older
  1922. // particles (including possibly other new particles) so that the newest
  1923. // particles are preserved. The particles are initialized to their state at
  1924. // the end of the current interval.
  1925. void ParticleBufferClass::Get_New_Particles(void)
  1926. {
  1927. unsigned int current_time = WW3D::Get_Sync_Time();
  1928. // position is the current frame position, prev_pos is the previous frames position (only if
  1929. // we have enabled pingpong position buffers)
  1930. Vector3 *position;
  1931. Vector3 *prev_pos;
  1932. if (PingPongPosition) {
  1933. int pingpong = WW3D::Get_Frame_Count() & 0x1;
  1934. position = Position[pingpong]->Get_Array();
  1935. prev_pos = Position[pingpong ^ 0x1]->Get_Array();
  1936. } else {
  1937. position = Position[0]->Get_Array();
  1938. prev_pos = NULL;
  1939. }
  1940. for (; NewParticleQueueCount;) {
  1941. // Get particle off new particle queue:
  1942. NewParticleStruct &new_particle = NewParticleQueue[NewParticleQueueStart];
  1943. if (++NewParticleQueueStart == MaxNum) NewParticleQueueStart = 0U;
  1944. NewParticleQueueCount--;
  1945. // Get particle birth time stamp, calculate age. If not under maxage
  1946. // skip this particle.
  1947. TimeStamp[NewEnd] = new_particle.TimeStamp;
  1948. unsigned int age = current_time - TimeStamp[NewEnd];
  1949. if (age >= MaxAge) continue;
  1950. float fp_age = (float)age;
  1951. // Apply velocity and acceleration if present. Otherwise, just apply
  1952. // velocity.
  1953. if (HasAccel) {
  1954. position[NewEnd] = new_particle.Position +
  1955. (new_particle.Velocity + 0.5f * Accel * fp_age) * fp_age;
  1956. Velocity[NewEnd] = new_particle.Velocity + (Accel * fp_age);
  1957. } else {
  1958. position[NewEnd] =new_particle.Position +
  1959. (new_particle.Velocity * fp_age);
  1960. Velocity[NewEnd] = new_particle.Velocity;
  1961. }
  1962. // If pingpong enabled, store starting position in prev_pos[].
  1963. if (PingPongPosition) {
  1964. prev_pos[NewEnd] = new_particle.Position;
  1965. }
  1966. // Advance the 'end of new particles' index.
  1967. NewEnd++;
  1968. if (NewEnd == MaxNum) NewEnd = 0;
  1969. // Update the new particles count.
  1970. NewNum++;
  1971. // If we have just overflowed the total buffer, advance Start.
  1972. if ((NewNum + NonNewNum) == (signed)(MaxNum + 1)) {
  1973. Start++;
  1974. if (Start == MaxNum) Start = 0;
  1975. NonNewNum--;
  1976. // If this underflows the 'non-new' buffer, advance End.
  1977. if (NonNewNum == -1) {
  1978. End++;
  1979. if (End == MaxNum) End = 0;
  1980. NonNewNum = 0;
  1981. NewNum--;
  1982. }
  1983. }
  1984. }
  1985. }
  1986. void ParticleBufferClass::Kill_Old_Particles(void)
  1987. {
  1988. // Scan from Start and find the first particle which has an age less than
  1989. // MaxAge - set Start to that position.
  1990. // In the general case, a range in a circular buffer can be composed of up
  1991. // to two subranges. Find the Start - End subranges.
  1992. unsigned int sub1_end; // End of subrange 1.
  1993. unsigned int sub2_start; // Start of subrange 2.
  1994. unsigned int i; // Loop index.
  1995. if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
  1996. sub1_end = End;
  1997. sub2_start = End;
  1998. } else {
  1999. sub1_end = MaxNum;
  2000. sub2_start = 0;
  2001. }
  2002. unsigned int current_time = WW3D::Get_Sync_Time();
  2003. // Stop when the current particle is young enough to be alive.
  2004. bool broke = false;
  2005. for (i = Start; i < sub1_end; i++) {
  2006. if ((current_time - TimeStamp[i]) < MaxAge) {
  2007. broke = true;
  2008. break;
  2009. }
  2010. NonNewNum--;
  2011. }
  2012. if (!broke) {
  2013. for (i = sub2_start; i < End; i++) {
  2014. if ((current_time - TimeStamp[i]) < MaxAge) break;
  2015. NonNewNum--;
  2016. }
  2017. }
  2018. Start = i;
  2019. // NOTE: we do not scan the new particles, because they have been already
  2020. // preculled to be under MaxAge.
  2021. }
  2022. void ParticleBufferClass::Update_Non_New_Particles(unsigned int elapsed)
  2023. {
  2024. // In the general case, a range in a circular buffer can be composed of up
  2025. // to two subranges. Find the Start - End subranges.
  2026. unsigned int sub1_end; // End of subrange 1.
  2027. unsigned int sub2_start; // Start of subrange 2.
  2028. unsigned int i; // Loop index.
  2029. if ((Start < End) || ((Start == End) && NonNewNum ==0)) {
  2030. sub1_end = End;
  2031. sub2_start = End;
  2032. } else {
  2033. sub1_end = MaxNum;
  2034. sub2_start = 0;
  2035. }
  2036. float fp_elapsed_time = (float)elapsed;
  2037. // Update position and velocity for all particles.
  2038. if (PingPongPosition) {
  2039. int pingpong = WW3D::Get_Frame_Count() & 0x1;
  2040. Vector3 *position = Position[pingpong]->Get_Array();
  2041. Vector3 *prev_pos = Position[pingpong ^ 0x1]->Get_Array();
  2042. if (HasAccel) {
  2043. Vector3 delta_v = Accel * fp_elapsed_time;
  2044. Vector3 accel_p = Accel * (0.5f * fp_elapsed_time * fp_elapsed_time);
  2045. for (i = Start; i < sub1_end; i++) {
  2046. position[i] = prev_pos[i] + Velocity[i] * fp_elapsed_time + accel_p;
  2047. Velocity[i] += delta_v;
  2048. }
  2049. for (i = sub2_start; i < End; i++) {
  2050. position[i] = prev_pos[i] + Velocity[i] * fp_elapsed_time + accel_p;
  2051. Velocity[i] += delta_v;
  2052. }
  2053. } else {
  2054. for (i = Start; i < sub1_end; i++) {
  2055. position[i] += Velocity[i] * fp_elapsed_time;
  2056. }
  2057. for (i = sub2_start; i < End; i++) {
  2058. position[i] += Velocity[i] * fp_elapsed_time;
  2059. }
  2060. }
  2061. } else {
  2062. Vector3 *position = Position[0]->Get_Array();
  2063. if (HasAccel) {
  2064. Vector3 delta_v = Accel * fp_elapsed_time;
  2065. Vector3 accel_p = Accel * (0.5f * fp_elapsed_time * fp_elapsed_time);
  2066. for (i = Start; i < sub1_end; i++) {
  2067. position[i] += Velocity[i] * fp_elapsed_time + accel_p;
  2068. Velocity[i] += delta_v;
  2069. }
  2070. for (i = sub2_start; i < End; i++) {
  2071. position[i] += Velocity[i] * fp_elapsed_time + accel_p;
  2072. Velocity[i] += delta_v;
  2073. }
  2074. } else {
  2075. for (i = Start; i < sub1_end; i++) {
  2076. position[i] += Velocity[i] * fp_elapsed_time;
  2077. }
  2078. for (i = sub2_start; i < End; i++) {
  2079. position[i] += Velocity[i] * fp_elapsed_time;
  2080. }
  2081. }
  2082. }
  2083. }
  2084. void ParticleBufferClass::Get_Color_Key_Frames (ParticlePropertyStruct<Vector3> &colors) const
  2085. {
  2086. int real_keyframe_count = (NumColorKeyFrames > 0) ? (NumColorKeyFrames - 1) : 0;
  2087. bool create_last_keyframe = false;
  2088. //
  2089. // Determine if there is a keyframe at the very end of the particle's lifetime
  2090. //
  2091. if ((ColorKeyFrameDeltas != NULL) &&
  2092. ((ColorKeyFrameDeltas[NumColorKeyFrames - 1].X != 0) ||
  2093. (ColorKeyFrameDeltas[NumColorKeyFrames - 1].Y != 0) ||
  2094. (ColorKeyFrameDeltas[NumColorKeyFrames - 1].Z != 0))) {
  2095. real_keyframe_count ++;
  2096. create_last_keyframe = true;
  2097. }
  2098. colors.Start = ColorKeyFrameValues[0];
  2099. colors.Rand = ColorRandom;
  2100. colors.NumKeyFrames = real_keyframe_count;
  2101. colors.KeyTimes = NULL;
  2102. colors.Values = NULL;
  2103. //
  2104. // If we have more than just the start color, build
  2105. // an array of key times and color vatues
  2106. //
  2107. if (real_keyframe_count > 0) {
  2108. colors.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
  2109. colors.Values = W3DNEWARRAY Vector3[real_keyframe_count];
  2110. //
  2111. // Copy the keytimes and color values
  2112. //
  2113. unsigned int index;
  2114. for (index = 1; index < NumColorKeyFrames; index ++) {
  2115. colors.KeyTimes[index - 1] = ((float)ColorKeyFrameTimes[index]) / 1000;
  2116. colors.Values[index - 1] = ColorKeyFrameValues[index];
  2117. }
  2118. //
  2119. // Add a keyframe at the very end of the timeline if necessary
  2120. //
  2121. if (create_last_keyframe) {
  2122. colors.KeyTimes[index - 1] = ((float)MaxAge / 1000);
  2123. //
  2124. // Determine what the value of the last keyframe should be
  2125. //
  2126. Vector3 start_color = ColorKeyFrameValues[index - 1];
  2127. Vector3 &delta = ColorKeyFrameDeltas[NumColorKeyFrames - 1];
  2128. float time_delta = MaxAge - ColorKeyFrameTimes[index - 1];
  2129. colors.Values[index - 1] = start_color + (delta * time_delta);
  2130. }
  2131. }
  2132. return ;
  2133. }
  2134. void ParticleBufferClass::Get_Opacity_Key_Frames (ParticlePropertyStruct<float> &opacities) const
  2135. {
  2136. int real_keyframe_count = (NumAlphaKeyFrames > 0) ? (NumAlphaKeyFrames - 1) : 0;
  2137. bool create_last_keyframe = false;
  2138. //
  2139. // Determine if there is a keyframe at the very end of the particle's lifetime
  2140. //
  2141. if ((AlphaKeyFrameDeltas != NULL) &&
  2142. (AlphaKeyFrameDeltas[NumAlphaKeyFrames - 1] != 0)) {
  2143. real_keyframe_count ++;
  2144. create_last_keyframe = true;
  2145. }
  2146. opacities.Start = AlphaKeyFrameValues[0];
  2147. opacities.Rand = OpacityRandom;
  2148. opacities.NumKeyFrames = real_keyframe_count;
  2149. opacities.KeyTimes = NULL;
  2150. opacities.Values = NULL;
  2151. //
  2152. // If we have more than just the start opacity, build
  2153. // an array of key times and opacity values
  2154. //
  2155. if (real_keyframe_count > 0) {
  2156. opacities.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
  2157. opacities.Values = W3DNEWARRAY float[real_keyframe_count];
  2158. //
  2159. // Copy the keytimes and opacity values
  2160. //
  2161. unsigned int index;
  2162. for (index = 1; index < NumAlphaKeyFrames; index ++) {
  2163. opacities.KeyTimes[index - 1] = ((float)AlphaKeyFrameTimes[index]) / 1000;
  2164. opacities.Values[index - 1] = AlphaKeyFrameValues[index];
  2165. }
  2166. //
  2167. // Add a keyframe at the very end of the timeline if necessary
  2168. //
  2169. if (create_last_keyframe) {
  2170. opacities.KeyTimes[index - 1] = ((float)MaxAge / 1000);
  2171. //
  2172. // Determine what the value of the last keyframe should be
  2173. //
  2174. float start_alpha = AlphaKeyFrameValues[index - 1];
  2175. float &delta = AlphaKeyFrameDeltas[NumAlphaKeyFrames - 1];
  2176. float time_delta = MaxAge - AlphaKeyFrameTimes[index - 1];
  2177. opacities.Values[index - 1] = start_alpha + (delta * time_delta);
  2178. }
  2179. }
  2180. return ;
  2181. }
  2182. void ParticleBufferClass::Get_Size_Key_Frames (ParticlePropertyStruct<float> &sizes) const
  2183. {
  2184. int real_keyframe_count = (NumSizeKeyFrames > 0) ? (NumSizeKeyFrames - 1) : 0;
  2185. bool create_last_keyframe = false;
  2186. //
  2187. // Determine if there is a keyframe at the very end of the particle's lifetime
  2188. //
  2189. if ((SizeKeyFrameDeltas != NULL) &&
  2190. (SizeKeyFrameDeltas[NumSizeKeyFrames - 1] != 0)) {
  2191. real_keyframe_count ++;
  2192. create_last_keyframe = true;
  2193. }
  2194. sizes.Start = SizeKeyFrameValues[0];
  2195. sizes.Rand = SizeRandom;
  2196. sizes.NumKeyFrames = real_keyframe_count;
  2197. sizes.KeyTimes = NULL;
  2198. sizes.Values = NULL;
  2199. //
  2200. // If we have more than just the start opacity, build
  2201. // an array of key times and opacity values
  2202. //
  2203. if (real_keyframe_count > 0) {
  2204. sizes.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
  2205. sizes.Values = W3DNEWARRAY float[real_keyframe_count];
  2206. //
  2207. // Copy the keytimes and size values
  2208. //
  2209. unsigned int index;
  2210. for (index = 1; index < NumSizeKeyFrames; index ++) {
  2211. sizes.KeyTimes[index - 1] = ((float)SizeKeyFrameTimes[index]) / 1000;
  2212. sizes.Values[index - 1] = SizeKeyFrameValues[index];
  2213. }
  2214. //
  2215. // Add a keyframe at the very end of the timeline if necessary
  2216. //
  2217. if (create_last_keyframe) {
  2218. sizes.KeyTimes[index - 1] = ((float)MaxAge / 1000);
  2219. //
  2220. // Determine what the value of the last keyframe should be
  2221. //
  2222. float start_size = SizeKeyFrameValues[index - 1];
  2223. float &delta = SizeKeyFrameDeltas[NumSizeKeyFrames - 1];
  2224. float time_delta = MaxAge - SizeKeyFrameTimes[index - 1];
  2225. sizes.Values[index - 1] = start_size + (delta * time_delta);
  2226. }
  2227. }
  2228. return ;
  2229. }
  2230. void ParticleBufferClass::Get_Rotation_Key_Frames (ParticlePropertyStruct<float> &rotations) const
  2231. {
  2232. int real_keyframe_count = (NumRotationKeyFrames > 0) ? (NumRotationKeyFrames - 1) : 0;
  2233. bool create_last_keyframe = false;
  2234. /*
  2235. ** NOTE: Rotations are stored internally in rotations per millisecond. These will be converted to rotations per second.
  2236. */
  2237. //
  2238. // Determine if there is a keyframe at the very end of the particle's lifetime
  2239. //
  2240. if ((HalfRotationKeyFrameDeltas != NULL) &&
  2241. (HalfRotationKeyFrameDeltas[NumRotationKeyFrames - 1] != 0)) {
  2242. real_keyframe_count ++;
  2243. create_last_keyframe = true;
  2244. }
  2245. // Convert the rotation values from rotations per millisecond to rotations per second.
  2246. rotations.Start = RotationKeyFrameValues ? RotationKeyFrameValues[0] * 1000.0f : 0;
  2247. rotations.Rand = RotationRandom * 1000.0f;
  2248. rotations.NumKeyFrames = real_keyframe_count;
  2249. rotations.KeyTimes = NULL;
  2250. rotations.Values = NULL;
  2251. //
  2252. // If we have more than just the start rotation, build
  2253. // an array of key times and rotation values
  2254. //
  2255. if (real_keyframe_count > 0) {
  2256. rotations.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
  2257. rotations.Values = W3DNEWARRAY float[real_keyframe_count];
  2258. //
  2259. // Copy the keytimes and rotation values
  2260. //
  2261. unsigned int index;
  2262. for (index = 1; index < NumRotationKeyFrames; index ++) {
  2263. rotations.KeyTimes[index - 1] = ((float)RotationKeyFrameTimes[index]) / 1000;
  2264. rotations.Values[index - 1] = RotationKeyFrameValues[index] * 1000.0f;
  2265. }
  2266. //
  2267. // Add a keyframe at the very end of the timeline if necessary
  2268. //
  2269. if (create_last_keyframe) {
  2270. rotations.KeyTimes[index - 1] = ((float)MaxAge / 1000);
  2271. //
  2272. // Determine what the value of the last keyframe should be
  2273. //
  2274. float start_rotation = RotationKeyFrameValues[index - 1];
  2275. float delta = 2.0f * HalfRotationKeyFrameDeltas[NumRotationKeyFrames - 1];
  2276. float time_delta = MaxAge - RotationKeyFrameTimes[index - 1];
  2277. rotations.Values[index - 1] = (start_rotation + (delta * time_delta)) * 1000.0f;
  2278. }
  2279. }
  2280. return ;
  2281. }
  2282. void ParticleBufferClass::Get_Frame_Key_Frames (ParticlePropertyStruct<float> &frames) const
  2283. {
  2284. int real_keyframe_count = (NumFrameKeyFrames > 0) ? (NumFrameKeyFrames - 1) : 0;
  2285. bool create_last_keyframe = false;
  2286. //
  2287. // Determine if there is a keyframe at the very end of the particle's lifetime
  2288. //
  2289. if ((FrameKeyFrameDeltas != NULL) &&
  2290. (FrameKeyFrameDeltas[NumFrameKeyFrames - 1] != 0)) {
  2291. real_keyframe_count ++;
  2292. create_last_keyframe = true;
  2293. }
  2294. frames.Start = FrameKeyFrameValues[0];
  2295. frames.Rand = FrameRandom;
  2296. frames.NumKeyFrames = real_keyframe_count;
  2297. frames.KeyTimes = NULL;
  2298. frames.Values = NULL;
  2299. //
  2300. // If we have more than just the start rotation, build
  2301. // an array of key times and rotation values
  2302. //
  2303. if (real_keyframe_count > 0) {
  2304. frames.KeyTimes = W3DNEWARRAY float[real_keyframe_count];
  2305. frames.Values = W3DNEWARRAY float[real_keyframe_count];
  2306. //
  2307. // Copy the keytimes and frame values
  2308. //
  2309. unsigned int index;
  2310. for (index = 1; index < NumFrameKeyFrames; index ++) {
  2311. frames.KeyTimes[index - 1] = ((float)FrameKeyFrameTimes[index]) / 1000;
  2312. frames.Values[index - 1] = FrameKeyFrameValues[index];
  2313. }
  2314. //
  2315. // Add a keyframe at the very end of the timeline if necessary
  2316. //
  2317. if (create_last_keyframe) {
  2318. frames.KeyTimes[index - 1] = ((float)MaxAge / 1000);
  2319. //
  2320. // Determine what the value of the last keyframe should be
  2321. //
  2322. float start_frame = FrameKeyFrameValues[index - 1];
  2323. float &delta = FrameKeyFrameDeltas[NumFrameKeyFrames - 1];
  2324. float time_delta = MaxAge - FrameKeyFrameTimes[index - 1];
  2325. frames.Values[index - 1] = start_frame + (delta * time_delta);
  2326. }
  2327. }
  2328. return ;
  2329. }
  2330. void ParticleBufferClass::Set_LOD_Max_Screen_Size(int lod_level,float max_screen_size)
  2331. {
  2332. if ((lod_level <0) || (lod_level > 17)) {
  2333. return;
  2334. }
  2335. LODMaxScreenSizes[lod_level] = max_screen_size;
  2336. }
  2337. float ParticleBufferClass::Get_LOD_Max_Screen_Size(int lod_level)
  2338. {
  2339. if ((lod_level <0) || (lod_level > 17)) {
  2340. return NO_MAX_SCREEN_SIZE;
  2341. }
  2342. return LODMaxScreenSizes[lod_level];
  2343. }
  2344. int ParticleBufferClass::Get_Line_Texture_Mapping_Mode(void) const
  2345. {
  2346. if (LineRenderer != NULL) {
  2347. return LineRenderer->Get_Texture_Mapping_Mode();
  2348. }
  2349. return SegLineRendererClass::UNIFORM_WIDTH_TEXTURE_MAP;
  2350. }
  2351. int ParticleBufferClass::Is_Merge_Intersections(void) const
  2352. {
  2353. if (LineRenderer != NULL) {
  2354. return LineRenderer->Is_Merge_Intersections();
  2355. }
  2356. return false;
  2357. }
  2358. int ParticleBufferClass::Is_Freeze_Random(void) const
  2359. {
  2360. if (LineRenderer != NULL) {
  2361. return LineRenderer->Is_Freeze_Random();
  2362. }
  2363. return false;
  2364. }
  2365. int ParticleBufferClass::Is_Sorting_Disabled(void) const
  2366. {
  2367. if (LineRenderer != NULL) {
  2368. return LineRenderer->Is_Sorting_Disabled();
  2369. }
  2370. return false;
  2371. }
  2372. int ParticleBufferClass::Are_End_Caps_Enabled(void) const
  2373. {
  2374. if (LineRenderer != NULL) {
  2375. return LineRenderer->Are_End_Caps_Enabled();
  2376. }
  2377. return false;
  2378. }
  2379. int ParticleBufferClass::Get_Subdivision_Level(void) const
  2380. {
  2381. if (LineRenderer != NULL) {
  2382. return LineRenderer->Get_Current_Subdivision_Level();
  2383. }
  2384. return 0;
  2385. }
  2386. float ParticleBufferClass::Get_Noise_Amplitude(void) const
  2387. {
  2388. if (LineRenderer != NULL) {
  2389. return LineRenderer->Get_Noise_Amplitude();
  2390. }
  2391. return 0.0f;
  2392. }
  2393. float ParticleBufferClass::Get_Merge_Abort_Factor(void) const
  2394. {
  2395. if (LineRenderer != NULL) {
  2396. return LineRenderer->Get_Merge_Abort_Factor();
  2397. }
  2398. return 0.0f;
  2399. }
  2400. float ParticleBufferClass::Get_Texture_Tile_Factor(void) const
  2401. {
  2402. if (LineRenderer != NULL) {
  2403. return LineRenderer->Get_Texture_Tile_Factor();
  2404. }
  2405. return 1.0f;
  2406. }
  2407. Vector2 ParticleBufferClass::Get_UV_Offset_Rate(void) const
  2408. {
  2409. if (LineRenderer != NULL) {
  2410. return LineRenderer->Get_UV_Offset_Rate();
  2411. }
  2412. return Vector2(0.0f,0.0f);
  2413. }