diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/llvopart.cpp | 1358 |
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 | |||
44 | const F32 MAX_PART_LIFETIME = 120.f; | ||
45 | |||
46 | extern U64 gFrameTime; | ||
47 | |||
48 | LLVOPart::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 | |||
60 | LLVOPart::~LLVOPart() | ||
61 | { | ||
62 | delete [] mParticleState; | ||
63 | mParticleState = NULL; | ||
64 | |||
65 | delete [] mDeadArr; | ||
66 | mDeadArr = NULL; | ||
67 | } | ||
68 | |||
69 | void LLVOPart::initClass() | ||
70 | { | ||
71 | } | ||
72 | |||
73 | U32 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 | |||
205 | BOOL LLVOPart::isActive() const | ||
206 | { | ||
207 | return TRUE; | ||
208 | } | ||
209 | |||
210 | BOOL 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 | |||
252 | void 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 | |||
270 | LLDrawable* 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 | |||
281 | BOOL 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 | |||
478 | void 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 | |||
592 | U8 LLVOPart::setParticlesDistFadeout(F32 beginFadeout, F32 endFadeout) | ||
593 | { | ||
594 | // This doesn't do anything... | ||
595 | return 1; | ||
596 | } | ||
597 | |||
598 | unsigned 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 | |||
651 | void 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 | ||
661 | void 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 | ||
844 | void 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 | |||
859 | unsigned 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 | |||
995 | U8 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 | |||
1299 | void 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 | |||
1316 | void 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 | |||
1331 | void 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 | |||
1349 | void LLVOPart::rotateParticlesBy(const LLQuaternion &q) | ||
1350 | { | ||
1351 | mOriginOrientation *= q; | ||
1352 | } | ||
1353 | |||
1354 | |||
1355 | void LLVOPart::rotateParticlesTo(const LLQuaternion &q) | ||
1356 | { | ||
1357 | mOriginOrientation.setQuatInit(q.mQ[VX], q.mQ[VY], q.mQ[VZ], q.mQ[VW]); | ||
1358 | } | ||