aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llviewerpartsim.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llviewerpartsim.cpp')
-rw-r--r--linden/indra/newview/llviewerpartsim.cpp645
1 files changed, 645 insertions, 0 deletions
diff --git a/linden/indra/newview/llviewerpartsim.cpp b/linden/indra/newview/llviewerpartsim.cpp
new file mode 100644
index 0000000..1175553
--- /dev/null
+++ b/linden/indra/newview/llviewerpartsim.cpp
@@ -0,0 +1,645 @@
1/**
2 * @file llviewerpartsim.cpp
3 * @brief LLViewerPart class implementation
4 *
5 * Copyright (c) 2003-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 "llviewerpartsim.h"
31
32#include "llviewercontrol.h"
33
34#include "llagent.h"
35#include "llviewerobjectlist.h"
36#include "llviewerpartsource.h"
37#include "llviewerregion.h"
38#include "llvopartgroup.h"
39#include "llworld.h"
40#include "pipeline.h"
41
42const S32 MAX_PART_COUNT = 4096;
43
44const F32 PART_SIM_BOX_SIDE = 32.f;
45const F32 PART_SIM_BOX_OFFSET = 0.5f*PART_SIM_BOX_SIDE;
46const F32 PART_SIM_BOX_RAD = 0.5f*F_SQRT3*PART_SIM_BOX_SIDE;
47
48//static
49S32 LLViewerPartSim::sMaxParticleCount = 0;
50S32 LLViewerPartSim::sParticleCount = 0;
51
52
53U32 LLViewerPart::sNextPartID = 1;
54
55LLViewerPart::LLViewerPart()
56{
57 mPartSourcep = NULL;
58}
59
60LLViewerPart::~LLViewerPart()
61{
62 mPartSourcep = NULL;
63}
64
65LLViewerPart &LLViewerPart::operator=(const LLViewerPart &part)
66{
67 mPartID = part.mPartID;
68 mFlags = part.mFlags;
69 mMaxAge = part.mMaxAge;
70
71 mStartColor = part.mStartColor;
72 mEndColor = part.mEndColor;
73 mStartScale = part.mStartScale;
74 mEndScale = part.mEndScale;
75
76 mPosOffset = part.mPosOffset;
77 mParameter = part.mParameter;
78
79 mLastUpdateTime = part.mLastUpdateTime;
80 mVPCallback = part.mVPCallback;
81 mPartSourcep = part.mPartSourcep;
82
83 mImagep = part.mImagep;
84 mPosAgent = part.mPosAgent;
85 mVelocity = part.mVelocity;
86 mAccel = part.mAccel;
87 mColor = part.mColor;
88 mScale = part.mScale;
89
90
91 return *this;
92}
93
94void LLViewerPart::init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb)
95{
96 mPartID = LLViewerPart::sNextPartID;
97 LLViewerPart::sNextPartID++;
98 mFlags = 0x00f;
99 mLastUpdateTime = 0.f;
100 mMaxAge = 10.f;
101
102 mVPCallback = cb;
103 mPartSourcep = sourcep;
104
105 mImagep = imagep;
106}
107
108
109/////////////////////////////
110//
111// LLViewerPartGroup implementation
112//
113//
114
115
116LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 box_side)
117{
118 mVOPartGroupp = NULL;
119 mRegionp = gWorldPointer->getRegionFromPosAgent(center_agent);
120 if (!mRegionp)
121 {
122 //llwarns << "No region at position, using agent region!" << llendl;
123 mRegionp = gAgent.getRegion();
124 }
125 mCenterAgent = center_agent;
126 mBoxRadius = F_SQRT3*box_side*0.5f;
127
128 LLVector3 rad_vec(box_side*0.5f, box_side*0.5f, box_side*0.5f);
129 rad_vec += LLVector3(0.001f, 0.001f, 0.001f);
130 mMinObjPos = mCenterAgent - rad_vec;
131 mMaxObjPos = mCenterAgent + rad_vec;
132}
133
134LLViewerPartGroup::~LLViewerPartGroup()
135{
136 cleanup();
137 S32 count = mParticles.count();
138 S32 i;
139
140 for (i = 0; i < count; i++)
141 {
142 mParticles[i].mPartSourcep = NULL;
143 }
144 mParticles.reset();
145 LLViewerPartSim::decPartCount(count);
146}
147
148void LLViewerPartGroup::cleanup()
149{
150 if (mVOPartGroupp)
151 {
152 if (!mVOPartGroupp->isDead())
153 {
154 gObjectList.killObject(mVOPartGroupp);
155 }
156 mVOPartGroupp = NULL;
157 }
158}
159
160BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos)
161{
162 if ((pos.mV[VX] < mMinObjPos.mV[VX])
163 || (pos.mV[VY] < mMinObjPos.mV[VY])
164 || (pos.mV[VZ] < mMinObjPos.mV[VZ]))
165 {
166 return FALSE;
167 }
168
169 if ((pos.mV[VX] > mMaxObjPos.mV[VX])
170 || (pos.mV[VY] > mMaxObjPos.mV[VY])
171 || (pos.mV[VZ] > mMaxObjPos.mV[VZ]))
172 {
173 return FALSE;
174 }
175
176 return TRUE;
177}
178
179
180BOOL LLViewerPartGroup::addPart(LLViewerPart &part)
181{
182 if (!posInGroup(part.mPosAgent) ||
183 (mVOPartGroupp.notNull() && (part.mImagep != mVOPartGroupp->getTEImage(0))))
184 {
185 return FALSE;
186 }
187
188 if (!mVOPartGroupp)
189 {
190 mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
191 mVOPartGroupp->setViewerPartGroup(this);
192 mVOPartGroupp->setPositionAgent(getCenterAgent());
193 mVOPartGroupp->setScale(LLVector3(PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE));
194 mVOPartGroupp->setTEImage(0, part.mImagep);
195 gPipeline.addObject(mVOPartGroupp);
196 }
197
198 mParticles.put(part);
199 LLViewerPartSim::incPartCount(1);
200 return TRUE;
201}
202
203
204void LLViewerPartGroup::removePart(const S32 part_num)
205{
206 // Remove the entry for the particle we just deleted.
207 LLPointer<LLViewerPartSource> ps = mParticles[mParticles.count() - 1].mPartSourcep;
208
209 mParticles[mParticles.count() - 1].mPartSourcep = NULL;
210 mParticles.remove(part_num);
211 if (part_num < mParticles.count())
212 {
213 mParticles[part_num].mPartSourcep = ps;
214 }
215
216 LLViewerPartSim::decPartCount(1);
217}
218
219
220void LLViewerPartGroup::updateParticles(const F32 dt)
221{
222 S32 i, count;
223
224
225 LLVector3 gravity(0.f, 0.f, -9.8f);
226
227 LLViewerRegion *regionp = getRegion();
228 count = mParticles.count();
229 for (i = 0; i < count; i++)
230 {
231 LLVector3 a(0.f, 0.f, 0.f);
232 LLViewerPart &part = mParticles[i];
233
234 // Update current time
235 const F32 cur_time = part.mLastUpdateTime + dt;
236 const F32 frac = cur_time/part.mMaxAge;
237
238 // "Drift" the object based on the source object
239 if (part.mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
240 {
241 part.mPosAgent = part.mPartSourcep->mPosAgent;
242 part.mPosAgent += part.mPosOffset;
243 }
244
245 // Do a custom callback if we have one...
246 if (part.mVPCallback)
247 {
248 (*part.mVPCallback)(part, dt);
249 }
250
251 if (part.mFlags & LLPartData::LL_PART_WIND_MASK)
252 {
253 LLVector3 tempVel(part.mVelocity);
254 part.mVelocity *= 1.f - 0.1f*dt;
255 part.mVelocity += 0.1f*dt*regionp->mWind.getVelocity(regionp->getPosRegionFromAgent(part.mPosAgent));
256 }
257
258 // Now do interpolation towards a target
259 if (part.mFlags & LLPartData::LL_PART_TARGET_POS_MASK)
260 {
261 F32 remaining = part.mMaxAge - part.mLastUpdateTime;
262 F32 step = dt / remaining;
263
264 step = llclamp(step, 0.f, 0.1f);
265 step *= 5.f;
266 // we want a velocity that will result in reaching the target in the
267 // Interpolate towards the target.
268 LLVector3 delta_pos = part.mPartSourcep->mTargetPosAgent - part.mPosAgent;
269
270 delta_pos /= remaining;
271
272 part.mVelocity *= (1.f - step);
273 part.mVelocity += step*delta_pos;
274 //part.mPosAgent *= 1.f - to_target_frac;
275 //part.mPosAgent += to_target_frac*part.mPartSourcep->mTargetPosAgent;
276 }
277
278
279 if (part.mFlags & LLPartData::LL_PART_TARGET_LINEAR_MASK)
280 {
281 LLVector3 delta_pos = part.mPartSourcep->mTargetPosAgent - part.mPartSourcep->mPosAgent;
282 part.mPosAgent = part.mPartSourcep->mPosAgent;
283 part.mPosAgent += frac*delta_pos;
284 part.mVelocity = delta_pos;
285 }
286 else
287 {
288 // Do velocity interpolation
289 part.mPosAgent += dt*part.mVelocity;
290 part.mPosAgent += 0.5f*dt*dt*part.mAccel;
291 part.mVelocity += part.mAccel*dt;
292 }
293
294 // Do a bounce test
295 if (part.mFlags & LLPartData::LL_PART_BOUNCE_MASK)
296 {
297 // Need to do point vs. plane check...
298 // For now, just check relative to object height...
299 F32 dz = part.mPosAgent.mV[VZ] - part.mPartSourcep->mPosAgent.mV[VZ];
300 if (dz < 0)
301 {
302 part.mPosAgent.mV[VZ] += -2.f*dz;
303 part.mVelocity.mV[VZ] *= -0.75f;
304 }
305 }
306
307
308 // Reset the offset from the source position
309 if (part.mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
310 {
311 part.mPosOffset = part.mPosAgent;
312 part.mPosOffset -= part.mPartSourcep->mPosAgent;
313 }
314
315 // Do color interpolation
316 if (part.mFlags & LLPartData::LL_PART_INTERP_COLOR_MASK)
317 {
318 part.mColor.setVec(part.mStartColor);
319 part.mColor *= 1.f - frac;
320 part.mColor.mV[3] *= (1.f - frac)*part.mStartColor.mV[3];
321 part.mColor += frac*part.mEndColor;
322 part.mColor.mV[3] += frac*part.mEndColor.mV[3];
323 }
324
325 // Do scale interpolation
326 if (part.mFlags & LLPartData::LL_PART_INTERP_SCALE_MASK)
327 {
328 part.mScale.setVec(part.mStartScale);
329 part.mScale *= 1.f - frac;
330 part.mScale += frac*part.mEndScale;
331 }
332
333 // Set the last update time to now.
334 part.mLastUpdateTime = cur_time;
335
336
337 // Kill dead particles (either flagged dead, or too old)
338 if ((part.mLastUpdateTime > part.mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part.mFlags))
339 {
340 removePart(i);
341 i--;
342 count--;
343 }
344 else if (!posInGroup(part.mPosAgent))
345 {
346 // Transfer particles between groups
347 gWorldPointer->mPartSim.put(part);
348 removePart(i);
349 i--;
350 count--;
351 }
352 }
353
354 // Kill the viewer object if this particle group is empty
355 if (!mParticles.count())
356 {
357 gObjectList.killObject(mVOPartGroupp);
358 mVOPartGroupp = NULL;
359 }
360}
361
362
363void LLViewerPartGroup::shift(const LLVector3 &offset)
364{
365 mCenterAgent += offset;
366 mMinObjPos += offset;
367 mMaxObjPos += offset;
368
369 S32 count = mParticles.count();
370 S32 i;
371 for (i = 0; i < count; i++)
372 {
373 mParticles[i].mPosAgent += offset;
374 }
375}
376
377
378//////////////////////////////////
379//
380// LLViewerPartSim implementation
381//
382//
383
384
385LLViewerPartSim::LLViewerPartSim()
386{
387 sMaxParticleCount = gSavedSettings.getS32("RenderMaxPartCount");
388}
389
390
391LLViewerPartSim::~LLViewerPartSim()
392{
393 S32 i;
394 S32 count;
395
396 // Kill all of the groups (and particles)
397 count = mViewerPartGroups.count();
398 for (i = 0; i < count; i++)
399 {
400 delete mViewerPartGroups[i];
401 }
402 mViewerPartGroups.reset();
403
404 // Kill all of the sources
405 count = mViewerPartSources.count();
406 for (i = 0; i < count; i++)
407 {
408 mViewerPartSources[i] = NULL;
409 }
410 mViewerPartSources.reset();
411}
412
413BOOL LLViewerPartSim::shouldAddPart()
414{
415 if (sParticleCount > 0.75f*sMaxParticleCount)
416 {
417
418 F32 frac = (F32)sParticleCount/(F32)sMaxParticleCount;
419 frac -= 0.75;
420 frac *= 3.f;
421 if (frand(1.f) < frac)
422 {
423 // Skip...
424 return FALSE;
425 }
426 }
427 if (sParticleCount >= MAX_PART_COUNT)
428 {
429 return FALSE;
430 }
431
432 return TRUE;
433}
434
435void LLViewerPartSim::addPart(LLViewerPart &part)
436{
437 if (sParticleCount < MAX_PART_COUNT)
438 {
439 put(part);
440 }
441}
442
443LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart &part)
444{
445 const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
446 if (part.mPosAgent.magVecSquared() > MAX_MAG)
447 {
448#ifndef LL_RELEASE_FOR_DOWNLOAD
449 llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
450 llwarns << part.mPosAgent << llendl;
451#endif
452 return NULL;
453 }
454
455 S32 i;
456 S32 count;
457
458 count = mViewerPartGroups.count();
459 for (i = 0; i < count; i++)
460 {
461 if (mViewerPartGroups[i]->addPart(part))
462 {
463 // We found a spatial group that we fit into, add us and exit
464 return mViewerPartGroups[i];
465 }
466 }
467
468 // Hmm, we didn't fit in any of the existing spatial groups
469 // Create a new one...
470 LLViewerPartGroup *groupp = createViewerPartGroup(part.mPosAgent);
471 if (!groupp->addPart(part))
472 {
473 llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
474 llinfos << groupp->getCenterAgent() << llendl;
475 llinfos << part.mPosAgent << llendl;
476 return NULL;
477 }
478 return groupp;
479}
480
481LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent)
482{
483 F32 x_origin = ((S32)(pos_agent.mV[VX]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
484 if (x_origin > pos_agent.mV[VX])
485 {
486 x_origin -= PART_SIM_BOX_SIDE;
487 }
488
489 F32 y_origin = ((S32)(pos_agent.mV[VY]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
490 if (y_origin > pos_agent.mV[VY])
491 {
492 y_origin -= PART_SIM_BOX_SIDE;
493 }
494
495 F32 z_origin = ((S32)(pos_agent.mV[VZ]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
496 if (z_origin > pos_agent.mV[VZ])
497 {
498 z_origin -= PART_SIM_BOX_SIDE;
499 }
500
501 LLVector3 group_center(x_origin + PART_SIM_BOX_OFFSET,
502 y_origin + PART_SIM_BOX_OFFSET,
503 z_origin + PART_SIM_BOX_OFFSET);
504
505 LLViewerPartGroup *groupp = new LLViewerPartGroup(group_center, PART_SIM_BOX_SIDE);
506 mViewerPartGroups.put(groupp);
507 return groupp;
508}
509
510
511void LLViewerPartSim::shift(const LLVector3 &offset)
512{
513 S32 i;
514 S32 count;
515
516 count = mViewerPartSources.count();
517 for (i = 0; i < count; i++)
518 {
519 mViewerPartSources[i]->mPosAgent += offset;
520 mViewerPartSources[i]->mTargetPosAgent += offset;
521 mViewerPartSources[i]->mLastUpdatePosAgent += offset;
522 }
523
524 count = mViewerPartGroups.count();
525 for (i = 0; i < count; i++)
526 {
527 mViewerPartGroups[i]->shift(offset);
528 }
529}
530
531
532void LLViewerPartSim::updateSimulation()
533{
534 LLMemType mt(LLMemType::MTYPE_PARTICLES);
535
536 static LLFrameTimer update_timer;
537
538 const F32 dt = update_timer.getElapsedTimeAndResetF32();
539
540 if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
541 {
542 return;
543 }
544
545 // Start at a random particle system so the same
546 // particle system doesn't always get first pick at the
547 // particles. Theoretically we'd want to do this in distance
548 // order or something, but sorting particle sources will be a big
549 // pain.
550 S32 i;
551 S32 count = mViewerPartSources.count();
552 S32 start = (S32)frand((F32)count);
553 S32 dir = 1;
554 if (frand(1.0) > 0.5f)
555 {
556 dir = -1;
557 }
558
559 S32 num_updates = 0;
560 for (i = start; num_updates < count;)
561 {
562 if (i >= count)
563 {
564 i = 0;
565 }
566 if (i < 0)
567 {
568 i = count - 1;
569 }
570
571 if (!mViewerPartSources[i]->isDead())
572 {
573 mViewerPartSources[i]->update(dt);
574 }
575
576 if (mViewerPartSources[i]->isDead())
577 {
578 mViewerPartSources.remove(i);
579 count--;
580 }
581 else
582 {
583 i += dir;
584 }
585 num_updates++;
586 }
587
588
589 count = mViewerPartGroups.count();
590 for (i = 0; i < count; i++)
591 {
592 mViewerPartGroups[i]->updateParticles(dt);
593 if (!mViewerPartGroups[i]->getCount())
594 {
595 delete mViewerPartGroups[i];
596 mViewerPartGroups.remove(i);
597 i--;
598 count--;
599 }
600 }
601 //llinfos << "Particles: " << sParticleCount << llendl;
602}
603
604
605void LLViewerPartSim::addPartSource(LLViewerPartSource *sourcep)
606{
607 if (!sourcep)
608 {
609 llwarns << "Null part source!" << llendl;
610 return;
611 }
612 mViewerPartSources.put(sourcep);
613}
614
615
616void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
617{
618 S32 i, count;
619 count = mViewerPartGroups.count();
620 for (i = 0; i < count; i++)
621 {
622 if (mViewerPartGroups[i]->getRegion() == regionp)
623 {
624 delete mViewerPartGroups[i];
625 mViewerPartGroups.remove(i);
626 i--;
627 count--;
628 }
629 }
630}
631
632void LLViewerPartSim::cleanMutedParticles(const LLUUID& task_id)
633{
634 S32 i;
635 S32 count = mViewerPartSources.count();
636 for (i = 0; i < count; ++i)
637 {
638 if (mViewerPartSources[i]->getOwnerUUID() == task_id)
639 {
640 mViewerPartSources.remove(i);
641 i--;
642 count--;
643 }
644 }
645}