mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-19 19:59:09 +00:00
Hoist MOULOpenSourceClientPlugin/Plasma20/* to top level
to match H'uru layout and make patching/cherry-picking easier.
This commit is contained in:
911
Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.cpp
Normal file
911
Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.cpp
Normal file
@ -0,0 +1,911 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#include "hsTypes.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "plParticle.h"
|
||||
#include "plParticleEffect.h"
|
||||
#include "plEffectTargetInfo.h"
|
||||
#include "plConvexVolume.h"
|
||||
#include "plBoundInterface.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "plPipeline.h"
|
||||
#include "hsFastMath.h"
|
||||
#include "../plMath/plRandom.h"
|
||||
#include "plParticleSystem.h"
|
||||
#include "../plMessage/plParticleUpdateMsg.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
plParticleCollisionEffect::plParticleCollisionEffect()
|
||||
{
|
||||
fBounds = nil;
|
||||
fSceneObj = nil;
|
||||
}
|
||||
|
||||
plParticleCollisionEffect::~plParticleCollisionEffect()
|
||||
{
|
||||
}
|
||||
|
||||
void plParticleCollisionEffect::PrepareEffect(const plEffectTargetInfo &target)
|
||||
{
|
||||
if (fBounds == nil)
|
||||
{
|
||||
plBoundInterface *bi = plBoundInterface::ConvertNoRef(fSceneObj->GetGenericInterface(plBoundInterface::Index()));
|
||||
if (bi == nil)
|
||||
return;
|
||||
fBounds = bi->GetVolume();
|
||||
}
|
||||
}
|
||||
|
||||
hsBool plParticleCollisionEffect::MsgReceive(plMessage* msg)
|
||||
{
|
||||
plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg);
|
||||
plSceneObject *so;
|
||||
if (refMsg)
|
||||
{
|
||||
if (so = plSceneObject::ConvertNoRef(refMsg->GetRef()))
|
||||
{
|
||||
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
|
||||
fSceneObj = so;
|
||||
else
|
||||
fSceneObj = nil;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return hsKeyedObject::MsgReceive(msg);
|
||||
}
|
||||
|
||||
void plParticleCollisionEffect::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
hsKeyedObject::Read(s, mgr);
|
||||
|
||||
plGenRefMsg* msg;
|
||||
msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0); // SceneObject
|
||||
mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
|
||||
fBounds = nil;
|
||||
}
|
||||
|
||||
void plParticleCollisionEffect::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
hsKeyedObject::Write(s, mgr);
|
||||
|
||||
mgr->WriteKey(s, fSceneObj);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Some permutations on the CollisionEffect follow
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plParticleCollisionEffectBeat::plParticleCollisionEffectBeat()
|
||||
{
|
||||
}
|
||||
|
||||
hsBool plParticleCollisionEffectBeat::ApplyEffect(const plEffectTargetInfo &target, Int32 i)
|
||||
{
|
||||
hsAssert(i >= 0, "Use of default argument doesn't make sense for plParticleCollisionEffect");
|
||||
|
||||
if( !fBounds )
|
||||
return false;
|
||||
|
||||
hsPoint3 *currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride);
|
||||
fBounds->ResolvePoint(*currPos);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plParticleCollisionEffectDie::plParticleCollisionEffectDie()
|
||||
{
|
||||
}
|
||||
|
||||
hsBool plParticleCollisionEffectDie::ApplyEffect(const plEffectTargetInfo &target, Int32 i)
|
||||
{
|
||||
hsAssert(i >= 0, "Use of default argument doesn't make sense for plParticleCollisionEffect");
|
||||
|
||||
if( !fBounds )
|
||||
return false;
|
||||
|
||||
hsPoint3 *currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride);
|
||||
return fBounds->IsInside(*currPos);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plParticleCollisionEffectBounce::plParticleCollisionEffectBounce()
|
||||
: fBounce(1.f),
|
||||
fFriction(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
hsBool plParticleCollisionEffectBounce::ApplyEffect(const plEffectTargetInfo &target, Int32 i)
|
||||
{
|
||||
hsAssert(i >= 0, "Use of default argument doesn't make sense for plParticleCollisionEffect");
|
||||
|
||||
if( !fBounds )
|
||||
return false;
|
||||
|
||||
hsPoint3* currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride);
|
||||
hsVector3* currVel = (hsVector3*)(target.fVelocity + i * target.fVelocityStride);
|
||||
fBounds->BouncePoint(*currPos, *currVel, fBounce, fFriction);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void plParticleCollisionEffectBounce::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleCollisionEffect::Read(s, mgr);
|
||||
|
||||
fBounce = s->ReadSwapScalar();
|
||||
fFriction = s->ReadSwapScalar();
|
||||
}
|
||||
|
||||
void plParticleCollisionEffectBounce::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleCollisionEffect::Write(s, mgr);
|
||||
|
||||
s->WriteSwapScalar(fBounce);
|
||||
s->WriteSwapScalar(fFriction);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plParticleFadeVolumeEffect::plParticleFadeVolumeEffect() : fLength(100.0f), fIgnoreZ(true)
|
||||
{
|
||||
}
|
||||
|
||||
plParticleFadeVolumeEffect::~plParticleFadeVolumeEffect()
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// It's not really clear looking at the math here what's actually going on,
|
||||
// but once you visualize it, it's pretty easy to follow. So the camera position,
|
||||
// view direction, and length of the fade volume define a sphere, where the camera
|
||||
// position is a point on the sphere, the view direction points from that surface
|
||||
// point toward the center, and the length is the sphere's radius. Since the view
|
||||
// direction points straight through the sphere, that sphere is the sweet zone for
|
||||
// putting our particles to pile them up in front of the camera. But we'd like to
|
||||
// do this independently for each axis (for efficiency, rather than true 3D calculations),
|
||||
// so we put an axis aligned box around the sphere, and that's the volume we restrict
|
||||
// our particles to.
|
||||
// Now we could fade all around the box, but that's not really what we want, because
|
||||
// that means fading particles that are behind us. And in the case where we're looking
|
||||
// along an axis, the camera is pushed up against a face of the box (where the axis
|
||||
// aligned box is tangent to the inscribed sphere), so we'd actually be fading
|
||||
// particles just in front of the camera. Because of this non-symmetry, we're going to
|
||||
// define the Max in a given dimension as the world space value for that dimension
|
||||
// FARTHEST from the camera (NOT largest in value). So if the camera is looking
|
||||
// in a negative direction in one dimension, the Max will be less than the Min for
|
||||
// that dimension.
|
||||
// So we set up our Max's and Min's for each dimension in PrepareEffect(), and then
|
||||
// at runtime we calculate the parameter value of the particle ranging from 0 where
|
||||
// particleLoc == Min to 1 where particleLoc == Max. If the parameter is outside
|
||||
// [0..1], then we can move it into the box using the fractional part of the parameter.
|
||||
// Finally, if the (possibly relocated) parameter value says the particle is approaching
|
||||
// the Max value, we can calclulate its faded opacity from the parameter.
|
||||
//
|
||||
|
||||
// Need to experiment to minimize this fade distance. The greater
|
||||
// the fade distance, the more faded out (wasted) particles we're drawing.
|
||||
// The shorter the distance, the more noticable the fade out.
|
||||
// Note the wierdness between the fractions, because kFadeFrac is fraction
|
||||
// of fLength, but kFadeParm and kInvFadeFrac are fraction of 2.f*fLength. Sorry.
|
||||
const hsScalar kFadeFrac = 0.5f;
|
||||
const hsScalar kFadeParm = 1.f - kFadeFrac * 0.5f;
|
||||
const hsScalar kInvFadeFrac = 1.f / (kFadeFrac * 0.5f);
|
||||
|
||||
void plParticleFadeVolumeEffect::PrepareEffect(const plEffectTargetInfo &target)
|
||||
{
|
||||
hsPoint3 viewLoc = target.fContext.fPipeline->GetViewPositionWorld();
|
||||
hsVector3 viewDir = target.fContext.fPipeline->GetViewDirWorld();
|
||||
|
||||
// Nuking out the setting of viewDir.fZ to 0 when we aren't centering
|
||||
// about Z (fIgnoreZ == true), because we still want to center our
|
||||
// volume about the camera (rather than push the camera to the edge of
|
||||
// the cylinder) in that case, so we don't get artifacts when we look
|
||||
// straight up or down. mf
|
||||
|
||||
hsPoint3 signs(viewDir.fX >= 0 ? 1.f : -1.f, viewDir.fY >= 0 ? 1.f : -1.f, viewDir.fZ >= 0 ? 1.f : -1.f);
|
||||
|
||||
fMax.fX = viewLoc.fX + (viewDir.fX + signs.fX) * fLength;
|
||||
fMin.fX = fMax.fX - 2.f * signs.fX * fLength;
|
||||
|
||||
fMax.fY = viewLoc.fY + (viewDir.fY + signs.fY) * fLength;
|
||||
fMin.fY = fMax.fY - 2.f * signs.fY * fLength;
|
||||
|
||||
fMax.fZ = viewLoc.fZ + (viewDir.fZ + signs.fZ) * fLength;
|
||||
fMin.fZ = fMax.fZ - 2.f * signs.fZ * fLength;
|
||||
|
||||
fNorm.fX = 1.f / (fMax.fX - fMin.fX);
|
||||
fNorm.fY = 1.f / (fMax.fY - fMin.fY);
|
||||
fNorm.fZ = 1.f / (fMax.fZ - fMin.fZ);
|
||||
}
|
||||
|
||||
hsBool plParticleFadeVolumeEffect::ApplyEffect(const plEffectTargetInfo& target, Int32 i)
|
||||
{
|
||||
hsPoint3 *currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride);
|
||||
|
||||
hsScalar parm;
|
||||
|
||||
hsScalar fade = 1.f;
|
||||
|
||||
parm = (currPos->fX - fMin.fX) * fNorm.fX;
|
||||
if( parm < 0 )
|
||||
{
|
||||
parm -= int(parm);
|
||||
currPos->fX = fMax.fX + parm * (fMax.fX - fMin.fX);
|
||||
parm += 1.f;
|
||||
}
|
||||
else if( parm > 1.f )
|
||||
{
|
||||
parm -= int(parm);
|
||||
currPos->fX = fMin.fX + parm * (fMax.fX - fMin.fX);
|
||||
}
|
||||
if( parm > kFadeParm )
|
||||
{
|
||||
parm = 1.f - parm;
|
||||
parm *= kInvFadeFrac;
|
||||
if( parm < fade )
|
||||
fade = parm;
|
||||
}
|
||||
|
||||
parm = (currPos->fY - fMin.fY) * fNorm.fY;
|
||||
if( parm < 0 )
|
||||
{
|
||||
parm -= int(parm);
|
||||
currPos->fY = fMax.fY + parm * (fMax.fY - fMin.fY);
|
||||
parm += 1.f;
|
||||
}
|
||||
else if( parm > 1.f )
|
||||
{
|
||||
parm -= int(parm);
|
||||
currPos->fY = fMin.fY + parm * (fMax.fY - fMin.fY);
|
||||
}
|
||||
if( parm > kFadeParm )
|
||||
{
|
||||
parm = 1.f - parm;
|
||||
parm *= kInvFadeFrac;
|
||||
if( parm < fade )
|
||||
fade = parm;
|
||||
}
|
||||
|
||||
if( !fIgnoreZ )
|
||||
{
|
||||
parm = (currPos->fZ - fMin.fZ) * fNorm.fZ;
|
||||
if( parm < 0 )
|
||||
{
|
||||
parm -= int(parm);
|
||||
currPos->fZ = fMax.fZ + parm * (fMax.fZ - fMin.fZ);
|
||||
parm += 1.f;
|
||||
}
|
||||
else if( parm > 1.f )
|
||||
{
|
||||
parm -= int(parm);
|
||||
currPos->fZ = fMin.fZ + parm * (fMax.fZ - fMin.fZ);
|
||||
}
|
||||
if( parm > kFadeParm )
|
||||
{
|
||||
parm = 1.f - parm;
|
||||
parm *= kInvFadeFrac;
|
||||
if( parm < fade )
|
||||
fade = parm;
|
||||
}
|
||||
}
|
||||
|
||||
if( fade < 1.f )
|
||||
{
|
||||
UInt32 *color = (UInt32 *)(target.fColor + i * target.fColorStride);
|
||||
UInt32 alpha = (UInt32)((*color >> 24) * fade);
|
||||
*color = (*color & 0x00ffffff) | (alpha << 24);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void plParticleFadeVolumeEffect::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
hsKeyedObject::Read(s, mgr);
|
||||
|
||||
fLength = s->ReadSwapScalar();
|
||||
fIgnoreZ = s->ReadBool();
|
||||
}
|
||||
|
||||
void plParticleFadeVolumeEffect::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
hsKeyedObject::Write(s, mgr);
|
||||
|
||||
s->WriteSwapScalar(fLength);
|
||||
s->WriteBool(fIgnoreZ);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Particle wind - Base class first
|
||||
plParticleWindEffect::plParticleWindEffect()
|
||||
: fWindVec(0,0,0),
|
||||
fDir(1.f,0,0),
|
||||
fSwirl(0.1f),
|
||||
fConstancy(0),
|
||||
fHorizontal(0),
|
||||
fLastDirSecs(-1.f),
|
||||
fRefDir(0.f,0.f,0.f),
|
||||
fRandDir(1.f,0.f,0.f)
|
||||
{
|
||||
}
|
||||
|
||||
plParticleWindEffect::~plParticleWindEffect()
|
||||
{
|
||||
}
|
||||
|
||||
void plParticleWindEffect::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
hsKeyedObject::Read(s, mgr);
|
||||
|
||||
fStrength = s->ReadSwapScalar();
|
||||
fConstancy = s->ReadSwapScalar();
|
||||
fSwirl = s->ReadSwapScalar();
|
||||
fHorizontal = s->ReadBool();
|
||||
fRefDir.Read(s);
|
||||
fDir.Read(s);
|
||||
fRandDir = fDir;
|
||||
}
|
||||
|
||||
void plParticleWindEffect::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
hsKeyedObject::Write(s, mgr);
|
||||
|
||||
s->WriteSwapScalar(fStrength);
|
||||
s->WriteSwapScalar(fConstancy);
|
||||
s->WriteSwapScalar(fSwirl);
|
||||
s->WriteBool(fHorizontal);
|
||||
fRefDir.Write(s);
|
||||
fDir.Write(s);
|
||||
}
|
||||
|
||||
void plParticleWindEffect::SetRefDirection(const hsVector3& v)
|
||||
{
|
||||
fRefDir = v;
|
||||
hsScalar lenSq = fRefDir.MagnitudeSquared();
|
||||
if( lenSq > 1.e-1f )
|
||||
{
|
||||
fDir = fRefDir * hsFastMath::InvSqrtAppr(lenSq);
|
||||
fRandDir = fDir;
|
||||
}
|
||||
}
|
||||
|
||||
void plParticleWindEffect::PrepareEffect(const plEffectTargetInfo& target)
|
||||
{
|
||||
if( fLastDirSecs != target.fContext.fSecs )
|
||||
{
|
||||
static plRandom random;
|
||||
fRandDir.fX += random.RandMinusOneToOne() * fSwirl;
|
||||
fRandDir.fY += random.RandMinusOneToOne() * fSwirl;
|
||||
if( !GetHorizontal() )
|
||||
fRandDir.fZ += random.RandMinusOneToOne() * fSwirl;
|
||||
hsFastMath::NormalizeAppr(fRandDir);
|
||||
fDir = fRandDir + fRefDir;
|
||||
hsFastMath::NormalizeAppr(fDir);
|
||||
|
||||
fWindVec = fDir * (fStrength * target.fContext.fSystem->GetWindMult() * target.fContext.fDelSecs);
|
||||
|
||||
fLastDirSecs = target.fContext.fSecs;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Localized wind (how much wind you're getting depends on where you are
|
||||
plParticleLocalWind::plParticleLocalWind()
|
||||
: fScale(0, 0, 0),
|
||||
fSpeed(0),
|
||||
fPhase(0,0,0),
|
||||
fInvScale(0,0,0),
|
||||
fLastPhaseSecs(-1.f)
|
||||
{
|
||||
}
|
||||
|
||||
plParticleLocalWind::~plParticleLocalWind()
|
||||
{
|
||||
}
|
||||
|
||||
void plParticleLocalWind::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleWindEffect::Read(s, mgr);
|
||||
|
||||
fScale.Read(s);
|
||||
fSpeed = s->ReadSwapScalar();
|
||||
}
|
||||
|
||||
void plParticleLocalWind::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleWindEffect::Write(s, mgr);
|
||||
|
||||
fScale.Write(s);
|
||||
s->WriteSwapScalar(fSpeed);
|
||||
}
|
||||
|
||||
void plParticleLocalWind::PrepareEffect(const plEffectTargetInfo& target)
|
||||
{
|
||||
if( fLastPhaseSecs != target.fContext.fSecs )
|
||||
{
|
||||
plParticleWindEffect::PrepareEffect(target);
|
||||
|
||||
fPhase += fDir * fSpeed * target.fContext.fDelSecs;
|
||||
|
||||
fInvScale.fX = fScale.fX > 0 ? 1.f / fScale.fX : 0;
|
||||
fInvScale.fY = fScale.fY > 0 ? 1.f / fScale.fY : 0;
|
||||
fInvScale.fZ = fScale.fZ > 0 ? 1.f / fScale.fZ : 0;
|
||||
|
||||
fLastPhaseSecs = target.fContext.fSecs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hsBool plParticleLocalWind::ApplyEffect(const plEffectTargetInfo& target, Int32 i)
|
||||
{
|
||||
const hsPoint3& pos = *(hsPoint3 *)(target.fPos + i * target.fPosStride);
|
||||
hsVector3& vel = *(hsVector3*)(target.fVelocity + i * target.fVelocityStride);
|
||||
|
||||
const hsScalar kMinToBother = 0;
|
||||
|
||||
float strength = 1.f / ( (1.f + fConstancy) * (1.f + fConstancy) );
|
||||
float s, c, t;
|
||||
t = (pos[0] - fPhase[0]) * fInvScale[0];
|
||||
hsFastMath::SinCosAppr(t, s, c);
|
||||
c += fConstancy;
|
||||
if( c <= kMinToBother )
|
||||
return false;
|
||||
strength *= c;
|
||||
|
||||
t = (pos[1] - fPhase[1]) * fInvScale[1];
|
||||
hsFastMath::SinCosAppr(t, s, c);
|
||||
c += fConstancy;
|
||||
if( c <= kMinToBother )
|
||||
return false;
|
||||
strength *= c;
|
||||
|
||||
#if 0 // if you turn this back on, strength needs to drop by another factor of (1.f + fConstancy)
|
||||
t = (pos[2] - fPhase[2]) * fInvScale[2];
|
||||
hsFastMath::SinCosAppr(t, s, c);
|
||||
c += fConstancy;
|
||||
if( c <= kMinToBother )
|
||||
return false;
|
||||
strength *= c;
|
||||
#endif
|
||||
|
||||
const hsScalar& invMass = *(hsScalar*)(target.fInvMass + i * target.fInvMassStride);
|
||||
strength *= invMass;
|
||||
|
||||
vel += fWindVec * strength;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Uniform wind - wind changes over time, but not space
|
||||
plParticleUniformWind::plParticleUniformWind()
|
||||
: fFreqMin(0.1f),
|
||||
fFreqMax(0.2f),
|
||||
fFreqCurr(0.1f),
|
||||
fFreqRate(0.05f),
|
||||
|
||||
fCurrPhase(0),
|
||||
fLastFreqSecs(-1.f),
|
||||
fCurrentStrength(0)
|
||||
{
|
||||
}
|
||||
|
||||
plParticleUniformWind::~plParticleUniformWind()
|
||||
{
|
||||
}
|
||||
|
||||
void plParticleUniformWind::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleWindEffect::Read(s, mgr);
|
||||
|
||||
fFreqMin = s->ReadSwapScalar();
|
||||
fFreqMax = s->ReadSwapScalar();
|
||||
fFreqRate = s->ReadSwapScalar();
|
||||
|
||||
#if 0
|
||||
fFreqMin = 1.f / 6.f;
|
||||
fFreqMax = 1.f / 1.f;
|
||||
fConstancy = -0.5f;
|
||||
fSwirl = 0.05f;
|
||||
#endif
|
||||
|
||||
fFreqCurr = fFreqMin;
|
||||
}
|
||||
|
||||
void plParticleUniformWind::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleWindEffect::Write(s, mgr);
|
||||
|
||||
s->WriteSwapScalar(fFreqMin);
|
||||
s->WriteSwapScalar(fFreqMax);
|
||||
s->WriteSwapScalar(fFreqRate);
|
||||
}
|
||||
|
||||
void plParticleUniformWind::SetFrequencyRange(hsScalar minSecsPerCycle, hsScalar maxSecsPerCycle)
|
||||
{
|
||||
const hsScalar kMinSecsPerCycle = 1.f;
|
||||
if( minSecsPerCycle < kMinSecsPerCycle )
|
||||
minSecsPerCycle = kMinSecsPerCycle;
|
||||
if( minSecsPerCycle < kMinSecsPerCycle )
|
||||
minSecsPerCycle = kMinSecsPerCycle;
|
||||
|
||||
if( minSecsPerCycle < maxSecsPerCycle )
|
||||
{
|
||||
fFreqMin = 1.f / maxSecsPerCycle;
|
||||
fFreqMax = 1.f / minSecsPerCycle;
|
||||
}
|
||||
else
|
||||
{
|
||||
fFreqMin = 1.f / minSecsPerCycle;
|
||||
fFreqMax = 1.f / maxSecsPerCycle;
|
||||
}
|
||||
}
|
||||
|
||||
void plParticleUniformWind::SetFrequencyRate(hsScalar secsPerCycle)
|
||||
{
|
||||
const hsScalar kMinSecsPerCycle = 1.f;
|
||||
if( secsPerCycle < kMinSecsPerCycle )
|
||||
secsPerCycle = kMinSecsPerCycle;
|
||||
fFreqRate = 1.f / secsPerCycle;
|
||||
}
|
||||
|
||||
|
||||
void plParticleUniformWind::PrepareEffect(const plEffectTargetInfo& target)
|
||||
{
|
||||
plParticleWindEffect::PrepareEffect(target);
|
||||
|
||||
if( fLastFreqSecs != target.fContext.fSecs )
|
||||
{
|
||||
static plRandom random;
|
||||
|
||||
const double kTwoPi = hsScalarPI * 2.0;
|
||||
double t0 = fFreqCurr * fLastFreqSecs + fCurrPhase;
|
||||
hsScalar t1 = (hsScalar)fmod(t0, kTwoPi);
|
||||
fCurrPhase -= t0 - t1;
|
||||
|
||||
fFreqCurr += fFreqRate * target.fContext.fDelSecs * random.RandZeroToOne();
|
||||
|
||||
if( fFreqCurr > fFreqMax )
|
||||
{
|
||||
fFreqCurr = fFreqMax;
|
||||
fFreqRate = -fFreqRate;
|
||||
}
|
||||
else if( fFreqCurr < fFreqMin )
|
||||
{
|
||||
fFreqCurr = fFreqMin;
|
||||
fFreqRate = -fFreqRate;
|
||||
}
|
||||
|
||||
hsScalar phaseDel = (hsScalar)(t1 - (fFreqCurr * fLastFreqSecs + fCurrPhase));
|
||||
fCurrPhase += phaseDel;
|
||||
|
||||
hsScalar t = hsScalar(fFreqCurr * target.fContext.fSecs + fCurrPhase);
|
||||
hsScalar s;
|
||||
hsFastMath::SinCosAppr(t, s, fCurrentStrength);
|
||||
fCurrentStrength += fConstancy;
|
||||
fCurrentStrength /= (1.f + fConstancy);
|
||||
|
||||
if( fCurrentStrength < 0 )
|
||||
fCurrentStrength = 0;
|
||||
|
||||
fLastFreqSecs = target.fContext.fSecs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hsBool plParticleUniformWind::ApplyEffect(const plEffectTargetInfo& target, Int32 i)
|
||||
{
|
||||
hsVector3& vel = *(hsVector3*)(target.fVelocity + i * target.fVelocityStride);
|
||||
|
||||
const hsScalar& invMass = *(hsScalar*)(target.fInvMass + i * target.fInvMassStride);
|
||||
|
||||
vel += fWindVec * (invMass * fCurrentStrength);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Simplified flocking.
|
||||
|
||||
plParticleFlockEffect::plParticleFlockEffect() :
|
||||
fInfAvgRadSq(1),
|
||||
fInfRepRadSq(1),
|
||||
fAvgVelStr(1),
|
||||
fRepDirStr(1),
|
||||
fGoalOrbitStr(1),
|
||||
fGoalChaseStr(1),
|
||||
fGoalDistSq(1),
|
||||
fFullChaseDistSq(1),
|
||||
fMaxOrbitSpeed(1),
|
||||
fMaxChaseSpeed(1),
|
||||
fMaxParticles(0),
|
||||
fDistSq(nil),
|
||||
fInfluences(nil)
|
||||
{
|
||||
fTargetOffset.Set(0.f, 0.f, 0.f);
|
||||
fDissenterTarget.Set(0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
plParticleFlockEffect::~plParticleFlockEffect()
|
||||
{
|
||||
SetMaxParticles(0);
|
||||
}
|
||||
|
||||
void plParticleFlockEffect::IUpdateDistances(const plEffectTargetInfo& target)
|
||||
{
|
||||
int i, j;
|
||||
int numParticles = hsMinimum(fMaxParticles, target.fNumValidParticles);
|
||||
|
||||
for (i = 0; i < numParticles; i++)
|
||||
{
|
||||
for (j = i + 1; j < numParticles; j++)
|
||||
{
|
||||
hsVector3 diff((hsPoint3*)(target.fPos + i * target.fPosStride), (hsPoint3*)(target.fPos + j * target.fPosStride));
|
||||
fDistSq[i * fMaxParticles + j] = fDistSq[j * fMaxParticles + i] = diff.MagnitudeSquared();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void plParticleFlockEffect::IUpdateInfluences(const plEffectTargetInfo &target)
|
||||
{
|
||||
int i, j;
|
||||
int numParticles = hsMinimum(fMaxParticles, target.fNumValidParticles);
|
||||
|
||||
for (i = 0; i < numParticles; i++)
|
||||
{
|
||||
int numAvg = 0;
|
||||
int numRep = 0;
|
||||
fInfluences[i].fAvgVel.Set(0.f, 0.f, 0.f);
|
||||
fInfluences[i].fRepDir.Set(0.f, 0.f, 0.f);
|
||||
|
||||
for (j = 0; j < numParticles; j++)
|
||||
{
|
||||
if (i == j)
|
||||
continue;
|
||||
|
||||
const int distIdx = i * fMaxParticles + j;
|
||||
if (fDistSq[distIdx] > fInfAvgRadSq)
|
||||
{
|
||||
numAvg++;
|
||||
fInfluences[i].fAvgVel += *(hsVector3*)(target.fVelocity + j * target.fVelocityStride);
|
||||
}
|
||||
|
||||
if (fDistSq[distIdx] > fInfRepRadSq)
|
||||
{
|
||||
numRep++;
|
||||
hsVector3 repDir((hsPoint3*)(target.fPos + i * target.fPosStride), (hsPoint3*)(target.fPos + j * target.fPosStride));
|
||||
repDir.Normalize();
|
||||
fInfluences[i].fRepDir += repDir;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (numAvg > 0)
|
||||
fInfluences[i].fAvgVel /= (hsScalar)numAvg;
|
||||
if (numRep > 0)
|
||||
fInfluences[i].fRepDir /= (hsScalar)numRep;
|
||||
}
|
||||
}
|
||||
|
||||
void plParticleFlockEffect::PrepareEffect(const plEffectTargetInfo& target)
|
||||
{
|
||||
IUpdateDistances(target);
|
||||
IUpdateInfluences(target);
|
||||
}
|
||||
|
||||
// Some of this is the same for every particle and should be cached in PrepareEffect().
|
||||
// Holding off on that until I like the behavior.
|
||||
hsBool plParticleFlockEffect::ApplyEffect(const plEffectTargetInfo& target, Int32 i)
|
||||
{
|
||||
if (i >= fMaxParticles)
|
||||
return false; // Don't have the memory to deal with you. Good luck kid...
|
||||
|
||||
const hsPoint3 &pos = *(hsPoint3*)(target.fPos + i * target.fPosStride);
|
||||
hsVector3 &vel = *(hsVector3*)(target.fVelocity + i * target.fVelocityStride);
|
||||
|
||||
hsScalar curSpeed = vel.Magnitude();
|
||||
hsPoint3 goal;
|
||||
if (*(UInt32*)(target.fMiscFlags + i * target.fMiscFlagsStride) & plParticleExt::kImmortal)
|
||||
goal = target.fContext.fSystem->GetTarget(0)->GetLocalToWorld().GetTranslate() + fTargetOffset;
|
||||
else
|
||||
goal = fDissenterTarget;
|
||||
|
||||
hsVector3 goalDir;
|
||||
goalDir.Set(&(goal - pos));
|
||||
hsScalar distSq = goalDir.MagnitudeSquared();
|
||||
|
||||
goalDir.Normalize();
|
||||
|
||||
hsScalar goalStr;
|
||||
hsScalar maxSpeed;
|
||||
hsScalar maxSpeedSq;
|
||||
if (distSq <= fGoalDistSq)
|
||||
{
|
||||
goalStr = fGoalOrbitStr;
|
||||
if (i & 0x1)
|
||||
goalDir.Set(goalDir.fY, -goalDir.fX, goalDir.fZ);
|
||||
else
|
||||
goalDir.Set(-goalDir.fY, goalDir.fX, goalDir.fZ);
|
||||
|
||||
maxSpeed = fMaxOrbitSpeed;
|
||||
}
|
||||
else if (distSq >= fFullChaseDistSq)
|
||||
{
|
||||
goalStr = fGoalChaseStr;
|
||||
maxSpeed = fMaxChaseSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
hsScalar pct = (distSq - fGoalDistSq) / (fFullChaseDistSq - fGoalDistSq);
|
||||
goalStr = fGoalOrbitStr + (fGoalChaseStr - fGoalOrbitStr) * pct;
|
||||
maxSpeed = fMaxOrbitSpeed + (fMaxChaseSpeed - fMaxOrbitSpeed) * pct;
|
||||
}
|
||||
maxSpeedSq = maxSpeed * maxSpeed;
|
||||
|
||||
vel += (fInfluences[i].fAvgVel - vel) * (fAvgVelStr * target.fContext.fDelSecs);
|
||||
vel += goalDir * (curSpeed * goalStr * target.fContext.fDelSecs);
|
||||
vel += fInfluences[i].fRepDir * (curSpeed * fRepDirStr * target.fContext.fDelSecs);
|
||||
|
||||
if (vel.MagnitudeSquared() > maxSpeedSq)
|
||||
{
|
||||
vel.Normalize();
|
||||
vel *= maxSpeed;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void plParticleFlockEffect::SetMaxParticles(const UInt16 num)
|
||||
{
|
||||
delete [] fDistSq;
|
||||
delete [] fInfluences;
|
||||
fMaxParticles = num;
|
||||
|
||||
if (num > 0)
|
||||
{
|
||||
fDistSq = TRACKED_NEW hsScalar[num * num];
|
||||
fInfluences = TRACKED_NEW plParticleInfluenceInfo[num];
|
||||
}
|
||||
}
|
||||
|
||||
void plParticleFlockEffect::Read(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleEffect::Read(s, mgr);
|
||||
|
||||
fTargetOffset.Read(s);
|
||||
fDissenterTarget.Read(s);
|
||||
fInfAvgRadSq = s->ReadSwapScalar();
|
||||
fInfRepRadSq = s->ReadSwapScalar();
|
||||
fGoalDistSq = s->ReadSwapScalar();
|
||||
fFullChaseDistSq = s->ReadSwapScalar();
|
||||
fAvgVelStr = s->ReadSwapScalar();
|
||||
fRepDirStr = s->ReadSwapScalar();
|
||||
fGoalOrbitStr = s->ReadSwapScalar();
|
||||
fGoalChaseStr = s->ReadSwapScalar();
|
||||
SetMaxOrbitSpeed(s->ReadSwapScalar());
|
||||
SetMaxChaseSpeed(s->ReadSwapScalar());
|
||||
SetMaxParticles((UInt16)s->ReadSwapScalar());
|
||||
}
|
||||
|
||||
void plParticleFlockEffect::Write(hsStream *s, hsResMgr *mgr)
|
||||
{
|
||||
plParticleEffect::Write(s, mgr);
|
||||
|
||||
fTargetOffset.Write(s);
|
||||
fDissenterTarget.Write(s);
|
||||
s->WriteSwapScalar(fInfAvgRadSq);
|
||||
s->WriteSwapScalar(fInfRepRadSq);
|
||||
s->WriteSwapScalar(fGoalDistSq);
|
||||
s->WriteSwapScalar(fFullChaseDistSq);
|
||||
s->WriteSwapScalar(fAvgVelStr);
|
||||
s->WriteSwapScalar(fRepDirStr);
|
||||
s->WriteSwapScalar(fGoalOrbitStr);
|
||||
s->WriteSwapScalar(fGoalChaseStr);
|
||||
s->WriteSwapScalar(fMaxOrbitSpeed);
|
||||
s->WriteSwapScalar(fMaxChaseSpeed);
|
||||
s->WriteSwapScalar(fMaxParticles);
|
||||
}
|
||||
|
||||
hsBool plParticleFlockEffect::MsgReceive(plMessage *msg)
|
||||
{
|
||||
plParticleFlockMsg *flockMsg = plParticleFlockMsg::ConvertNoRef(msg);
|
||||
if (flockMsg)
|
||||
{
|
||||
switch (flockMsg->fCmd)
|
||||
{
|
||||
case plParticleFlockMsg::kFlockCmdSetDissentPoint:
|
||||
fDissenterTarget.Set(flockMsg->fX, flockMsg->fY, flockMsg->fZ);
|
||||
break;
|
||||
case plParticleFlockMsg::kFlockCmdSetOffset:
|
||||
fTargetOffset.Set(flockMsg->fX, flockMsg->fY, flockMsg->fZ);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return plParticleEffect::MsgReceive(msg);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plParticleFollowSystemEffect::plParticleFollowSystemEffect() : fEvalThisFrame(true)
|
||||
{
|
||||
fOldW2L = hsMatrix44::IdentityMatrix();
|
||||
}
|
||||
|
||||
void plParticleFollowSystemEffect::PrepareEffect(const plEffectTargetInfo& target)
|
||||
{
|
||||
fEvalThisFrame = (fOldW2L != target.fContext.fSystem->GetTarget(0)->GetWorldToLocal());
|
||||
}
|
||||
|
||||
hsBool plParticleFollowSystemEffect::ApplyEffect(const plEffectTargetInfo& target, Int32 i)
|
||||
{
|
||||
if (fEvalThisFrame)
|
||||
{
|
||||
if (i < target.fFirstNewParticle && !fOldW2L.IsIdentity())
|
||||
{
|
||||
hsPoint3 &pos = *(hsPoint3*)(target.fPos + i * target.fPosStride);
|
||||
pos = target.fContext.fSystem->GetTarget(0)->GetLocalToWorld() * fOldW2L * pos;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void plParticleFollowSystemEffect::EndEffect(const plEffectTargetInfo& target)
|
||||
{
|
||||
if (fEvalThisFrame)
|
||||
fOldW2L = target.fContext.fSystem->GetTarget(0)->GetWorldToLocal();
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user