aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llvopart.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llvopart.cpp1358
1 files changed, 0 insertions, 1358 deletions
diff --git a/linden/indra/newview/llvopart.cpp b/linden/indra/newview/llvopart.cpp
deleted file mode 100644
index 6b38cf9..0000000
--- a/linden/indra/newview/llvopart.cpp
+++ /dev/null
@@ -1,1358 +0,0 @@
1/**
2 * @file llvopart.cpp
3 * @brief Viewer-object derived particle system.
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llvopart.h"
31
32#include "llfasttimer.h"
33#include "message.h"
34
35#include "llagent.h"
36#include "lldrawable.h"
37#include "llface.h"
38#include "llsky.h"
39#include "llviewercamera.h"
40#include "llviewerimagelist.h"
41#include "llviewerregion.h"
42#include "pipeline.h"
43
44const F32 MAX_PART_LIFETIME = 120.f;
45
46extern U64 gFrameTime;
47
48LLVOPart::LLVOPart(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
49: LLViewerObject(id, pcode, regionp)
50{
51 mParticlesDead = FALSE;
52 setNumTEs(1);
53 setDefaultValues();
54
55 mbCanSelect = FALSE; // users can't select particle systems
56 mNumLiveParticles = 0;
57}
58
59
60LLVOPart::~LLVOPart()
61{
62 delete [] mParticleState;
63 mParticleState = NULL;
64
65 delete [] mDeadArr;
66 mDeadArr = NULL;
67}
68
69void LLVOPart::initClass()
70{
71}
72
73U32 LLVOPart::processUpdateMessage(LLMessageSystem *mesgsys,
74 void **user_data,
75 U32 block_num,
76 const EObjectUpdateType update_type,
77 LLDataPacker *dp)
78{
79 S32 dataSize;
80 U8 packed_psys_data[180];
81 // Do base class updates...
82 mTimeLastFrame = gFrameTime;
83 U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
84
85 if (update_type == OUT_TERSE_IMPROVED)
86 {
87 // Nothing else needs to be done for the terse message.
88 return retval;
89 }
90
91 dataSize = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data);
92
93 if(dataSize == sizeof(LLPartInitData))
94 {
95 // Uncompressed particle. Is this used? JC
96 mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, &mInitSysData, dataSize, block_num);
97
98 if(mInitSysData.createMe)
99 {
100 if (mInitSysData.initialParticles >= mInitSysData.maxParticles)
101 {
102 mInitSysData.initialParticles = mInitSysData.maxParticles - 1;
103 }
104 setParticleParams(mInitSysData.bounce_b,
105 getPositionRegion().mV,
106 getRotation().mQ,
107 mInitSysData.maxParticles,
108 mInitSysData.mImageUuid,
109 mInitSysData.mFlags);
110 initializeParticlesAndConstraints(mInitSysData.initialParticles,
111 mInitSysData.diffEqAlpha,
112 mInitSysData.diffEqScale,
113 mInitSysData.scale_range,
114 mInitSysData.alpha_range,
115 mInitSysData.vel_offset,
116 mInitSysData.killPlaneZ,
117 mInitSysData.killPlaneNormal,
118 mInitSysData.bouncePlaneZ,
119 mInitSysData.bouncePlaneNormal,
120 mInitSysData.spawnRange,
121 mInitSysData.spawnFrequency,
122 mInitSysData.spawnFreqencyRange,
123 mInitSysData.spawnDirection,
124 mInitSysData.spawnDirectionRange,
125 mInitSysData.spawnVelocity,
126 mInitSysData.spawnVelocityRange,
127 mInitSysData.speedLimit,
128 mInitSysData.windWeight,
129 mInitSysData.currentGravity,
130 mInitSysData.gravityWeight,
131 mInitSysData.globalLifetime,
132 mInitSysData.individualLifetime,
133 mInitSysData.individualLifetimeRange,
134 mInitSysData.alphaDecay,
135 mInitSysData.scaleDecay,
136 mInitSysData.distanceDeath,
137 mInitSysData.dampMotionFactor,
138 mInitSysData.windDiffusionFactor);
139 setParticlesDistFadeout(mInitSysData.mDistBeginFadeout,
140 mInitSysData.mDistEndFadeout);
141 }
142 }
143 else if(dataSize > 4)
144 {
145 mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, packed_psys_data, dataSize, block_num);
146
147 LLPartSysCompressedPacket CompObjectData;
148 U32 sizeUsed;
149
150 CompObjectData.fromUnsignedBytes(packed_psys_data, dataSize);
151 CompObjectData.toLLPartInitData(&mInitSysData, &sizeUsed);
152
153 if(mInitSysData.createMe)
154 {
155 if (mInitSysData.initialParticles >= mInitSysData.maxParticles)
156 {
157 mInitSysData.initialParticles = mInitSysData.maxParticles - 1;
158 }
159 setParticleParams(mInitSysData.bounce_b,
160 getPositionRegion().mV,
161 getRotation().mQ,
162 mInitSysData.maxParticles,
163 mInitSysData.mImageUuid,
164 mInitSysData.mFlags);
165 initializeParticlesAndConstraints(mInitSysData.initialParticles,
166 mInitSysData.diffEqAlpha,
167 mInitSysData.diffEqScale,
168 mInitSysData.scale_range,
169 mInitSysData.alpha_range,
170 mInitSysData.vel_offset,
171 mInitSysData.killPlaneZ,
172 mInitSysData.killPlaneNormal,
173 mInitSysData.bouncePlaneZ,
174 mInitSysData.bouncePlaneNormal,
175 mInitSysData.spawnRange,
176 mInitSysData.spawnFrequency,
177 mInitSysData.spawnFreqencyRange,
178 mInitSysData.spawnDirection,
179 mInitSysData.spawnDirectionRange,
180 mInitSysData.spawnVelocity,
181 mInitSysData.spawnVelocityRange,
182 mInitSysData.speedLimit,
183 mInitSysData.windWeight,
184 mInitSysData.currentGravity,
185 mInitSysData.gravityWeight,
186 mInitSysData.globalLifetime,
187 mInitSysData.individualLifetime,
188 mInitSysData.individualLifetimeRange,
189 mInitSysData.alphaDecay,
190 mInitSysData.scaleDecay,
191 mInitSysData.distanceDeath,
192 mInitSysData.dampMotionFactor,
193 mInitSysData.windDiffusionFactor);
194 setParticlesDistFadeout(mInitSysData.mDistBeginFadeout,
195 mInitSysData.mDistEndFadeout);
196 }
197 }
198
199 translateParticlesTo(getPositionRegion());
200 rotateParticlesTo(getRotation());
201 return retval;
202}
203
204
205BOOL LLVOPart::isActive() const
206{
207 return TRUE;
208}
209
210BOOL LLVOPart::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
211{
212 if (mDeathTimer.getElapsedTimeF32() > MAX_PART_LIFETIME)
213 {
214 //llinfos << "LLVOPart dead due to extended lifetime" << llendl;
215 return FALSE;
216 }
217
218 if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES))
219 {
220 if (!mDrawable)
221 {
222 llwarns << "LLVOPart idle with no drawable!" << llendl;
223 return FALSE;
224 }
225 // I don't know why you'd want to do ANYTHING with invisible particles. ??? - Doug
226 if(mFlags[PART_SYS_INVISIBLE_BYTE] & PART_SYS_INVISIBLE_BIT)
227 {
228 llwarns << "Invisible particle, killing" << llendl;
229 return FALSE;
230 }
231
232 F64 delta_time = ((S64)(gFrameTime - mTimeLastFrame))*(1.0/((F64)USEC_PER_SEC));
233 mParticlesDead = !iterateParticles((F32)delta_time);
234
235 if(mParticlesDead)
236 {
237 return FALSE;
238 }
239
240 translateParticlesTo(getPositionRegion());
241
242 mTimeLastFrame = gFrameTime;
243 setChanged(GEOMETRY);
244 gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
245
246 }
247 LLViewerObject::idleUpdate(agent, world, time);
248 return TRUE;
249}
250
251
252void LLVOPart::updateTextures(LLAgent &agent)
253{
254 if (getTEImage(0))
255 {
256 LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
257 F32 dot_product = relative_position * agent.getFrameAgent().getAtAxis();
258 F32 cos_angle = dot_product / relative_position.magVec();
259
260 if (cos_angle > 1.f)
261 {
262 cos_angle = 1.f;
263 }
264
265 getTEImage(0)->addTextureStats(mPixelArea, 1.f, cos_angle);
266 }
267}
268
269
270LLDrawable* LLVOPart::createDrawable(LLPipeline *pipeline)
271{
272 pipeline->allocDrawable(this);
273 mDrawable->setLit(FALSE);
274 mDrawable->setRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
275
276 LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
277 mDrawable->setNumFaces(mNumPart, pool, getTEImage(0));
278 return mDrawable;
279}
280
281BOOL LLVOPart::updateGeometry(LLDrawable *drawable)
282{
283 if (isChanged(LLPrimitive::GEOMETRY))
284 {
285 LLFace *face;
286
287 ///////////////////////
288 //
289 // Allocate/deallocate faces based on number of particles we need to render
290 //
291 //
292 if (drawable->getNumFaces())
293 {
294 face = drawable->getFace(0);
295 drawable->setNumFaces(mNumPart, face->getPool(), getTEImage(0));
296 }
297 else
298 {
299 LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
300 drawable->setNumFaces(mNumPart, pool, getTEImage(0));
301 }
302
303 LLVector3 light_norm;
304
305 if (gSky.sunUp())
306 {
307 light_norm = -gSky.getSunDirection();
308 }
309 else
310 {
311 light_norm = -gSky.getMoonDirection();
312 }
313 light_norm.normVec();
314
315 // Figure out the lighting for the particle system.
316 LLColor4 color(1.f,1.f,1.f,1.f);
317 LLVector3 at, left, up;
318
319 at = gCamera->getAtAxis();
320 left = gCamera->getLeftAxis();
321 up = gCamera->getUpAxis();
322
323 LLVector3 v_agent[4];
324
325 LLMatrix3 cached_oo;
326 cached_oo.setRot(mOriginOrientation);
327 U32 i;
328 U32 cur_face = 0;
329 for (i = 0; i < mNumPart; i++)
330 {
331 face = drawable->getFace(cur_face++);
332
333 if (0 != mDeadArr[i])
334 {
335 face->setSize(0);
336 continue; // if this particle is dead, don't render it
337 }
338
339
340 LLStrider<LLVector3> verticesp;
341 LLStrider<LLVector3> normalsp;
342 LLStrider<LLVector2> texCoordsp;
343 U32 *indicesp;
344 S32 index_offset;
345
346 face->setPrimType(LLTriangles);
347 face->setSize(4, 6);
348 index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
349 if (-1 == index_offset)
350 {
351 llerrs << "Error allocating geometry!" << llendl;
352 }
353
354 LLVector3 position_agent;
355 LLVector3 part_pos_local;
356 F32 alpha = 1.0f;
357 F32 scale = 1.0f; // elements of the particle system have random scales too! -- MDS
358
359 position_agent = mSpawnPoint + getRegion()->getOriginAgent();
360
361 if(mFlags[PART_SYS_FOLLOW_VEL_BYTE] & PART_SYS_FOLLOW_VEL_BIT)
362 {
363 part_pos_local.mV[0] = mParticleState[i].position[0]*cached_oo.mMatrix[0][0]+
364 mParticleState[i].position[1]*cached_oo.mMatrix[0][1]+
365 mParticleState[i].position[2]*cached_oo.mMatrix[0][2];
366
367 part_pos_local.mV[1] = mParticleState[i].position[0]*cached_oo.mMatrix[1][0]+
368 mParticleState[i].position[1]*cached_oo.mMatrix[1][1]+
369 mParticleState[i].position[2]*cached_oo.mMatrix[1][2];
370
371 part_pos_local.mV[2] = mParticleState[i].position[0]*cached_oo.mMatrix[2][0]+
372 mParticleState[i].position[1]*cached_oo.mMatrix[2][1]+
373 mParticleState[i].position[2]*cached_oo.mMatrix[2][2];
374
375 scale = mParticleState[i].scale[0];
376 alpha = mParticleState[i].alpha[0];
377
378 //26 September 2001 - alter alpha and scale as approach death
379 //j = death_offset_i(i);
380 scale *= ((1.f - mScaleDecay) + (mScaleDecay * mParticleState[i].deathOffset));
381 alpha *= ((1.f - mAlphaDecay) + (mAlphaDecay * mParticleState[i].deathOffset));
382
383 up.mV[0] = -mParticleState[i].position[0];
384 up.mV[1] = -mParticleState[i].position[1];
385 up.mV[2] = -mParticleState[i].position[2]; // set "up" to trail velocity
386
387 if(up.magVec() == 0.0f) // alleviate potential divide by zero bug
388 {
389 up.mV[2] += 1.0f;
390 }
391
392 up.normVec();
393 up = up - (up*at) * at;
394
395 left = up % at;
396
397 up *= scale * 0.5f;
398 left *= scale * 0.5f;
399 position_agent += part_pos_local;
400 face->mCenterAgent = position_agent;
401 v_agent[0] = position_agent + left + up;
402 v_agent[1] = position_agent - left + up;
403 v_agent[2] = position_agent - left - up;
404 v_agent[3] = position_agent + left - up;
405 *(texCoordsp) = LLVector2(0.f, 1.f);
406 texCoordsp++;
407 *(texCoordsp) = LLVector2(0.f, 0.f);
408 texCoordsp++;
409 *(texCoordsp) = LLVector2(1.f, 1.f);
410 texCoordsp++;
411 *(texCoordsp) = LLVector2(1.f, 0.f);
412 texCoordsp++;
413 }
414 else
415 {
416 part_pos_local.mV[0] = mParticleState[i].position[0]*cached_oo.mMatrix[0][0]+
417 mParticleState[i].position[1]*cached_oo.mMatrix[0][1]+
418 mParticleState[i].position[2]*cached_oo.mMatrix[0][2];
419
420 part_pos_local.mV[1] = mParticleState[i].position[0]*cached_oo.mMatrix[1][0]+
421 mParticleState[i].position[1]*cached_oo.mMatrix[1][1]+
422 mParticleState[i].position[2]*cached_oo.mMatrix[1][2];
423
424 part_pos_local.mV[2] = mParticleState[i].position[0]*cached_oo.mMatrix[2][0]+
425 mParticleState[i].position[1]*cached_oo.mMatrix[2][1]+
426 mParticleState[i].position[2]*cached_oo.mMatrix[2][2];
427
428
429 scale = mParticleState[i].scale[0];
430 alpha = mParticleState[i].alpha[0];
431
432 //26 September 2001 - alter alpha and scale as approach death
433 scale *= ((1.f - mScaleDecay) + (mScaleDecay * mParticleState[i].deathOffset));
434 alpha *= ((1.f - mAlphaDecay) + (mAlphaDecay * mParticleState[i].deathOffset));
435
436 LLVector3 part_up = scale * 0.5f * up;
437 LLVector3 part_left = scale * 0.5f * left;
438
439 position_agent += part_pos_local;
440 face->mCenterAgent = position_agent;
441 v_agent[0] = position_agent + part_left + part_up;
442 v_agent[1] = position_agent - part_left + part_up;
443 v_agent[2] = position_agent - part_left - part_up;
444 v_agent[3] = position_agent + part_left - part_up;
445 *(texCoordsp) = LLVector2(0.f, 1.f);
446 texCoordsp++;
447 *(texCoordsp) = LLVector2(0.f, 0.f);
448 texCoordsp++;
449 *(texCoordsp) = LLVector2(1.f, 1.f);
450 texCoordsp++;
451 *(texCoordsp) = LLVector2(1.f, 0.f);
452 texCoordsp++;
453 }
454
455 color.mV[3] = alpha;
456 face->setFaceColor(color);
457
458 *(verticesp++) = v_agent[1];
459 *(verticesp++) = v_agent[2];
460 *(verticesp++) = v_agent[0];
461 *(verticesp++) = v_agent[3];
462
463 *(indicesp++) = index_offset + 0;
464 *(indicesp++) = index_offset + 2;
465 *(indicesp++) = index_offset + 1;
466
467 *(indicesp++) = index_offset + 1;
468 *(indicesp++) = index_offset + 2;
469 *(indicesp++) = index_offset + 3;
470 }
471 LLPipeline::sCompiles++;
472 }
473
474 return TRUE;
475}
476
477
478void LLVOPart::setDefaultValues()
479{
480 U32 i;
481
482 // initialize to safe but meaningless values : no other constructors
483 mParticleState = NULL;
484 mNumPart = 0;
485 mAlpha = 1.0f;
486 mLastTime = mCurrTime = 0.0f;
487 //mOriginPosition[0] = mOriginPosition[1] = mOriginPosition[2] = 0.0f;
488 mOriginOrientation.setQuatInit(0.0f, 0.0f, 0.0f, 1.0f);
489 mDeadArr = NULL;
490
491 mKillPlaneNormal.mV[VX] = 0.0f;//Straight up - needs to be unit
492 mKillPlaneNormal.mV[VY] = 0.0f;
493 mKillPlaneNormal.mV[VZ] = 1.0f;
494
495 mBouncePlaneNormal.mV[VX] = 0.0f;//Straight up - needs to be unit
496 mBouncePlaneNormal.mV[VY] = 0.0f;
497 mBouncePlaneNormal.mV[VZ] = 1.0f;
498
499 mSpawnPoint.mV[VX] = 0.0f;
500 mSpawnPoint.mV[VY] = 0.0f;
501 mSpawnPoint.mV[VZ] = 0.0f;
502
503 mSpawnDirection.mV[VX] = 0.0f;//Straight up - needs to be unit
504 mSpawnDirection.mV[VY] = 0.0f;
505 mSpawnDirection.mV[VZ] = 1.0f;
506
507 mCurrentWind.mV[VX] = 0.0f;
508 mCurrentWind.mV[VY] = 0.0f;
509 mCurrentWind.mV[VZ] = 0.0f;
510
511 mCurrentWindMagnitude = 0.0f;
512 mCurrentWindMagnitudeSquareRoot = 0.0f;
513
514 mCurrentGravity.mV[VX] = 0.0f;//Straight down
515 mCurrentGravity.mV[VY] = 0.0f;
516 mCurrentGravity.mV[VZ] = -9.81f;
517
518 mVelocityOffset.mV[VX] = 0.0f;
519 mVelocityOffset.mV[VY] = 0.0f;
520 mVelocityOffset.mV[VZ] = 0.0f;
521
522 for(i = 0; i < PART_SYS_BYTES_OF_FLAGS; i++)
523 {
524 mFlags[i] = 0x00;
525 }
526
527 //set default action and kill flags
528 //These defaults are for an explosion - a short lived set of debris affected by gravity.
529 //Action flags default to PART_SYS_AFFECTED_BY_WIND + PART_SYS_AFFECTED_BY_GRAVITY + PART_SYS_DISTANCE_DEATH
530 mFlags[PART_SYS_ACTION_BYTE] = PART_SYS_AFFECTED_BY_WIND | PART_SYS_AFFECTED_BY_GRAVITY | PART_SYS_DISTANCE_DEATH;
531 mFlags[PART_SYS_KILL_BYTE] = PART_SYS_DISTANCE_DEATH + PART_SYS_TIME_DEATH;
532
533 for (i = 0; i < 3; i++)
534 {
535 mDiffEqAlpha[i] = 0.0f;
536 mDiffEqScale[i] = 0.0f;
537 }
538
539 mScale_range[0] = 1.00f;
540 mScale_range[1] = 5.00f;
541 mScale_range[2] = mScale_range[3] = 0.0f;
542
543 mAlpha_range[0] = mAlpha_range[1] = 1.0f;
544 mAlpha_range[2] = mAlpha_range[3] = 0.0f;
545
546
547 mKillPlaneZ = 0.0f;
548 mBouncePlaneZ = 0.0f;
549
550 mSpawnRange = 1.0f;
551 mSpawnFrequency = 0.0f;
552 mSpawnFrequencyRange = 0.0f;
553 mSpawnDirectionRange = 1.0f; //everywhere
554 mSpawnVelocity = 0.75f;
555 mSpawnVelocityRange = 0.25f; //velocity +/- 0.25
556 mSpeedLimitSquared = 1.0f;
557 mWindWeight = 0.5f; //0.0f means looks like a heavy object (if gravity is on), 1.0f means light and fluffy
558 mGravityWeight = 0.5f; //0.0f means boyed by air, 1.0f means it's a lead weight
559 mGlobalLifetime = 0.0f; //Arbitrary, but default is no global die, so doesn't matter
560 mOriginalGlobalLifetime = 0.0f;
561 mIndividualLifetime = 5.0f;
562 if (mIndividualLifetime > 0.0f)
563 {
564 mOneOverIndividualLifetime = 1.0f / mIndividualLifetime;
565 }
566 else
567 {
568 mOneOverIndividualLifetime = 0.0f;
569 }
570 mIndividualLifetimeRange = 1.0f; //Particles last 5 secs +/- 1
571 mAlphaDecay = 1.0f; //normal alpha fadeout
572 mScaleDecay = 0.0f; //no scale decay
573 mDistanceDeathSquared = 10.0f; //die if hit unit radius
574 if (mDistanceDeathSquared > 0.0f)
575 {
576 mOneOverDistanceDeathSquared = 1.0f / mDistanceDeathSquared;
577 }
578 else
579 {
580 mOneOverDistanceDeathSquared = 0.0f;
581 }
582 mDampMotionFactor = 0.0f;
583
584 mWindDiffusionFactor.mV[VX] = 0.0f;
585 mWindDiffusionFactor.mV[VY] = 0.0f;
586 mWindDiffusionFactor.mV[VZ] = 0.0f;
587
588 mUpdatePhysicsInputsTime = mCurrTime;
589}
590
591
592U8 LLVOPart::setParticlesDistFadeout(F32 beginFadeout, F32 endFadeout)
593{
594 // This doesn't do anything...
595 return 1;
596}
597
598unsigned char LLVOPart::setParticleParams(F32 bounce_b,
599 const F32 o_pos[3],
600 const F32 o_or[3],
601 U32 n,
602 LLUUID image_uuid,
603 U8 flags[PART_SYS_BYTES_OF_FLAGS])
604{
605 mBounceBehavior = bounce_b;
606 mSpawnPoint.setVec(o_pos[0], o_pos[1], o_pos[2]);
607
608 mOriginOrientation.setQuatInit(o_or[0], o_or[1], o_or[2],
609 (F32)sqrt(1.0f - o_or[0]*o_or[0] - o_or[1]*o_or[1] - o_or[2]*o_or[2]));
610
611 if(mNumPart < n )
612 {
613 // this crazy logic is just in case "setParams" gets called multiple times
614 if(mNumPart != 0)
615 {
616 if(mParticleState != NULL)
617 {
618 delete [] mParticleState;
619 }
620 if(mDeadArr != NULL)
621 {
622 delete [] mDeadArr;
623 }
624
625 mParticleState = NULL;
626 mDeadArr = NULL;
627 mNumPart = 0;
628 }
629 mNumPart = n;
630 //state_arr = new F32[n*FLOATS_PER_PARTICLE];
631 mParticleState = new OneParticleData[n];
632 mDeadArr = new U8[n];
633 memset(mDeadArr,1,n); // initialize these to 1;
634 }
635 else
636 {
637 mNumPart = n;
638 }
639
640 setTETexture(0, image_uuid);
641
642 for(U32 i = 0; i< PART_SYS_BYTES_OF_FLAGS; i++)
643 {
644 mFlags[i] = flags[i];
645 }
646
647 return '\0'; // success
648}
649
650
651void LLVOPart::setParticleCountdownStateWaitingDead(const U32 particleNumber)
652{
653 F32 frequency;
654
655 frequency = mSpawnFrequency;
656 // **** Hack! remainingLifetime counts up from negative, to avoid subtracts! - djs
657 mParticleState[particleNumber].remainingLifetime = -(((mSpawnFrequencyRange * 2.0f)*ll_frand()) + frequency - mSpawnFrequencyRange);
658}
659
660//Can override later
661void LLVOPart::spawnParticle(const U32 particleNumber)
662{
663 F32 randomUnitValue;
664 LLVector3 direction;
665
666 if (particleNumber >= mNumPart)
667 {
668 llinfos << "Trying to spawn particle beyond initialized particles! " << particleNumber << " : " << mNumPart << llendl;
669 return;
670 }
671
672 mDeadArr[particleNumber] = 0; // not dead yet!
673
674 //j = pos_offset_i(particleNumber);
675 mParticleState[particleNumber].position[0] = 2.f*mSpawnRange*(ll_frand()) - mSpawnRange;
676 mParticleState[particleNumber].position[1] = 2.f*mSpawnRange*(ll_frand()) - mSpawnRange;
677 mParticleState[particleNumber].position[2] = 2.f*mSpawnRange*(ll_frand()) - mSpawnRange;
678
679 //mParticleStateArray[j] = (pos_ranges[1] - pos_ranges[0])*(F32)rand()/((F32)RAND_MAX) + pos_ranges[0];
680 //j++;
681
682 //mParticleStateArray[j] = (pos_ranges[3] - pos_ranges[2])*(F32)rand()/((F32)RAND_MAX) + pos_ranges[2];
683 //j++;
684
685 //mParticleStateArray[j] = (pos_ranges[5] - pos_ranges[4])*(F32)rand()/((F32)RAND_MAX) + pos_ranges[4];
686
687 //Create the ranged direction vector first - then rotate by the actual direction and then scale
688 //Creating a random value about 1,0,0
689 //1. pick a random angle YZ orientation through full circle.
690 randomUnitValue = (ll_frand());
691 direction.mV[VY] = sinf(randomUnitValue * 2.0 * F_PI);
692 direction.mV[VZ] = cosf(randomUnitValue * 2.0 * F_PI);
693
694 //2. pick a rotation to this angle to project into z which is scaled by mSpawnDirectionRange
695 randomUnitValue = (ll_frand());
696 randomUnitValue *= mSpawnDirectionRange;
697 direction.mV[VY] = direction.mV[VY] * sinf(randomUnitValue * F_PI);
698 direction.mV[VZ] = direction.mV[VZ] * sinf(randomUnitValue * F_PI);
699 direction.mV[VX] = cosf(randomUnitValue * F_PI); //works as still dealing with a unit vector
700
701 //3.To rotate into the spawn direction coord system, derive a yaw and pitch (roll doesnt matter)
702 //from the offset between the unit vector 1,0,0 and random direction.
703 {F32 length;
704
705 //TODO - math behind this may be incorrect
706 //Assume the initial axis is +ve x
707 //derive pitch using the XZ or XY components.
708 //derive yaw using YZ components.
709 length = (mSpawnDirection.mV[VY]*mSpawnDirection.mV[VY]) +
710 (mSpawnDirection.mV[VZ]*mSpawnDirection.mV[VZ]);
711 if (length > 0.0f)
712 {//Only happens when spawn a particle, so can afford some heavy math.
713 F32 xYaw, yYaw, xPitch, yPitch;
714 LLVector3 tempResult, tempResult2;
715
716 //Pitch is the XZ component (but if Z is 0 and Y is not, switch)
717 tempResult.setVec(mSpawnDirection.mV[VX], mSpawnDirection.mV[VY], mSpawnDirection.mV[VZ]);
718 if (0.0f != mSpawnDirection.mV[VZ])
719 {
720 length = sqrtf((mSpawnDirection.mV[VX]*mSpawnDirection.mV[VX]) +
721 (mSpawnDirection.mV[VZ]*mSpawnDirection.mV[VZ]));
722 if (length > 0.0f)
723 {
724 xPitch = tempResult.mV[VX] / length;
725 yPitch = -tempResult.mV[VZ] / length;
726 }
727 else
728 {//length is 0, so no x component, so pitch must be PI/2
729 xPitch = 0.0f;
730 yPitch = 1.0f;
731 }
732
733 //To obtain yaw, remove pitch from the direction vector by inverse rotation (negate the yPitch)
734 tempResult2.mV[VX] = (tempResult.mV[VX] * xPitch) + (tempResult.mV[VZ] * (-yPitch));
735 tempResult2.mV[VY] = tempResult.mV[VY];
736 tempResult2.mV[VZ] = (tempResult.mV[VZ] * xPitch) - (tempResult.mV[VX] * (-yPitch));
737 }
738 else
739 {//Need XY, because if XZ is zero there is no pitch, and yaw may not pick up the discrepancy.
740 //This also avoids roll, so less math.
741 length = sqrtf((mSpawnDirection.mV[VX]*mSpawnDirection.mV[VX]) +
742 (mSpawnDirection.mV[VY]*mSpawnDirection.mV[VY]));
743 if (length > 0.0f)
744 {
745 xPitch = tempResult.mV[VX] / length;
746 yPitch = -tempResult.mV[VY] / length;
747 }
748 else
749 {//length is 0, so no x component, so pitch must be PI/2
750 xPitch = 0.0f;
751 yPitch = 1.0f;
752 }
753
754 //To obtain yaw, remove pitch from the direction vector by inverse rotation (negate the yPitch)
755 tempResult2.mV[VX] = (tempResult.mV[VX] * xPitch) + (tempResult.mV[VY] * (-yPitch));
756 tempResult2.mV[VY] = (tempResult.mV[VY] * xPitch) - (tempResult.mV[VX] * (-yPitch));
757 tempResult2.mV[VZ] = tempResult.mV[VZ];
758 }
759
760 //Yaw is the YZ component
761 length = sqrtf((tempResult2.mV[VZ]*tempResult2.mV[VZ]) +
762 (tempResult2.mV[VY]*tempResult2.mV[VY]));
763 if (length > 0.0f)
764 {
765 xYaw = tempResult2.mV[VZ] / length;
766 yYaw = tempResult2.mV[VY] / length;
767 }
768 else
769 {
770 xYaw = 1.0f;
771 yYaw = 0.0f;
772 }
773
774 //Now apply the rotations to the actual data in the same order as derived above (pitch first)
775 tempResult.setVec(direction.mV[VX], direction.mV[VY], direction.mV[VZ]);
776 //Remember which axis pitch was on, as need to apply in the same manner here for consistency.
777 if (0.0f != mSpawnDirection.mV[VZ])
778 {
779 tempResult2.mV[VX] = (tempResult.mV[VX] * xPitch) + (tempResult.mV[VZ] * yPitch);
780 tempResult2.mV[VY] = tempResult.mV[VY];
781 tempResult2.mV[VZ] = (tempResult.mV[VZ] * xPitch) - (tempResult.mV[VX] * yPitch);
782 }
783 else
784 {
785 tempResult2.mV[VX] = (tempResult.mV[VX] * xPitch) + (tempResult.mV[VY] * yPitch);
786 tempResult2.mV[VY] = (tempResult.mV[VY] * xPitch) - (tempResult.mV[VX] * yPitch);
787 tempResult2.mV[VZ] = tempResult.mV[VZ];
788 }
789 direction.mV[VX] = tempResult2.mV[VX];
790 direction.mV[VY] = (tempResult2.mV[VY] * xYaw) + (tempResult2.mV[VZ] * yYaw);
791 direction.mV[VZ] = (tempResult2.mV[VZ] * xYaw) - (tempResult2.mV[VY] * yYaw);
792 }
793 else
794 {//The is no YZ component, so we a pointing straight along X axis (default) & therefore no rotation
795 //However, direction may be reversed
796 if (mSpawnDirection.mV[VX] < 0.0f)
797 {
798 direction.mV[VX] = -direction.mV[VX];
799 direction.mV[VZ] = -direction.mV[VZ];
800 }
801
802 }
803 }
804
805
806 //4. scale the vector by a random scale by mSpawnVelocityRange and offset by mSpawnVelocity
807 randomUnitValue = (ll_frand());
808 randomUnitValue = (randomUnitValue * mSpawnVelocityRange) + mSpawnVelocity;
809
810 mParticleState[particleNumber].velocity[0] = direction.mV[VX] * randomUnitValue;
811 mParticleState[particleNumber].velocity[1] = direction.mV[VY] * randomUnitValue;
812 mParticleState[particleNumber].velocity[2] = direction.mV[VZ] * randomUnitValue;
813
814 //add in velocity offset to match what spawned these particles
815 mParticleState[particleNumber].velocity[0] += mVelocityOffset.mV[VX];
816 mParticleState[particleNumber].velocity[1] += mVelocityOffset.mV[VY];
817 mParticleState[particleNumber].velocity[2] += mVelocityOffset.mV[VZ];
818
819 mParticleState[particleNumber].acceleration[0] = 0.0f;
820
821 mParticleState[particleNumber].acceleration[1] = 0.0f;
822
823 mParticleState[particleNumber].acceleration[2] = 0.0f;
824
825 mParticleState[particleNumber].scale[0] = (mScale_range[1] - mScale_range[0])*ll_frand() + mScale_range[0];
826 mParticleState[particleNumber].scale[1] = (mScale_range[3] - mScale_range[2])*ll_frand() + mScale_range[2];
827 mParticleState[particleNumber].scale[2] = 0.0f;
828
829 mParticleState[particleNumber].alpha[0] = (mAlpha_range[1] - mAlpha_range[0])*ll_frand() + mAlpha_range[0];
830 mParticleState[particleNumber].alpha[1] = (mAlpha_range[3] - mAlpha_range[2])*ll_frand() + mAlpha_range[2];
831 mParticleState[particleNumber].alpha[2] = 0.0f;
832
833 // **** Hack! remainingLifetime counts up from negative, to avoid subtracts! - djs
834 mParticleState[particleNumber].remainingLifetime = -((mIndividualLifetimeRange*2.0f)*ll_frand() + mIndividualLifetime - mIndividualLifetimeRange);
835
836 //rest of the state - 0 for now
837 mParticleState[particleNumber].deathOffset = 0.0f;
838 mParticleState[particleNumber].localWind[0] = 0.0f;
839 mParticleState[particleNumber].localWind[1] = 0.0f;
840 mParticleState[particleNumber].localWind[2] = 0.0f;
841}
842
843//Can override later
844void LLVOPart::onParticleBounce(const U32 particleNumber)
845{
846 mParticleState[particleNumber].velocity[0] += - (1.0f + mBounceBehavior) * mBouncePlaneNormal.mV[0] * mParticleState[particleNumber].velocity[0];
847 mParticleState[particleNumber].velocity[1] += - (1.0f + mBounceBehavior) * mBouncePlaneNormal.mV[1] * mParticleState[particleNumber].velocity[1];
848 mParticleState[particleNumber].velocity[2] += - (1.0f + mBounceBehavior) * mBouncePlaneNormal.mV[2] * mParticleState[particleNumber].velocity[2];
849
850
851 //Need to offset particle so above the plane, so it doesn't oscillate!
852 if (mParticleState[particleNumber].position[2] < mBouncePlaneZ)
853 {
854 mParticleState[particleNumber].position[2] = mBouncePlaneZ;
855 }
856}
857
858
859unsigned char LLVOPart::initializeParticlesAndConstraints(U32 initialParticles,
860 F32 diffEqAlpha[3],
861 F32 diffEqScale[3],
862 F32 scale_ranges[4],
863 F32 alpha_ranges[4],
864 F32 velocityOffset[3],
865 F32 killPlaneZ,
866 F32 killPlaneNormal[3],
867 F32 bouncePlaneZ,
868 F32 bouncePlaneNormal[3],
869 F32 spawnRange,
870 F32 spawnFrequency,
871 F32 spawnFrequencyRange,
872 F32 spawnDirection[3],
873 F32 spawnDirectionRange,
874 F32 spawnVelocity,
875 F32 spawnVelocityRange,
876 F32 speedLimit,
877 F32 windWeight,
878 F32 currentGravity[3],
879 F32 gravityWeight,
880 F32 globalLifetime,
881 F32 individualLifetime,
882 F32 individualLifetimeRange,
883 F32 alphaDecay,
884 F32 scaleDecay,
885 F32 distanceDeath,
886 F32 dampMotionFactor,
887 F32 windDiffusionFactor[3])
888{
889 // initializes particles randomly within these ranges
890 // scale ranges and alpha ranges contain initial conditions plus rates of change
891 // of initial conditions My naming is incosistent (passing alpha derivatives with alpha
892 // values), but this class is wrapped by another one, and this function is called only once
893 U32 i;
894
895
896 for (i = 0; i < 3; i++)
897 {
898 mDiffEqAlpha[i] = diffEqAlpha[i];
899 mDiffEqScale[i] = diffEqScale[i];
900 }
901
902 //First - store the initial conditions
903 for (i = 0; i < 4; i++)
904 {
905 mScale_range[i] = scale_ranges[i];
906 mAlpha_range[i] = alpha_ranges[i];
907 }
908
909 mVelocityOffset.setVec(velocityOffset[0], velocityOffset[1], velocityOffset[2]);
910
911 mKillPlaneZ = killPlaneZ;
912 mKillPlaneNormal.setVec(killPlaneNormal[0], killPlaneNormal[1], killPlaneNormal[2]);
913 mKillPlaneNormal.normVec();
914 mBouncePlaneZ = bouncePlaneZ;
915 mBouncePlaneNormal.setVec(bouncePlaneNormal[0], bouncePlaneNormal[1], bouncePlaneNormal[2]);
916 mBouncePlaneNormal.normVec();
917
918 mSpawnRange = spawnRange;
919
920 mSpawnFrequency = spawnFrequency;
921 mSpawnFrequencyRange = spawnFrequencyRange;
922
923 mSpawnDirection.setVec(spawnDirection[0], spawnDirection[1], spawnDirection[2]);
924 mSpawnDirection.normVec();
925 mSpawnDirectionRange = spawnDirectionRange;
926
927 mSpawnVelocity = spawnVelocity;
928 mSpawnVelocityRange = spawnVelocityRange;
929 mSpeedLimitSquared = speedLimit * speedLimit;
930 if (mSpeedLimitSquared < 0.0000001f)
931 {
932 mSpeedLimitSquared = 0.0000001f; //speed must be finite +ve to avoid divide by zero
933 }
934
935 mWindWeight = windWeight;
936 mCurrentGravity.setVec(currentGravity[0], currentGravity[1], currentGravity[2]);
937 mGravityWeight = gravityWeight;
938
939 mGlobalLifetime = globalLifetime;
940 mOriginalGlobalLifetime = mGlobalLifetime;
941 mIndividualLifetime = individualLifetime;
942 if (mIndividualLifetime > 0.0f)
943 {
944 mOneOverIndividualLifetime = 1.0f / mIndividualLifetime;
945 }
946 else
947 {
948 mOneOverIndividualLifetime = 0.0f;
949 }
950 mIndividualLifetimeRange = individualLifetimeRange;
951
952 mAlphaDecay = alphaDecay;
953 mScaleDecay = scaleDecay;
954 mDistanceDeathSquared = distanceDeath * distanceDeath;
955 if (mDistanceDeathSquared > 0.0f)
956 {
957 mOneOverDistanceDeathSquared = 1.0f / mDistanceDeathSquared;
958 }
959 else
960 {
961 mOneOverDistanceDeathSquared = 0.0f;
962 }
963 mDampMotionFactor = dampMotionFactor;
964
965 mWindDiffusionFactor.setVec(windDiffusionFactor[0], windDiffusionFactor[1], windDiffusionFactor[2]);
966 //Scale the values down a lot, as can expand sprites incredibly fast
967 mWindDiffusionFactor *= 0.02f;
968 if (mWindDiffusionFactor.mV[VX] > 0.02f)
969 {
970 mWindDiffusionFactor.mV[VX] = 0.02f;
971 }
972 if (mWindDiffusionFactor.mV[VY] > 0.02f)
973 {
974 mWindDiffusionFactor.mV[VY] = 0.02f;
975 }
976 if (mWindDiffusionFactor.mV[VZ] > 0.02f)
977 {
978 mWindDiffusionFactor.mV[VZ] = 0.02f;
979 }
980 //llinfos << "Made a particle system" << llendl;
981
982 for(i = 0; i < initialParticles; i++)
983 {
984 spawnParticle(i);
985 }
986
987 for(i = initialParticles; i < mNumPart; i++)
988 {
989 //create initial conditions - dead waiting to live timer
990 setParticleCountdownStateWaitingDead(i);
991 }
992 return '\0'; // success
993}
994
995U8 LLVOPart::iterateParticles(F32 deltaT)
996{
997 const F32 PART_SYS_UPDATE_PHYSICS_INPUTS_TIME = 0.2f; //How many seconds between querying wind force on a particle
998
999 U32 i; //, pos_off, vel_off, scale_off, alpha_off, lifetime_off, death_off, local_wind_off;
1000 F32 weightedDeathSum;
1001 U8 any_leftQ = 0;
1002
1003 F32 windWeightDT = mWindWeight*deltaT;
1004 F32 gravityWeightDT = mGravityWeight*deltaT;
1005
1006 mLastTime = mCurrTime;
1007 mCurrTime += deltaT;
1008
1009 if ((mCurrTime - mUpdatePhysicsInputsTime) > PART_SYS_UPDATE_PHYSICS_INPUTS_TIME)
1010 {
1011 // If needed, obtain latest wind for the whole system
1012 mCurrentWind = mRegionp->mWind.getVelocity(getPositionRegion());
1013 mCurrentWindMagnitude = sqrt((mCurrentWind.mV[VX] * mCurrentWind.mV[VX]) +
1014 (mCurrentWind.mV[VY] * mCurrentWind.mV[VY]) +
1015 (mCurrentWind.mV[VZ] * mCurrentWind.mV[VZ]));
1016 //don't know a max, so just make up a reasonably large value for now and cap.
1017 mCurrentWindMagnitude = mCurrentWindMagnitude * 0.05f;
1018 if (mCurrentWindMagnitude > 1.0f)
1019 {
1020 mCurrentWindMagnitude = 1.0f;
1021 }
1022 mCurrentWindMagnitudeSquareRoot = sqrtf(mCurrentWindMagnitude);
1023 }
1024
1025 //TO DO - LOD algorithm (as with display??)
1026 for(i = 0; i < mNumPart; i++)
1027 {
1028 //lifetime_off = remaining_lifetime_offset_i(i);
1029 if(0 != mDeadArr[i])
1030 {
1031 //test for spawns amongst the dead as follows
1032 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_SPAWN)
1033 {
1034 // **** Hack! remainingLifetime counts up from negative, to avoid subtracts! - djs
1035 //if die below and spawn flag set, set mParticleState[i].remainingLifetime to a respawn random time
1036 //based on mSpawnFrequency and mSpawnFrequencyRange
1037 //in this section, if spawn flag set, count down - deltaT. If time is negative, call Spawn method, else continue
1038 mParticleState[i].remainingLifetime += deltaT;
1039 if (mParticleState[i].remainingLifetime > 0.0f)
1040 {
1041 spawnParticle(i);
1042 }
1043 }
1044
1045 if(0 != mDeadArr[i])
1046 {
1047 continue; // stop animating dead particles -- else they might come back alive!
1048 // since we're not alive, do nothing to "any_leftQ"
1049 }
1050 }
1051
1052
1053 //New way - apply external forces to each particle and then update
1054 // position at ....
1055 mParticleState[i].position[0] += mParticleState[i].velocity[0]*deltaT; // x position
1056 mParticleState[i].position[1] += mParticleState[i].velocity[1]*deltaT; // y position
1057 mParticleState[i].position[2] += mParticleState[i].velocity[2]*deltaT; // z position
1058
1059 //then apply force if required
1060 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_AFFECTED_BY_WIND)
1061 {
1062 mParticleState[i].velocity[0] += mCurrentWind.mV[0]*windWeightDT;
1063 mParticleState[i].velocity[1] += mCurrentWind.mV[1]*windWeightDT;
1064 mParticleState[i].velocity[2] += mCurrentWind.mV[2]*windWeightDT;
1065 }
1066 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_AFFECTED_BY_GRAVITY)
1067 {
1068 mParticleState[i].velocity[0] += mCurrentGravity.mV[0]*gravityWeightDT;
1069 mParticleState[i].velocity[1] += mCurrentGravity.mV[1]*gravityWeightDT;
1070 mParticleState[i].velocity[2] += mCurrentGravity.mV[2]*gravityWeightDT;
1071 }
1072 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_EVALUATE_WIND_PER_PARTICLE)
1073 {
1074 if ((mCurrTime - mUpdatePhysicsInputsTime) > PART_SYS_UPDATE_PHYSICS_INPUTS_TIME)
1075 {
1076 LLVector3 wind, current_position_region;
1077 // If needed, obtain latest wind per particle
1078 //Particle positions are relative to the object center.
1079 current_position_region.setVec(getPositionRegion());
1080 current_position_region.mV[VX] += (mParticleState[i].position[0]);
1081 current_position_region.mV[VY] += (mParticleState[i].position[1]);
1082 current_position_region.mV[VZ] += (mParticleState[i].position[2]);
1083
1084 wind = mRegionp->mWind.getVelocity(current_position_region);
1085 mParticleState[i].localWind[0] = wind.mV[0];
1086 mParticleState[i].localWind[1] = wind.mV[1];
1087 mParticleState[i].localWind[2] = wind.mV[2];
1088 }
1089 mParticleState[i].velocity[0] += mParticleState[i].localWind[0]*windWeightDT;
1090 mParticleState[i].velocity[1] += mParticleState[i].localWind[1]*windWeightDT;
1091 mParticleState[i].velocity[2] += mParticleState[i].localWind[2]*windWeightDT;
1092 }
1093
1094 //then apply drag if required
1095 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_DAMP_MOTION)
1096 {
1097 F32 dampAmount;
1098
1099 dampAmount = (mParticleState[i].velocity[0] * mParticleState[i].velocity[0]) +
1100 (mParticleState[i].velocity[1] * mParticleState[i].velocity[1]) +
1101 (mParticleState[i].velocity[2] * mParticleState[i].velocity[2]);
1102 dampAmount = 1.0f - ((mSpeedLimitSquared - dampAmount) / mSpeedLimitSquared);
1103 if (dampAmount < 0.0f)
1104 {
1105 dampAmount = 0.0f;
1106 }
1107 else if (dampAmount > 1.0f)
1108 {
1109 dampAmount = 1.0f;
1110 }
1111
1112 //Damp prop to deltaT
1113 dampAmount = -1.f * dampAmount * mDampMotionFactor * deltaT;
1114
1115 mParticleState[i].velocity[0] += dampAmount * mParticleState[i].velocity[0];
1116 mParticleState[i].velocity[1] += dampAmount * mParticleState[i].velocity[1];
1117 mParticleState[i].velocity[2] += dampAmount * mParticleState[i].velocity[2];
1118 }
1119
1120 //check for bounces if required
1121 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_BOUNCE)
1122 {//Is the particle below the bounce plane??
1123 LLVector3 tempBounceTest;
1124 tempBounceTest.setVec(mParticleState[i].position[0], mParticleState[i].position[1], mParticleState[i].position[2]);
1125 tempBounceTest.mV[VZ] -= mBouncePlaneZ;
1126
1127 //This is simplistic for now
1128 if ((tempBounceTest.mV[VZ] < 0.0f) && (mParticleState[i].velocity[2] < 0.0f))
1129 {
1130 onParticleBounce(i);
1131 }
1132 }
1133
1134 //maintain the old way of updating scale and alpha
1135 // clamp scale and alpha to reasonable values
1136 // should this section of code only be called if we're *actually* animating scale and alpha?
1137 mParticleState[i].alpha[2] = mDiffEqAlpha[0] + // constant term
1138 mDiffEqAlpha[1]*mParticleState[i].alpha[0] + // zeroth derivative term
1139 mDiffEqAlpha[2]*mParticleState[i].alpha[1]; // first derivative term
1140
1141 mParticleState[i].scale[2] = mDiffEqScale[0] + // constant term
1142 mDiffEqScale[1]*mParticleState[i].scale[0] + // zeroth derivative term
1143 mDiffEqScale[2]*mParticleState[i].scale[1]; // first derivative term
1144
1145 mParticleState[i].scale[0] += mParticleState[i].scale[1]*deltaT; // scale
1146
1147 mParticleState[i].alpha[0] += mParticleState[i].alpha[1]*deltaT; // alpha
1148 if (mParticleState[i].scale[0] < 0.0f)
1149 {
1150 mParticleState[i].scale[0] = 0.01f;
1151 }
1152
1153 if (mParticleState[i].alpha[0] < 0.0f)
1154 {
1155 mParticleState[i].alpha[0] = 0.0f;
1156 }
1157 else if (mParticleState[i].alpha[0] > 1.0f)
1158 {
1159 mParticleState[i].alpha[0] = 1.0f;
1160 }
1161 mParticleState[i].scale[1] += mParticleState[i].scale[2]*deltaT;
1162 mParticleState[i].alpha[1] += mParticleState[i].alpha[2]*deltaT;
1163
1164 //Is particle appearance affected by wind?
1165 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_WIND_DIFFUSION)
1166 {
1167 F32 windMagnitude, windMagnitudeSqrt;
1168
1169 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_EVALUATE_WIND_PER_PARTICLE)
1170 {
1171 windMagnitude = sqrt((mParticleState[i].localWind[0] * mParticleState[i].localWind[0]) +
1172 (mParticleState[i].localWind[1] * mParticleState[i].localWind[1]) +
1173 (mParticleState[i].localWind[2] * mParticleState[i].localWind[2]));
1174 //don't know a max, so just make up a reasonably large value for now and cap.
1175 windMagnitude = windMagnitude * 0.05f;
1176 if (windMagnitude > 1.0f)
1177 {
1178 windMagnitude = 1.0f;
1179 }
1180 windMagnitudeSqrt = sqrt(mCurrentWindMagnitude);
1181 }
1182 else
1183 {
1184 windMagnitude = mCurrentWindMagnitude;
1185 windMagnitudeSqrt = mCurrentWindMagnitudeSquareRoot;
1186 }
1187
1188 //ignore alpha for now, as it is reduced by distance death and life expectancy in any case.
1189 //mParticleState[i].alpha[0] *= (1.0 - (windMagnitude * mWindDiffusionFactor.mV[VX]));
1190 //Scaling prop to sqr root. Should really be cube root, as we are dealing with a volume, but
1191 //heavy math, and sqr looks OK.
1192 mParticleState[i].scale[0] *= (1.f + (windMagnitudeSqrt * mWindDiffusionFactor.mV[VX]));
1193 }
1194
1195 // we have to store the death of the particle somewhere
1196
1197 //now test for death if life expires or hits edge of death radius (given flags enabled)
1198 //aapply alpha and scale fade proportional to proximity to death - test for death flags and produce a weighted sum
1199 //taking into account time to live and distance to death.
1200 weightedDeathSum = 1.0f;
1201
1202 if (mFlags[PART_SYS_KILL_BYTE] & PART_SYS_TIME_DEATH)
1203 {
1204 // **** Hack! remainingLifetime counts up from negative, to avoid subtracts! - djs
1205 mParticleState[i].remainingLifetime += deltaT;
1206 weightedDeathSum *= -1.f * (mParticleState[i].remainingLifetime * mOneOverIndividualLifetime);
1207 if (mParticleState[i].remainingLifetime > 0.0f)
1208 {
1209 mDeadArr[i] = 1;
1210 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_SPAWN)
1211 {
1212 setParticleCountdownStateWaitingDead(i);
1213 }
1214 weightedDeathSum = 0.0f;
1215 }
1216 else if (weightedDeathSum > 1.0f)
1217 {
1218 weightedDeathSum = 1.0f;
1219 }
1220 }
1221 if (mFlags[PART_SYS_KILL_BYTE] & PART_SYS_DISTANCE_DEATH)
1222 {
1223 F32 radius;
1224 radius = (mParticleState[i].position[0]*mParticleState[i].position[0] +
1225 mParticleState[i].position[1]*mParticleState[i].position[1] +
1226 mParticleState[i].position[2]*mParticleState[i].position[2]);
1227 radius = (mDistanceDeathSquared - radius) * mOneOverDistanceDeathSquared;
1228 weightedDeathSum = weightedDeathSum * radius;
1229 if (radius <= 0.0f)
1230 {
1231 mDeadArr[i] = 1;
1232 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_SPAWN)
1233 {
1234 setParticleCountdownStateWaitingDead(i);
1235 }
1236 weightedDeathSum = 0.0f;
1237 }
1238 else if (weightedDeathSum > 1.0f)
1239 {
1240 weightedDeathSum = 1.0f;
1241 }
1242 }
1243 if (mFlags[PART_SYS_KILL_BYTE] & PART_SYS_KILL_PLANE)
1244 {
1245 if (mParticleState[i].position[2] <= mKillPlaneZ)
1246 {
1247 llinfos << "kill plane" << llendl;
1248 mDeadArr[i] = 1;
1249 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_SPAWN)
1250 {
1251 setParticleCountdownStateWaitingDead(i);
1252 }
1253 weightedDeathSum = 0.0f;
1254 }
1255 }
1256 if (mFlags[PART_SYS_KILL_BYTE] & PART_SYS_GLOBAL_DIE)
1257 {
1258 weightedDeathSum = weightedDeathSum * (mGlobalLifetime / mOriginalGlobalLifetime);
1259 }
1260
1261
1262 mParticleState[i].deathOffset = weightedDeathSum;
1263
1264 any_leftQ |= (1 - mDeadArr[i]);
1265 }
1266
1267 if (mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_SPAWN)
1268 {//May be just waiting for new particles to be born, with none around currently
1269 any_leftQ = TRUE;
1270 }
1271
1272
1273 if (mFlags[PART_SYS_KILL_BYTE] & PART_SYS_GLOBAL_DIE)
1274 {
1275 mGlobalLifetime -= deltaT;
1276 if (mGlobalLifetime < 0.0f)
1277 {
1278 any_leftQ = FALSE;
1279 }
1280 }
1281
1282
1283 if ((mCurrTime - mUpdatePhysicsInputsTime) > PART_SYS_UPDATE_PHYSICS_INPUTS_TIME)
1284 {//Have to execute this at end of itterate, as several independent processes rely on the test being true.
1285 mUpdatePhysicsInputsTime = mCurrTime;
1286 }
1287
1288 mNumLiveParticles = 0;
1289 for ( i = 0; i < mNumPart; i++)
1290 {
1291 if (!mDeadArr[i])
1292 {
1293 mNumLiveParticles++;
1294 }
1295 }
1296 return any_leftQ;
1297}
1298
1299void LLVOPart::reverseTranslateParticlesAndPotentiallyKill(const LLVector3 &moveBy)
1300{
1301 U32 i;//pos_off;
1302
1303 for(i = 0; i < mNumPart; i++)
1304 {
1305 if(0 == mDeadArr[i])
1306 {
1307 //pos_off = pos_offset_i(i);
1308 mParticleState[i].position[0] += moveBy.mV[VX];
1309 mParticleState[i].position[1] += moveBy.mV[VY];
1310 mParticleState[i].position[2] += moveBy.mV[VZ];
1311
1312 }
1313 }
1314}
1315
1316void LLVOPart::translateParticlesBy(const LLVector3 &moveBy)
1317{
1318 if (!(mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_BOUNCE))
1319 {//Do not translate if bouncing is on, as the whole system shifts adding or subtractiong potential energy
1320
1321 //If the object moves, the particles should not, otherwise will look like a rigid structure
1322 reverseTranslateParticlesAndPotentiallyKill(moveBy);
1323
1324 mSpawnPoint.mV[VX] += moveBy.mV[VX];
1325 mSpawnPoint.mV[VY] += moveBy.mV[VY];
1326 mSpawnPoint.mV[VZ] += moveBy.mV[VZ];
1327 }
1328}
1329
1330
1331void LLVOPart::translateParticlesTo(const LLVector3 &moveTo)
1332{
1333 LLVector3 moveBy;
1334
1335 if (!(mFlags[PART_SYS_ACTION_BYTE] & PART_SYS_BOUNCE))
1336 {//Do not translate if bouncing is on, as the whole system shifts adding or subtractiong potential energy
1337
1338 //If the object moves, the particles should not, otherwise will look like a rigid structure
1339 moveBy.setVec(moveTo.mV[VX] - mSpawnPoint.mV[VX], moveTo.mV[VY] - mSpawnPoint.mV[VY], moveTo.mV[VZ] - mSpawnPoint.mV[VZ]);
1340 reverseTranslateParticlesAndPotentiallyKill(moveBy);
1341
1342 mSpawnPoint.mV[VX] = moveTo.mV[VX];
1343 mSpawnPoint.mV[VY] = moveTo.mV[VY];
1344 mSpawnPoint.mV[VZ] = moveTo.mV[VZ];
1345 }
1346}
1347
1348
1349void LLVOPart::rotateParticlesBy(const LLQuaternion &q)
1350{
1351 mOriginOrientation *= q;
1352}
1353
1354
1355void LLVOPart::rotateParticlesTo(const LLQuaternion &q)
1356{
1357 mOriginOrientation.setQuatInit(q.mQ[VX], q.mQ[VY], q.mQ[VZ], q.mQ[VW]);
1358}