2
3
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-14 02:27:40 -04:00

CWE Directory Reorganization

Rearrange directory structure of CWE to be loosely equivalent to
the H'uru Plasma repository.

Part 1: Movement of directories and files.
This commit is contained in:
rarified
2021-05-15 12:49:46 -06:00
parent c3f4a640a3
commit 96903e8dca
4002 changed files with 159 additions and 644 deletions

View File

@ -0,0 +1,92 @@
/*==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 "hsGeometry3.h"
#include "plBoundInterface.h"
#include "plConvexVolume.h"
#include "hsResMgr.h"
plBoundInterface::plBoundInterface() : fBounds(nil)
{
}
plBoundInterface::~plBoundInterface()
{
ReleaseData();
}
void plBoundInterface::ReleaseData()
{
delete fBounds;
fBounds = nil;
}
void plBoundInterface::Init(plConvexVolume *bounds)
{
ReleaseData();
fBounds = bounds;
}
// Right now, this is ignoring the enabled property of ObjInterface, since I'm not aware that
// anything ever makes use of it (and if nothing does, this saves us on some needless matrix
// copying). Should we make use of the disabled prop, this function should just store the l2w
// matrix, but not send an update to fBounds.
void plBoundInterface::SetTransform(const hsMatrix44 &l2w, const hsMatrix44&w2l)
{
if (fBounds != nil)
fBounds->Update(l2w);
}
void plBoundInterface::Read(hsStream* s, hsResMgr* mgr)
{
plObjInterface::Read(s, mgr);
fBounds = plConvexVolume::ConvertNoRef(mgr->ReadCreatable(s));
//mgr->ReadKeyNotifyMe(s, new plIntRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kOwner), plRefFlags::kPassiveRef);
}
void plBoundInterface::Write(hsStream* s, hsResMgr* mgr)
{
plObjInterface::Write(s, mgr);
mgr->WriteCreatable(s, fBounds);
//mgr->WriteKey(s, fBounds);
}
// No need to save/load. The coordinate interface on our sceneObject will update us.

View File

@ -0,0 +1,83 @@
/*==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==*/
#ifndef plBoundInterface_inc
#define plBoundInterface_inc
#include "../pnSceneObject/plObjInterface.h"
class plConvexVolume;
struct hsMatrix44;
class plBoundInterface : public plObjInterface
{
enum {
kDisable = 0x0,
kNumProps // last
};
protected:
//hsMatrix44 fLocalToWorld;
plConvexVolume *fBounds;
public:
plBoundInterface();
~plBoundInterface();
void Init(plConvexVolume *bounds);
plConvexVolume *GetVolume() { return fBounds; }
CLASSNAME_REGISTER( plBoundInterface );
GETINTERFACE_ANY( plBoundInterface, plObjInterface );
virtual Int32 GetNumProperties() const { return kNumProps; }
virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l);
virtual void Read(hsStream* stream, hsResMgr* mgr);
virtual void Write(hsStream* stream, hsResMgr* mgr);
virtual void ReleaseData();
};
#endif // plBoundInterface_inc

View File

@ -0,0 +1,209 @@
/*==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 "hsMatrix44.h"
#include "plConvexVolume.h"
#include "hsStream.h"
plConvexVolume::plConvexVolume()
{
//fFlags = nil;
fLocalPlanes = nil;
fWorldPlanes = nil;
fNumPlanes = 0;
}
plConvexVolume::~plConvexVolume()
{
IClear();
}
void plConvexVolume::IClear()
{
//delete [] fFlags;
delete [] fLocalPlanes;
delete [] fWorldPlanes;
}
hsBool plConvexVolume::AddPlane(const hsPlane3 &plane)
{
// First check for a redundant plane (since we're convex, a comparison of normals should do)
int i;
// Start the comparison with the most recently added plane, it's most likely to match
for (i = fNumPlanes - 1; i >= 0; i--)
{
const float MIN_COS_THETA = 0.99999f; // translates to < 0.25 degree angle
// If the angle betwen the normals is close enough, count them as equal.
if (fLocalPlanes[i].fN.InnerProduct(plane.fN) >= MIN_COS_THETA)
return false; // no need to add it
}
fNumPlanes++;
//delete [] fFlags;
//fFlags = TRACKED_NEW UInt32[fNumPlanes];
hsPlane3 *tempPlanes = TRACKED_NEW hsPlane3[fNumPlanes];
for (i = 0; i < fNumPlanes - 1; i++)
{
tempPlanes[i] = fLocalPlanes[i];
}
tempPlanes[fNumPlanes - 1] = plane;
delete [] fLocalPlanes;
fLocalPlanes = tempPlanes;
delete [] fWorldPlanes;
fWorldPlanes = TRACKED_NEW hsPlane3[fNumPlanes];
return true;
}
void plConvexVolume::Update(const hsMatrix44 &l2w)
{
int i;
hsPoint3 planePt;
for (i = 0; i < fNumPlanes; i++)
{
// Since fN is an hsVector3, it will only apply the rotational aspect of the transform...
fWorldPlanes[i].fN = l2w * fLocalPlanes[i].fN;
planePt.Set(&(fLocalPlanes[i].fN * fLocalPlanes[i].fD));
fWorldPlanes[i].fD = -(l2w * planePt).InnerProduct(fWorldPlanes[i].fN);
}
}
void plConvexVolume::SetNumPlanesAndClear(const UInt32 num)
{
IClear();
//fFlags = TRACKED_NEW UInt32[num];
fLocalPlanes = TRACKED_NEW hsPlane3[num];
fWorldPlanes = TRACKED_NEW hsPlane3[num];
fNumPlanes = num;
}
void plConvexVolume::SetPlane(const hsPlane3 &plane, const UInt32 index)
{
fLocalPlanes[index] = plane;
}
hsBool plConvexVolume::IsInside(const hsPoint3 &pos) const
{
int i;
for( i = 0; i < fNumPlanes; i++ )
{
if (!TestPlane(pos, fWorldPlanes[i]))
return false;
}
return true;
}
hsBool plConvexVolume::ResolvePoint(hsPoint3 &pos) const
{
hsScalar minDist = 1.e33f;
Int32 minIndex = -1;
hsScalar currDist;
int i;
for (i = 0; i < fNumPlanes; i++)
{
currDist = -fWorldPlanes[i].fD - fWorldPlanes[i].fN.InnerProduct(pos);
if (currDist < 0)
return false; // We're not inside this plane, and thus outside the volume
if (currDist < minDist)
{
minDist = currDist;
minIndex = i;
}
}
pos += (-fWorldPlanes[minIndex].fD - fWorldPlanes[minIndex].fN.InnerProduct(pos)) * fWorldPlanes[minIndex].fN;
return true;
}
hsBool plConvexVolume::BouncePoint(hsPoint3 &pos, hsVector3 &velocity, hsScalar bounce, hsScalar friction) const
{
hsScalar minDist = 1.e33f;
Int32 minIndex = -1;
hsScalar currDist;
int i;
for (i = 0; i < fNumPlanes; i++)
{
currDist = -fWorldPlanes[i].fD - fWorldPlanes[i].fN.InnerProduct(pos);
if (currDist < 0)
return false; // We're not inside this plane, and thus outside the volume
if (currDist < minDist)
{
minDist = currDist;
minIndex = i;
}
}
pos += (-fWorldPlanes[minIndex].fD - fWorldPlanes[minIndex].fN.InnerProduct(pos)) * fWorldPlanes[minIndex].fN;
hsVector3 bnc = -velocity.InnerProduct(fWorldPlanes[minIndex].fN) * fWorldPlanes[minIndex].fN;
velocity += bnc;
velocity *= 1.f - friction;
velocity += bnc * bounce;
// velocity += (velocity.InnerProduct(fWorldPlanes[minIndex].fN) * -(1.f + bounce)) * fWorldPlanes[minIndex].fN;
return true;
}
void plConvexVolume::Read(hsStream* s, hsResMgr *mgr)
{
SetNumPlanesAndClear(s->ReadSwap32());
int i;
for (i = 0; i < fNumPlanes; i++)
{
fLocalPlanes[i].Read(s);
//fFlags[i] = s->ReadSwap32();
}
}
void plConvexVolume::Write(hsStream* s, hsResMgr *mgr)
{
s->WriteSwap32(fNumPlanes);
int i;
for (i = 0; i < fNumPlanes; i++)
{
fLocalPlanes[i].Write(s);
//s->WriteSwap32(fFlags[i]);
}
}

View File

@ -0,0 +1,103 @@
/*==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==*/
#ifndef plConvexVolume_inc
#define plConvexVolume_inc
#include "../pnSceneObject/plObjInterface.h"
struct hsPlane3;
struct hsPoint3;
struct hsMatrix44;
class hsResMgr;
// A convex volume defined by several boundary planes
// For now it assumes the user won't add planes that make it concave
class plConvexVolume : public plCreatable
{
public:
plConvexVolume();
~plConvexVolume();
CLASSNAME_REGISTER( plConvexVolume );
GETINTERFACE_ANY( plConvexVolume, plCreatable );
void Update(const hsMatrix44 &l2w);
hsBool AddPlane(const hsPlane3 &plane);
void SetNumPlanesAndClear(const UInt32 num);
void SetPlane(const hsPlane3 &plane, const UInt32 index);
// If you only care about the test, call this. Otherwise call ResolvePoint.
hsBool IsInside(const hsPoint3 &pos) const;
// returns true if the point was inside the volume, and thus moved outward.
hsBool ResolvePoint(hsPoint3 &pos) const;
// returns true if the point was inside and pos and velocity updated to bounce off offending plane.
// input bounce==1.f for perfect bounce, bounce==0 to slide.
hsBool BouncePoint(hsPoint3 &pos, hsVector3 &velocity, hsScalar bounce, hsScalar friction) const;
inline hsBool TestPlane(const hsPoint3 &pos, const hsPlane3 &plane) const; // Is the point inside the plane?
virtual void Read(hsStream* s, hsResMgr *mgr);
virtual void Write(hsStream* s, hsResMgr *mgr);
//virtual hsBool MsgReceive(plMessage* msg);
protected:
void IClear();
hsPlane3 *fLocalPlanes;
hsPlane3 *fWorldPlanes;
UInt32 fNumPlanes;
};
inline hsBool plConvexVolume::TestPlane(const hsPoint3 &pos, const hsPlane3 &plane) const
{
hsScalar dis = plane.fN.InnerProduct(pos);
dis += plane.fD;
if( dis >= 0.f )
return false;
return true;
}
#endif

View File

@ -0,0 +1,98 @@
/*==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==*/
#ifndef plEffectTargetInfo_inc
#define plEffectTargetInfo_inc
#include "hsTypes.h"
struct hsPoint3;
class plPipeline;
class plParticleSystem;
// This is the rendering context passed into an effect to let it cache up
// anything it needs to compute that will be the same for all particles.
// Not a lot of context to go on to begin with, but this will let that
// expand without any interface changes.
class plParticleContext
{
public:
plPipeline* fPipeline;
plParticleSystem* fSystem;
double fSecs;
hsScalar fDelSecs;
};
// This is just a collection of arrays and strides that a plParticleEffect object will reference and modify
// in the course of doing its job.
class plEffectTargetInfo
{
public:
// Byte arrays. Declared as type UInt8 so that adding the stride to the pointer is guaranteed to advance
// the exact number of bytes.
UInt8 *fPos;
UInt8 *fVelocity;
UInt8 *fInvMass;
UInt8 *fAcceleration;
UInt8 *fColor;
UInt8 *fRadsPerSec;
UInt8 *fMiscFlags;
UInt32 fPosStride;
UInt32 fVelocityStride;
UInt32 fInvMassStride;
UInt32 fAccelerationStride;
UInt32 fColorStride;
UInt32 fRadsPerSecStride;
UInt32 fMiscFlagsStride;
plParticleContext fContext;
UInt32 fNumValidParticles;
UInt32 fFirstNewParticle;
// We're going to need some sort of connectivity data for constraint satisfaction, but at least we have
// a system that allows that to be added in smoothly when it's needed, so for now, let's get the main
// goop working.
};
#endif

View File

@ -0,0 +1,114 @@
/*==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==*/
#ifndef plParticle_inc
#define plParticle_inc
#include "hsGeometry3.h"
#include "../CoreLib/hsColorRGBA.h"
// The meat of the particle. These classes, in combination with the plParticleEmitter that spawned it,
// should contain everything specific to a particle, necessary to build a renderable poly to represent a
// particular particle. (The emitter is necessary for properties (like texture) that are common among all
// particles that originated from the same emitter.
// For any reference in this object to a particle's poly vertices, the structure is as follows:
/*
|---| "HSize"
V3-----V2 -
| / | | "VSize"
| / | |
| P | -
| / |
| / | ("P" is the current position of the particle)
V0-----V1
So the vertices are arranged counter-clockwise, starting in the lower-left corner. Order all other attributes
accordingly.
*/
// The class plParticleCore should ONLY contain data necessary for the Drawable to create renderable polys
// Everything else goes into plParticleExt.
// plParticleEmitter is depending on the order that member variables appear in these classes, so
// DON'T MODIFY THEM WITHOUT MAKING SURE THE CONSTRUCTOR TO plParticleEmitter PROPERLY COMPUTES
// BASE ADDRESSES AND STRIDES!
// No initialization on construct. In nearly all cases, a default value won't be appropriate
// so there's no sense doing extra memory writes
class plParticleCore
{
public:
hsPoint3 fPos;
UInt32 fColor; // Particle opacity goes into the color's alpha.
hsPoint3 fOrientation; // fMiscFlags determines how this should be used.
hsVector3 fNormal;
hsScalar fHSize, fVSize; // distance from the heart of the particle to the borders of its poly.
hsPoint3 fUVCoords[4];
};
class plParticleExt
{
public:
//hsPoint3 fOldPos;
hsVector3 fVelocity;
hsScalar fInvMass; // The inverse (1 / mass) is what we actually need for calculations. Storing it this
// way allows us to make an object immovable with an inverse mass of 0 (and save a divide).
hsVector3 fAcceleration; // Accumulated from multiple forces.
hsScalar fLife; // how many seconds before we recycle this? (My particle has more of a life than I do...)
hsScalar fStartLife;
hsScalar fScale;
hsScalar fRadsPerSec;
//UInt32 fOrigColor;
enum // Miscellaneous flags for particles
{
kImmortal = 0x00000001,
};
UInt32 fMiscFlags; // I know... 32 bits for a single flag...
// Feel free to change this if you've got something to pack it against.
};
#endif

View File

@ -0,0 +1,135 @@
/*==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 "plParticleSystem.h"
#include "plParticleGenerator.h"
#include "plParticleApplicator.h"
#include "../plAvatar/plScalarChannel.h"
#include "../plAvatar/plAGModifier.h"
#include "../plMessage/plParticleUpdateMsg.h"
#include "../pnSceneObject/plSceneObject.h"
#define PI 3.14159
plParticleGenerator *plParticleApplicator::IGetParticleGen(plSceneObject *so)
{
UInt32 numMods = so->GetNumModifiers();
int i;
for (i = 0; i < numMods; i++)
{
const plParticleSystem *result = plParticleSystem::ConvertNoRef(so->GetModifier(i));
if (result != nil)
return result->GetExportedGenerator();
}
return nil;
}
void plParticleLifeMinApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamPartLifeMin,
chan->Value(time));
}
void plParticleLifeMaxApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamPartLifeMax,
chan->Value(time));
}
void plParticlePPSApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamParticlesPerSecond,
chan->Value(time));
}
void plParticleAngleApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamInitPitchRange,
(hsScalar)(chan->Value(time) * PI / 180.f));
}
void plParticleVelMinApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamVelMin,
chan->Value(time));
}
void plParticleVelMaxApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamVelMax,
chan->Value(time));
}
void plParticleScaleMinApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamScaleMin,
chan->Value(time) / 100.f);
}
void plParticleScaleMaxApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamScaleMax,
chan->Value(time) / 100.f);
}
void plParticleGravityApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
// IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamParticlesPerSecond,
// chan->Value(time));
}
void plParticleDragApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel);
// IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamParticlesPerSecond,
// chan->Value(time));
}

View File

@ -0,0 +1,161 @@
/*==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==*/
#ifndef PLPARTICLEAPPLICATOR_INC
#define PLPARTICLEAPPLICATOR_INC
#include "../plAvatar/plAGChannel.h"
#include "../plAvatar/plAGApplicator.h"
class plParticleSystem;
class plParticleApplicator : public plAGApplicator
{
protected:
plParticleGenerator *IGetParticleGen(plSceneObject *so);
virtual void IApply(const plAGModifier *mod, double time) = 0;
public:
CLASSNAME_REGISTER( plParticleApplicator );
GETINTERFACE_ANY( plParticleApplicator, plAGApplicator );
};
class plParticleLifeMinApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleLifeMinApplicator );
GETINTERFACE_ANY( plParticleLifeMinApplicator, plAGApplicator );
};
class plParticleLifeMaxApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleLifeMaxApplicator );
GETINTERFACE_ANY( plParticleLifeMaxApplicator, plAGApplicator );
};
class plParticlePPSApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticlePPSApplicator );
GETINTERFACE_ANY( plParticlePPSApplicator, plAGApplicator );
};
class plParticleAngleApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleAngleApplicator );
GETINTERFACE_ANY( plParticleAngleApplicator, plAGApplicator );
};
class plParticleVelMinApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleVelMinApplicator );
GETINTERFACE_ANY( plParticleVelMinApplicator, plAGApplicator );
};
class plParticleVelMaxApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleVelMaxApplicator );
GETINTERFACE_ANY( plParticleVelMaxApplicator, plAGApplicator );
};
class plParticleScaleMinApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleScaleMinApplicator );
GETINTERFACE_ANY( plParticleScaleMinApplicator, plAGApplicator );
};
class plParticleScaleMaxApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleScaleMaxApplicator );
GETINTERFACE_ANY( plParticleScaleMaxApplicator, plAGApplicator );
};
class plParticleGravityApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleGravityApplicator );
GETINTERFACE_ANY( plParticleGravityApplicator, plAGApplicator );
};
class plParticleDragApplicator : public plParticleApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plParticleDragApplicator );
GETINTERFACE_ANY( plParticleDragApplicator, plAGApplicator );
};
#endif

View File

@ -0,0 +1,89 @@
/*==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==*/
#ifndef plParticleCreatable_inc
#define plParticleCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plParticleSystem.h"
#include "plParticleEffect.h"
#include "plParticleEmitter.h"
#include "plParticleGenerator.h"
#include "plParticleSystem.h"
#include "plParticleApplicator.h"
#include "plParticleSDLMod.h"
#include "plConvexVolume.h"
#include "plBoundInterface.h"
REGISTER_CREATABLE( plParticleSystem );
REGISTER_NONCREATABLE( plParticleEffect );
REGISTER_NONCREATABLE( plParticleCollisionEffect );
REGISTER_CREATABLE( plParticleCollisionEffectBeat );
REGISTER_CREATABLE( plParticleCollisionEffectDie );
REGISTER_CREATABLE( plParticleCollisionEffectBounce );
REGISTER_CREATABLE( plParticleFadeVolumeEffect );
REGISTER_NONCREATABLE( plParticleGenerator );
REGISTER_CREATABLE( plSimpleParticleGenerator );
REGISTER_CREATABLE( plOneTimeParticleGenerator );
REGISTER_CREATABLE( plParticleEmitter );
REGISTER_CREATABLE( plConvexVolume );
REGISTER_CREATABLE( plBoundInterface );
REGISTER_NONCREATABLE( plParticleApplicator );
REGISTER_CREATABLE( plParticleLifeMinApplicator );
REGISTER_CREATABLE( plParticleLifeMaxApplicator );
REGISTER_CREATABLE( plParticlePPSApplicator );
REGISTER_CREATABLE( plParticleAngleApplicator );
REGISTER_CREATABLE( plParticleVelMinApplicator );
REGISTER_CREATABLE( plParticleVelMaxApplicator );
REGISTER_CREATABLE( plParticleScaleMinApplicator );
REGISTER_CREATABLE( plParticleScaleMaxApplicator );
//REGISTER_CREATABLE( plParticleGravityApplicator );
//REGISTER_CREATABLE( plParticleDragApplicator );
REGISTER_NONCREATABLE( plParticleWindEffect );
REGISTER_CREATABLE( plParticleLocalWind );
REGISTER_CREATABLE( plParticleUniformWind );
REGISTER_CREATABLE( plParticleFlockEffect );
REGISTER_CREATABLE( plParticleFollowSystemEffect );
REGISTER_CREATABLE( plParticleSDLMod );
#endif // plParticleCreatable_inc

View 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();
}

View File

@ -0,0 +1,356 @@
/*==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==*/
#ifndef plParticleEffect_inc
#define plParticleEffect_inc
#include "../pnKeyedObject/hsKeyedObject.h"
#include "hsMatrix44.h"
class plEffectTargetInfo;
class plConvexVolume;
class hsResMgr;
class plSceneObject;
class plParticleEffect : public hsKeyedObject
{
public:
CLASSNAME_REGISTER( plParticleEffect );
GETINTERFACE_ANY( plParticleEffect, hsKeyedObject );
// Order is:
// PrepareEffect is called with a given target (including valid
// ParticleContext).
// ApplyEffect is called some once for each particle (maybe zero times).
// It can return true to kill a particle.
// Target and Context passed in with Prepare will be
// guaranteed to remain valid until,
// EndEffect marks no more particles will be processed with the above
// context (invalidating anything cached).
// Defaults for Prepare and End are no-ops.
virtual void PrepareEffect(const plEffectTargetInfo& target) {}
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i) = 0;
virtual void EndEffect(const plEffectTargetInfo& target) {}
};
class plParticleCollisionEffect : public plParticleEffect
{
public:
plParticleCollisionEffect();
~plParticleCollisionEffect();
CLASSNAME_REGISTER( plParticleCollisionEffect );
GETINTERFACE_ANY( plParticleCollisionEffect, plParticleEffect );
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
virtual hsBool MsgReceive(plMessage *msg);
protected:
plSceneObject *fSceneObj;
plConvexVolume *fBounds;
};
// Default particle blocker. Doesn't affect particle's velocity,
// so it'll keep "beat"ing on the deflector until the velocity
// dotted with the deflector normal slides it off an edge.
class plParticleCollisionEffectBeat : public plParticleCollisionEffect
{
public:
plParticleCollisionEffectBeat();
CLASSNAME_REGISTER( plParticleCollisionEffectBeat );
GETINTERFACE_ANY( plParticleCollisionEffectBeat, plParticleCollisionEffect );
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
};
// This particle blocker just kills any particles that hit it.
class plParticleCollisionEffectDie : public plParticleCollisionEffect
{
public:
plParticleCollisionEffectDie();
CLASSNAME_REGISTER( plParticleCollisionEffectDie );
GETINTERFACE_ANY( plParticleCollisionEffectDie, plParticleCollisionEffect );
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
};
class plParticleCollisionEffectBounce : public plParticleCollisionEffect
{
protected:
hsScalar fBounce;
hsScalar fFriction;
public:
plParticleCollisionEffectBounce();
CLASSNAME_REGISTER( plParticleCollisionEffectBounce );
GETINTERFACE_ANY( plParticleCollisionEffectBounce, plParticleCollisionEffect );
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
void SetBounce(hsScalar b) { fBounce = b; }
hsScalar GetBounce() const { return fBounce; }
void SetFriction(hsScalar f) { fFriction = f; }
hsScalar GetFriction() const { return fFriction; }
};
class plParticleFadeVolumeEffect : public plParticleEffect
{
protected:
// Some cached properties. These will be the same for all
// particles between matching PrepareEffect and EndEffect calls.
hsPoint3 fMax;
hsPoint3 fMin;
hsPoint3 fNorm;
public:
plParticleFadeVolumeEffect();
~plParticleFadeVolumeEffect();
CLASSNAME_REGISTER( plParticleFadeVolumeEffect );
GETINTERFACE_ANY( plParticleFadeVolumeEffect, plParticleEffect );
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
//virtual hsBool MsgReceive(plMessage *msg);
hsScalar fLength;
hsBool fIgnoreZ;
};
class plParticleWindEffect : public plParticleEffect
{
protected:
// The properties that define the wind. These might/should be animatable.
hsScalar fStrength;
hsScalar fConstancy;
hsScalar fSwirl;
hsBool fHorizontal;
hsVector3 fRefDir;
// Some cached properties. These will be the same for all
// particles between matching PrepareEffect and EndEffect calls.
// These define the current state of the wind.
hsVector3 fWindVec;
hsVector3 fRandDir;
hsVector3 fDir;
double fLastDirSecs;
public:
plParticleWindEffect();
~plParticleWindEffect();
CLASSNAME_REGISTER( plParticleWindEffect );
GETINTERFACE_ANY( plParticleWindEffect, plParticleEffect );
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i) = 0;
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
void SetStrength(hsScalar v) { fStrength = v; }
hsScalar GetStrength() const { return fStrength; }
void SetConstancy(hsScalar c) { fConstancy = c; }
hsScalar GetConstancy() const { return fConstancy; }
void SetSwirl(hsScalar s) { fSwirl = s; }
hsScalar GetSwirl() const { return fSwirl; }
void SetHorizontal(hsBool on) { fHorizontal = on; }
hsBool GetHorizontal() const { return fHorizontal; }
void SetRefDirection(const hsVector3& v);
const hsVector3& GetRefDirection() const { return fRefDir; }
};
class plParticleLocalWind : public plParticleWindEffect
{
protected:
hsVector3 fScale;
hsScalar fSpeed;
hsVector3 fPhase;
hsVector3 fInvScale;
double fLastPhaseSecs;
public:
plParticleLocalWind();
~plParticleLocalWind();
CLASSNAME_REGISTER( plParticleLocalWind );
GETINTERFACE_ANY( plParticleLocalWind, plParticleWindEffect );
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
void SetScale(const hsVector3& v) { fScale = v; }
const hsVector3& GetScale() const { return fScale; }
void SetSpeed(hsScalar v) { fSpeed = v; }
hsScalar GetSpeed() const { return fSpeed; }
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
};
class plParticleUniformWind : public plParticleWindEffect
{
protected:
hsScalar fFreqMin;
hsScalar fFreqMax;
hsScalar fFreqCurr;
hsScalar fFreqRate;
double fCurrPhase;
double fLastFreqSecs;
hsScalar fCurrentStrength;
public:
plParticleUniformWind();
~plParticleUniformWind();
CLASSNAME_REGISTER( plParticleUniformWind );
GETINTERFACE_ANY( plParticleUniformWind, plParticleWindEffect );
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
void SetFrequencyRange(hsScalar minSecsPerCycle, hsScalar maxSecsPerCycle);
void SetFrequencyRate(hsScalar secsPerCycle);
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
};
class plParticleInfluenceInfo
{
public:
hsVector3 fAvgVel;
hsVector3 fRepDir;
};
class plParticleFlockEffect : public plParticleEffect
{
//protected:
protected:
hsPoint3 fTargetOffset; // Worldspace offset from our target to get the true goal
hsPoint3 fDissenterTarget; // Where to particles go when they get scared and leave us?
hsScalar fInfAvgRadSq; // Square of the radius of influence for average velocity matching.
hsScalar fInfRepRadSq; // Same, for repelling from neighbors.
hsScalar fAvgVelStr; // How strongly are we influenced by average dir?
hsScalar fRepDirStr; // Same for repelling
hsScalar fGoalOrbitStr; // Same for the goal (when we're within the desired distance)
hsScalar fGoalChaseStr; // Same for the goal (when we're too far away, and chasing it)
hsScalar fGoalDistSq;
hsScalar fFullChaseDistSq;
hsScalar fMaxOrbitSpeed;
hsScalar fMaxChaseSpeed;
UInt16 fMaxParticles;
hsScalar *fDistSq; // Table of distances from particle to particle
plParticleInfluenceInfo *fInfluences;
void IUpdateDistances(const plEffectTargetInfo &target);
void IUpdateInfluences(const plEffectTargetInfo &target);
public:
plParticleFlockEffect();
~plParticleFlockEffect();
CLASSNAME_REGISTER( plParticleFlockEffect );
GETINTERFACE_ANY( plParticleFlockEffect, plParticleEffect );
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
void SetTargetOffset(const hsPoint3 &offset) { fTargetOffset = offset; }
void SetDissenterTarget(const hsPoint3 &target) { fDissenterTarget = target; }
void SetInfluenceAvgRadius(hsScalar val) { fInfAvgRadSq = val * val; }
void SetInfluenceRepelRadius(hsScalar val) { fInfRepRadSq = val * val; }
void SetGoalRadius(hsScalar val) { fGoalDistSq = val * val; }
void SetFullChaseRadius(hsScalar val) { fFullChaseDistSq = val * val; }
void SetConformStr(hsScalar val) { fAvgVelStr = val; }
void SetRepelStr(hsScalar val) { fRepDirStr = val; }
void SetGoalOrbitStr(hsScalar val) { fGoalOrbitStr = val; }
void SetGoalChaseStr(hsScalar val) { fGoalChaseStr = val; }
void SetMaxOrbitSpeed(hsScalar val) { fMaxOrbitSpeed = val; }
void SetMaxChaseSpeed(hsScalar val) { fMaxChaseSpeed = val; }
void SetMaxParticles(UInt16 num);
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
virtual hsBool MsgReceive(plMessage *msg);
};
class plParticleFollowSystemEffect : public plParticleEffect
{
public:
CLASSNAME_REGISTER( plParticleFollowSystemEffect );
GETINTERFACE_ANY( plParticleFollowSystemEffect, plParticleEffect );
plParticleFollowSystemEffect();
virtual void PrepareEffect(const plEffectTargetInfo& target);
virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i);
virtual void EndEffect(const plEffectTargetInfo& target);
protected:
hsMatrix44 fOldW2L;
hsBool fEvalThisFrame;
};
#endif

View File

@ -0,0 +1,585 @@
/*==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 "hsUtils.h"
#include "hsResMgr.h"
#include "../pnMessage/plRefMsg.h"
#include "../plMessage/plParticleUpdateMsg.h"
#include "plParticleGenerator.h"
#include "plParticleEmitter.h"
#include "plParticleSystem.h"
#include "plParticle.h"
#include "plParticleEffect.h"
#include "../CoreLib/hsColorRGBA.h"
#include "../plInterp/plController.h"
#include "../plSurface/hsGMaterial.h"
#include "../plSurface/plLayerInterface.h"
#include "plProfile.h"
#include "hsFastMath.h"
plProfile_CreateTimer("Update", "Particles", ParticleUpdate);
plProfile_CreateTimer("Generate", "Particles", ParticleGenerate);
plParticleEmitter::plParticleEmitter()
{
fParticleCores = nil;
fParticleExts = nil;
fGenerator = nil;
fLocalToWorld.Reset();
fTimeToLive = 0;
}
void plParticleEmitter::Init(plParticleSystem *system, UInt32 maxParticles, UInt32 spanIndex, UInt32 miscFlags,
plParticleGenerator *gen /* = nil */)
{
IClear();
fSystem = system;
plLayerInterface *layer = system->fTexture->GetLayer(0)->BottomOfStack();
fMiscFlags = miscFlags | kNeedsUpdate;
if( fMiscFlags & kOnReserve )
fTimeToLive = -1.f; // Wait for someone to give us a spurt of life.
if( layer->GetShadeFlags() & hsGMatState::kShadeEmissive )
{
fMiscFlags |= kMatIsEmissive;
fColor = layer->GetAmbientColor();
}
else
{
fColor = layer->GetRuntimeColor();
}
fColor.a = layer->GetOpacity();
fGenerator = gen;
fMaxParticles = maxParticles;
fSpanIndex = spanIndex;
ISetupParticleMem();
}
void plParticleEmitter::Clone(plParticleEmitter* src, UInt32 spanIndex)
{
Init(src->fSystem,
src->fMaxParticles,
spanIndex,
src->fMiscFlags,
src->fGenerator);
fMiscFlags |= kBorrowedGenerator;
fTimeToLive = -1.f;
}
void plParticleEmitter::OverrideLocalToWorld(const hsMatrix44& l2w)
{
fLocalToWorld = l2w;
fMiscFlags |= kOverrideLocalToWorld;
}
plParticleEmitter::~plParticleEmitter()
{
IClear();
}
void plParticleEmitter::IClear()
{
delete [] fParticleCores;
fParticleCores = nil;
delete [] fParticleExts;
fParticleExts = nil;
if( !(fMiscFlags & kBorrowedGenerator) )
delete fGenerator;
fGenerator = nil;
}
void plParticleEmitter::ISetupParticleMem()
{
fNumValidParticles = 0;
fParticleCores = TRACKED_NEW plParticleCore[fMaxParticles];
fParticleExts = TRACKED_NEW plParticleExt[fMaxParticles];
fTargetInfo.fPos = (UInt8 *)fParticleCores;
fTargetInfo.fColor = (UInt8 *)fParticleCores + sizeof(hsPoint3);
fTargetInfo.fPosStride = fTargetInfo.fColorStride = sizeof(plParticleCore);
fTargetInfo.fVelocity = (UInt8 *)fParticleExts;
fTargetInfo.fInvMass = fTargetInfo.fVelocity + sizeof(hsVector3);
fTargetInfo.fAcceleration = fTargetInfo.fInvMass + sizeof(hsScalar);
fTargetInfo.fMiscFlags = (UInt8 *)&(fParticleExts[0].fMiscFlags);
fTargetInfo.fRadsPerSec = (UInt8 *)&(fParticleExts[0].fRadsPerSec);
fTargetInfo.fVelocityStride
= fTargetInfo.fInvMassStride
= fTargetInfo.fAccelerationStride
= fTargetInfo.fRadsPerSecStride
= fTargetInfo.fMiscFlagsStride
= sizeof(plParticleExt);
}
UInt32 plParticleEmitter::GetNumTiles() const
{
return fSystem->GetNumTiles();
}
const hsMatrix44 &plParticleEmitter::GetLocalToWorld() const
{
return fMiscFlags & kOverrideLocalToWorld ? fLocalToWorld : fSystem->GetLocalToWorld();
}
void plParticleEmitter::AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex,
hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life,
hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec)
{
plParticleCore *core;
plParticleExt *ext;
UInt32 currParticle;
if (fNumValidParticles == fMaxParticles)
return; // No more room... you lose!
else
currParticle = fNumValidParticles++;
core = &fParticleCores[currParticle];
core->fPos = pos;
core->fOrientation = orientation;
core->fColor = CreateHexColor(fColor);
core->fHSize = hSize * scale;
core->fVSize = vSize * scale;
// even if the kNormalUp flag isn't there, we should initialize it to something sane.
//if (core->fMiscFlags & kNormalUp != 0)
core->fNormal.Set(0, 0, 1);
hsScalar xOff = (tileIndex % fSystem->fXTiles) / (float)fSystem->fXTiles;
hsScalar yOff = (tileIndex / fSystem->fXTiles) / (float)fSystem->fYTiles;
core->fUVCoords[0].fX = xOff;
core->fUVCoords[0].fY = yOff + 1.0f / fSystem->fYTiles;
core->fUVCoords[0].fZ = 1.0f;
core->fUVCoords[1].fX = xOff + 1.0f / fSystem->fXTiles;
core->fUVCoords[1].fY = yOff + 1.0f / fSystem->fYTiles;
core->fUVCoords[1].fZ = 1.0f;
core->fUVCoords[2].fX = xOff + 1.0f / fSystem->fXTiles;
core->fUVCoords[2].fY = yOff;
core->fUVCoords[2].fZ = 1.0f;
core->fUVCoords[3].fX = xOff;
core->fUVCoords[3].fY = yOff;
core->fUVCoords[3].fZ = 1.0f;
ext = &fParticleExts[currParticle];
ext->fVelocity = velocity;
ext->fInvMass = invMass;
ext->fLife = ext->fStartLife = life;
ext->fMiscFlags = miscFlags; // Is this ever NOT zero?
if (life <= 0)
ext->fMiscFlags |= plParticleExt::kImmortal;
ext->fRadsPerSec = radsPerSec;
ext->fAcceleration.Set(0, 0, 0);
ext->fScale = scale;
}
void plParticleEmitter::WipeExistingParticles()
{
fNumValidParticles = 0; // That was easy.
}
// This method is called from a network received message. Don't trust the args without checking.
void plParticleEmitter::KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags)
{
if (flags & plParticleKillMsg::kParticleKillPercentage)
{
if (num < 0)
num = 0;
if (num > 1)
num = 1;
num *= fNumValidParticles;
}
else
{
if (num < 0)
num = 0;
}
if (timeToDie < 0)
timeToDie = 0;
int i;
for (i = 0; i < fNumValidParticles && num > 0; i++)
{
if ((flags & plParticleKillMsg::kParticleKillImmortalOnly) && !(fParticleExts[i].fMiscFlags & plParticleExt::kImmortal))
continue;
fParticleExts[i].fLife = fParticleExts[i].fStartLife = timeToDie;
fParticleExts[i].fMiscFlags &= ~plParticleExt::kImmortal;
num--;
}
}
void plParticleEmitter::UpdateGenerator(UInt32 paramID, hsScalar paramValue)
{
if (fGenerator != nil)
fGenerator->UpdateParam(paramID, paramValue);
}
UInt16 plParticleEmitter::StealParticlesFrom(plParticleEmitter *victim, UInt16 num)
{
UInt16 spaceAvail = (UInt16)(fMaxParticles - fNumValidParticles);
UInt16 numToCopy = (UInt16)(hsMinimum(num, (victim ? victim->fNumValidParticles : 0)));
if (spaceAvail < numToCopy)
numToCopy = spaceAvail;
if (numToCopy > 0)
{
// copy them over
memcpy(&(fParticleCores[fNumValidParticles]), &(victim->fParticleCores[victim->fNumValidParticles - numToCopy]), numToCopy * sizeof(plParticleCore));
memcpy(&(fParticleExts[fNumValidParticles]), &(victim->fParticleExts[victim->fNumValidParticles- numToCopy]), numToCopy * sizeof(plParticleExt));
fNumValidParticles += numToCopy;
victim->fNumValidParticles -= numToCopy;
}
return numToCopy;
}
void plParticleEmitter::TranslateAllParticles(hsPoint3 &amount)
{
int i;
for (i = 0; i < fNumValidParticles; i++)
fParticleCores[i].fPos += amount;
}
hsBool plParticleEmitter::IUpdate(hsScalar delta)
{
if (fMiscFlags & kNeedsUpdate)
{
plProfile_BeginTiming(ParticleUpdate);
IUpdateParticles(delta);
IUpdateBoundsAndNormals(delta);
plProfile_EndTiming(ParticleUpdate);
}
if (fGenerator == nil && fNumValidParticles <= 0)
return false; // no generator, no particles, let the system decide if this emitter is done
else
return true;
}
void plParticleEmitter::IUpdateParticles(hsScalar delta)
{
int i, j;
// Have to remove particles before adding new ones, or we can run out of room.
for (i = 0; i < fNumValidParticles; i++)
{
fParticleExts[i].fLife -= delta;
if (fParticleExts[i].fLife <= 0 && !(fParticleExts[i].fMiscFlags & plParticleExt::kImmortal))
{
IRemoveParticle(i);
i--; // so that we hit this index again on the next iteration
continue;
}
}
fTargetInfo.fFirstNewParticle = fNumValidParticles;
if ((fGenerator != nil) && (fTimeToLive >= 0))
{
plProfile_BeginLap(ParticleGenerate, fSystem->GetKeyName());
if (!fGenerator->AddAutoParticles(this, delta))
{
delete fGenerator;
fGenerator = nil;
}
if( (fTimeToLive > 0) && ((fTimeToLive -= delta) <= 0) )
fTimeToLive = -1.f;
plProfile_EndLap(ParticleGenerate, fSystem->GetKeyName());
}
fTargetInfo.fContext = fSystem->fContext;
fTargetInfo.fNumValidParticles = fNumValidParticles;
const hsVector3 up(0, 0, 1.0f);
hsVector3 currDirection;
hsPoint3 *currPos;
hsVector3 *currVelocity;
hsVector3 *currAccel;
hsPoint3 color(fColor.r, fColor.g, fColor.b);
hsScalar alpha = fColor.a;
plController *colorCtl;
// Allow effects a chance to cache any upfront calculations
// that will apply to all particles.
for (j = 0; j < fSystem->fForces.GetCount(); j++)
{
fSystem->fForces[j]->PrepareEffect(fTargetInfo);
}
for (j = 0; j < fSystem->fEffects.GetCount(); j++)
{
fSystem->fEffects[j]->PrepareEffect(fTargetInfo);
}
for (j = 0; j < fSystem->fConstraints.GetCount(); j++)
{
fSystem->fConstraints[j]->PrepareEffect(fTargetInfo);
}
for (i = 0; i < fNumValidParticles; i++)
{
if (!( fParticleExts[i].fMiscFlags & plParticleExt::kImmortal ))
{
hsScalar percent = (1.0f - fParticleExts[i].fLife / fParticleExts[i].fStartLife);
colorCtl = (fMiscFlags & kMatIsEmissive ? fSystem->fAmbientCtl : fSystem->fDiffuseCtl);
if (colorCtl != nil)
colorCtl->Interp(colorCtl->GetLength() * percent, &color);
if (fSystem->fOpacityCtl != nil)
{
fSystem->fOpacityCtl->Interp(fSystem->fOpacityCtl->GetLength() * percent, &alpha);
alpha /= 100.0f;
if (alpha < 0)
alpha = 0;
else if (alpha > 1.f)
alpha = 1.f;
}
if (fSystem->fWidthCtl != nil)
{
fSystem->fWidthCtl->Interp(fSystem->fWidthCtl->GetLength() * percent,
&fParticleCores[i].fHSize);
fParticleCores[i].fHSize *= fParticleExts[i].fScale;
}
if (fSystem->fHeightCtl != nil)
{
fSystem->fHeightCtl->Interp(fSystem->fHeightCtl->GetLength() * percent,
&fParticleCores[i].fVSize);
fParticleCores[i].fVSize *= fParticleExts[i].fScale;
}
fParticleCores[i].fColor = CreateHexColor(color.fX, color.fY, color.fZ, alpha);
}
for (j = 0; j < fSystem->fForces.GetCount(); j++)
{
fSystem->fForces[j]->ApplyEffect(fTargetInfo, i);
}
currPos = (hsPoint3 *)(fTargetInfo.fPos + i * fTargetInfo.fPosStride);
currVelocity = (hsVector3 *)(fTargetInfo.fVelocity + i * fTargetInfo.fVelocityStride);
//currAccel = (hsVector3 *)(fTargetInfo.fAcceleration + i * fTargetInfo.fAccelerationStride);
currAccel = &fSystem->fAccel; // Nothing accellerates on a per-particle basis (yet)
*currPos += *currVelocity * delta;
// This is the only orientation option (so far) that requires an update here
if (fMiscFlags & (kOrientationVelocityBased | kOrientationVelocityStretch | kOrientationVelocityFlow))
fParticleCores[i].fOrientation.Set(&(*currVelocity * delta)); // mf - want the orientation to be a delposition
else if( fParticleExts[i].fRadsPerSec != 0 )
{
hsScalar sinX, cosX;
hsFastMath::SinCos(fParticleExts[i].fLife * fParticleExts[i].fRadsPerSec * 2.f * hsScalarPI, sinX, cosX);
fParticleCores[i].fOrientation.Set(sinX, -cosX, 0);
}
// Viscous force F(t) = -k V(t)
// Integral S from t0 to t1 of F(t) is
// = S(-kV(t))[t1..t0]
// = -k(P(t1) - P(t0))
// = -k*(currVelocity * delta)
// or
// V = V + -k*(V * delta)
// V *= (1 + -k * delta)
// Giving the change in velocity.
hsScalar drag = 1.f + fSystem->fDrag * delta;
// Clamp it at 0. Drag should never cause a reversal in velocity direction.
if( drag < 0.f )
drag = 0.f;
*currVelocity *= drag;
*currVelocity += *currAccel * delta;
for (j = 0; j < fSystem->fEffects.GetCount(); j++)
{
fSystem->fEffects[j]->ApplyEffect(fTargetInfo, i);
}
// We may need to do more than one iteration through the constraints. It's a trade-off
// between accurracy and speed (what's new?) but I'm going to go with just one
// for now until we decide things don't "look right"
for (j = 0; j < fSystem->fConstraints.GetCount(); j++)
{
if( fSystem->fConstraints[j]->ApplyEffect(fTargetInfo, i) )
{
IRemoveParticle(i);
i--; // so that we hit this index again on the next iteration
break;
// break will break us out of loop over constraints,
// and since we're last, we move onto next particle.
}
}
}
// Notify the effects that they are done for now.
for (j = 0; j < fSystem->fForces.GetCount(); j++)
{
fSystem->fForces[j]->EndEffect(fTargetInfo);
}
for (j = 0; j < fSystem->fEffects.GetCount(); j++)
{
fSystem->fEffects[j]->EndEffect(fTargetInfo);
}
for (j = 0; j < fSystem->fConstraints.GetCount(); j++)
{
fSystem->fConstraints[j]->EndEffect(fTargetInfo);
}
}
plProfile_CreateTimer("Bound", "Particles", ParticleBound);
plProfile_CreateTimer("Normal", "Particles", ParticleNormal);
void plParticleEmitter::IUpdateBoundsAndNormals(hsScalar delta)
{
plProfile_BeginTiming(ParticleBound);
fBoundBox.MakeEmpty();
int i;
for (i = 0; i < fNumValidParticles; i++)
fBoundBox.Union(&fParticleCores[i].fPos);
hsPoint3 center;
if (fNumValidParticles > 0)
center = fBoundBox.GetCenter();
plProfile_EndTiming(ParticleBound);
plProfile_BeginTiming(ParticleNormal);
hsVector3 normal;
if (fMiscFlags & kNormalVelUpVel)
{
for (i = fNumValidParticles - 1; i >=0; i--)
{
//currDirection.Set(&fParticleCores[i].fPos, &fParticleExts[i].fOldPos);
//normal = (currDirection % up % currDirection);
normal.Set(-fParticleExts[i].fVelocity.fX * fParticleExts[i].fVelocity.fZ,
-fParticleExts[i].fVelocity.fY * fParticleExts[i].fVelocity.fZ,
(fParticleExts[i].fVelocity.fX * fParticleExts[i].fVelocity.fX +
fParticleExts[i].fVelocity.fY * fParticleExts[i].fVelocity.fY));
if (!normal.IsEmpty()) // zero length check
{
normal.Normalize();
fParticleCores[i].fNormal = normal;
}
}
}
else if (fMiscFlags & kNormalFromCenter)
{
for (i = fNumValidParticles - 1; i >=0; i--)
{
normal.Set(&fParticleCores[i].fPos, &center);
if (!normal.IsEmpty()) // zero length check
{
normal.Normalize();
fParticleCores[i].fNormal = normal;
}
}
}
// otherwise we just keep the last normal.
plProfile_EndTiming(ParticleNormal);
}
void plParticleEmitter::IRemoveParticle(UInt32 index)
{
hsAssert(index < fNumValidParticles, "Trying to remove an invalid particle");
fNumValidParticles--;
if (fNumValidParticles <= 0)
{
fNumValidParticles = 0;
return;
}
fParticleCores[index] = fParticleCores[fNumValidParticles];
fParticleExts[index] = fParticleExts[fNumValidParticles];
}
// Reading and writing doesn't transfer individual particle info. We assume those are expendable.
// Only the configuration info necessary to began spawning particles again is saved.
// The particle system that owns this emitter is responsible for setting the pointer back to itself
void plParticleEmitter::Read(hsStream *s, hsResMgr *mgr)
{
plCreatable::Read(s, mgr);
fGenerator = plParticleGenerator::ConvertNoRef(mgr->ReadCreatable(s));
fSpanIndex = s->ReadSwap32();
fMaxParticles = s->ReadSwap32();
fMiscFlags = s->ReadSwap32();
fColor.Read(s);
if( fMiscFlags & kOnReserve )
fTimeToLive = -1.f; // Wait for someone to give us a spurt of life.
ISetupParticleMem();
}
void plParticleEmitter::Write(hsStream *s, hsResMgr *mgr)
{
plCreatable::Write(s, mgr);
mgr->WriteCreatable(s, fGenerator);
s->WriteSwap32(fSpanIndex);
s->WriteSwap32(fMaxParticles);
s->WriteSwap32(fMiscFlags);
fColor.Write(s);
}
UInt32 plParticleEmitter::CreateHexColor(const hsColorRGBA &color)
{
return CreateHexColor(color.r, color.g, color.b, color.a);
}
UInt32 plParticleEmitter::CreateHexColor(const hsScalar r, const hsScalar g, const hsScalar b, const hsScalar a)
{
UInt32 ru, gu, bu, au;
au = (UInt32)(a * 255.0f);
ru = (UInt32)(r * 255.0f);
gu = (UInt32)(g * 255.0f);
bu = (UInt32)(b * 255.0f);
return ( au << 24 ) | ( ru << 16 ) | ( gu << 8 ) | ( bu );
}

View File

@ -0,0 +1,159 @@
/*==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==*/
#ifndef plParticleEmitter_inc
#define plParticleEmitter_inc
#include "hsGeometry3.h"
#include "hsBounds.h"
#include "../pnNetCommon/plSynchedValue.h"
#include "../CoreLib/hsColorRGBA.h"
class hsBounds3Ext;
class plParticleSystem;
class plParticleCore;
class plParticleExt;
class plParticleGenerator;
class plSimpleParticleGenerator;
class hsResMgr;
#include "plEffectTargetInfo.h"
// This just holds a bunch of parameters for an emission location. A particle system can have several of these
class plParticleEmitter : public plCreatable
{
friend plParticleSystem;
friend plSimpleParticleGenerator;
public:
plParticleEmitter();
~plParticleEmitter();
void Init(plParticleSystem *system, UInt32 maxParticles, UInt32 spanIndex, UInt32 miscFlags,
plParticleGenerator *gen = nil);
void Clone(plParticleEmitter* src, UInt32 spanIndex);
plParticleCore *GetParticleArray() const { return fParticleCores; }
UInt32 GetParticleCount() const { return fNumValidParticles; }
UInt32 GetNumTiles() const;
const hsBounds3Ext &GetBoundingBox() const { return fBoundBox; }
UInt32 GetSpanIndex() const { return fSpanIndex; }
const hsMatrix44 &GetLocalToWorld() const;
void AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex,
hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life,
hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec=0);
void WipeExistingParticles();
void KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags);
UInt16 StealParticlesFrom(plParticleEmitter *victim, UInt16 num); // returns the number actually stolen
void TranslateAllParticles(hsPoint3 &amount); // Used to recenter the system when linking between ages.
void UpdateGenerator(UInt32 paramID, hsScalar paramValue);
static UInt32 CreateHexColor(const hsColorRGBA &color);
static UInt32 CreateHexColor(const hsScalar r, const hsScalar g, const hsScalar b, const hsScalar a);
void OverrideLocalToWorld(const hsMatrix44& l2w);
void UnOverrideLocalToWorld() { fMiscFlags &= ~kOverrideLocalToWorld; }
hsBool LocalToWorldOverridden() const { return 0 != (fMiscFlags & kOverrideLocalToWorld); }
void SetTimeToLive(hsScalar dt) { fTimeToLive = dt; }
hsScalar GetTimeToLive() const { return fTimeToLive; } // 0 time to live is never turn off.
CLASSNAME_REGISTER( plParticleEmitter );
GETINTERFACE_ANY( plParticleEmitter, plCreatable);
virtual void Read(hsStream* s, hsResMgr *mgr);
virtual void Write(hsStream* s, hsResMgr *mgr);
enum // Miscellaneous flags
{
kMatIsEmissive = 0x00000001,
kNormalUp = 0x00000010,
kNormalVelUpVel = 0x00000020,
kNormalFromCenter = 0x00000040,
kNormalDynamicMask = kNormalVelUpVel | kNormalFromCenter, // precalc methods that need updating each frame
kNormalPrecalcMask = kNormalDynamicMask | kNormalUp, // All types where emitter precalculates the normal
kNormalViewFacing = 0x00000100,
kNormalNearestLight = 0x00000200,
kNeedsUpdate = 0x01000000,
kBorrowedGenerator = 0x02000000,
kOverrideLocalToWorld = 0x04000000,
kOnReserve = 0x08000000,
kOrientationUp = 0x10000000,
kOrientationVelocityBased = 0x20000000,
kOrientationVelocityStretch = 0x40000000,
kOrientationVelocityFlow = 0x80000000,
kOrientationVelocityMask = kOrientationVelocityBased | kOrientationVelocityStretch | kOrientationVelocityFlow, // Velocity dependent
kOrientationMask = kOrientationUp | kOrientationVelocityMask,
};
UInt32 fMiscFlags;
protected:
plParticleSystem *fSystem; // The particle system this belongs to.
plParticleCore *fParticleCores; // The particle pool, created on init, initialized as needed, and recycled.
plParticleExt *fParticleExts; // Same mapping as the Core pool. Contains extra info the render pipeline
// doesn't need.
plParticleGenerator *fGenerator; // Optional auto generator (have this be nil if you don't want auto-generation)
UInt32 fSpanIndex; // Index of the span that this emitter uses.
UInt32 fNumValidParticles;
UInt32 fMaxParticles;
hsBounds3Ext fBoundBox;
plEffectTargetInfo fTargetInfo; // A collection of pointers and strides that plParticleEffects will manipulate.
hsColorRGBA fColor;
hsMatrix44 fLocalToWorld;
hsScalar fTimeToLive;
void IClear();
void ISetupParticleMem();
void ISetSystem(plParticleSystem *sys) { fSystem = sys; }
hsBool IUpdate(hsScalar delta);
void IUpdateParticles(hsScalar delta);
void IUpdateBoundsAndNormals(hsScalar delta);
void IRemoveParticle(UInt32 index);
};
#endif

View File

@ -0,0 +1,454 @@
/*==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 "hsStream.h"
#include "hsFastMath.h"
#include "hsUtils.h"
#include "plParticle.h"
#include "plParticleSystem.h"
#include "plParticleEmitter.h"
#include "plParticleGenerator.h"
#include "../CoreLib/hsColorRGBA.h"
#include "../plMessage/plParticleUpdateMsg.h"
#include "../plInterp/plController.h"
#include "hsResMgr.h"
#include "../plMath/plRandom.h"
static const hsScalar DEFAULT_INVERSE_MASS = 1.f;
static plRandom sRandom;
const void plParticleGenerator::ComputeDirection(float pitch, float yaw, hsVector3 &direction)
{
hsScalar cosPitch, sinPitch;
hsScalar cosYaw, sinYaw;
hsFastMath::SinCos(pitch, sinPitch, cosPitch);
hsFastMath::SinCos(yaw, sinYaw, cosYaw);
direction.Set(-sinYaw * cosPitch, sinPitch, cosPitch * cosYaw);
}
// Inverse function of ComputeDirection. Give it a normalized vector, and it will tell you a
// pitch and yaw (angles for the unit Z vector) to get there.
const void plParticleGenerator::ComputePitchYaw(float &pitch, float &yaw, const hsVector3 &dir)
{
const float PI = 3.14159f;
pitch = asin(dir.fY);
float cos_pitch = cos(pitch);
if (cos_pitch == 0)
{
yaw = 0;
return;
}
float inv = -dir.fX / cos_pitch;
if (inv > 1.0f)
inv = 1.0f;
if (inv < -1.0f)
inv = -1.0f;
yaw = asin(inv);
if (dir.fZ < 0)
yaw = PI - yaw;
}
plSimpleParticleGenerator::plSimpleParticleGenerator()
{
}
plSimpleParticleGenerator::~plSimpleParticleGenerator()
{
delete [] fInitPos;
delete [] fInitPitch;
delete [] fInitYaw;
}
void plSimpleParticleGenerator::Init(hsScalar genLife, hsScalar partLifeMin, hsScalar partLifeMax,
hsScalar particlesPerSecond, UInt32 numSources, hsPoint3 *initPos,
hsScalar *initPitch, hsScalar *initYaw, hsScalar angleRange,
hsScalar initVelMin, hsScalar initVelMax,
hsScalar xSize, hsScalar ySize,
hsScalar scaleMin, hsScalar scaleMax,
hsScalar massRange, hsScalar radsPerSecRange)
{
fGenLife = genLife;
fPartLifeMin = partLifeMin;
fPartLifeMax = partLifeMax;
fParticlesPerSecond = particlesPerSecond;
fNumSources = numSources;
fInitPos = initPos;
fInitPitch = initPitch;
fInitYaw = initYaw;
fAngleRange = angleRange;
fVelMin = initVelMin;
fVelMax = initVelMax;
fXSize = xSize;
fYSize = ySize;
fScaleMin = scaleMin;
fScaleMax = scaleMax;
fPartInvMassMin = 1.f / (DEFAULT_INVERSE_MASS + massRange);
fPartInvMassRange = 1.f / DEFAULT_INVERSE_MASS - fPartInvMassMin;
fPartRadsPerSecRange = radsPerSecRange;
fParticleSum = 0;
fMiscFlags = 0;
if (fGenLife < 0) fMiscFlags |= kImmortal;
}
hsBool plSimpleParticleGenerator::AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced /* = 0 */)
{
Int32 numNewParticles;
if (numForced == 0)
{
fGenLife -= dt;
if ((fGenLife < 0 && !(fMiscFlags & kImmortal)) || (fMiscFlags & kDisabled))
return true; // Leave it around so that a message can bring it back to life.
fParticleSum += fParticlesPerSecond * dt;
numNewParticles = (Int32)fParticleSum;
if (numNewParticles <= 0 || fParticlesPerSecond == 0)
return true;
}
else
{
numNewParticles = numForced;
}
UInt32 miscFlags = 0;
hsPoint3 currStart;
fParticleSum -= numNewParticles;
hsPoint3 orientation;
hsVector3 initDirection;
hsScalar vel = (fVelMax + fVelMin) * 0.5f;
hsScalar velRange = vel - fVelMin;
hsScalar initVelocity;
hsScalar initLife;
hsScalar life = (fPartLifeMax + fPartLifeMin) * 0.5f;
hsScalar lifeRange = life - fPartLifeMin;
hsScalar currSizeVar;
hsScalar scale = (fScaleMax + fScaleMin) * 0.5f;
hsScalar scaleRange = scale - fScaleMin;
hsScalar radsPerSec = 0;
UInt32 tile;
UInt32 sourceIndex;
const hsScalar lifeDiff = dt / numNewParticles;
hsScalar lifeSoFar;
int i;
for (i = 0, lifeSoFar = 0; i < numNewParticles; i++, lifeSoFar += lifeDiff)
{
initLife = life + lifeRange * sRandom.RandMinusOneToOne() - lifeSoFar;
// Careful here... if we're supposed to generate immortal particles, we do so
// by giving them a negative life. This is different that generating one with
// a positive lifetime that is now negative because of "lifeSoFar". The if is
// saying "if it's dead, but it was alive before we took away lifeSoFar, ignore it"
if (initLife <= 0 && initLife + lifeSoFar >= 0)
continue;
sourceIndex = (UInt32)(sRandom.RandZeroToOne() * fNumSources);
ComputeDirection(fInitPitch[sourceIndex] + fAngleRange * sRandom.RandMinusOneToOne(),
fInitYaw[sourceIndex] + fAngleRange * sRandom.RandMinusOneToOne(), initDirection);
initDirection = emitter->GetLocalToWorld() * initDirection;
initVelocity = (vel + velRange * sRandom.RandMinusOneToOne());
currStart = (emitter->GetLocalToWorld() * fInitPos[sourceIndex])
+ (initDirection * initVelocity * lifeSoFar) // Vo * t
+ (emitter->fSystem->fAccel * lifeSoFar * lifeSoFar); // at^2
if (emitter->fMiscFlags & emitter->kOrientationUp)
orientation.Set(0.0f, -1.0f, 0.0f);
else
orientation.Set(&initDirection);
tile = (UInt32)(sRandom.RandZeroToOne() * emitter->GetNumTiles());
currSizeVar = scale + scaleRange * sRandom.RandMinusOneToOne();
hsScalar invMass = fPartInvMassMin;
// Might be faster to just do the math instead of checking for zero...
if( fPartInvMassRange > 0 )
invMass += fPartInvMassRange * sRandom.RandZeroToOne();
if( fPartRadsPerSecRange > 0 )
radsPerSec = fPartRadsPerSecRange * sRandom.RandMinusOneToOne();
emitter->AddParticle(currStart, initDirection * initVelocity, tile, fXSize, fYSize, currSizeVar,
invMass, initLife, orientation, miscFlags, radsPerSec);
}
return true;
}
void plSimpleParticleGenerator::UpdateParam(UInt32 paramID, hsScalar paramValue)
{
switch (paramID)
{
case plParticleUpdateMsg::kParamParticlesPerSecond:
fParticlesPerSecond = paramValue;
break;
case plParticleUpdateMsg::kParamInitPitchRange:
case plParticleUpdateMsg::kParamInitYawRange:
fAngleRange = paramValue;
break;
// case plParticleUpdateMsg::kParamInitVel:
// fInitVel = paramValue;
// break;
// case plParticleUpdateMsg::kParamInitVelRange:
// fInitVelRange = paramValue;
// break;
case plParticleUpdateMsg::kParamVelMin:
fVelMin = paramValue;
break;
case plParticleUpdateMsg::kParamVelMax:
fVelMax = paramValue;
break;
case plParticleUpdateMsg::kParamXSize:
fXSize = paramValue;
break;
case plParticleUpdateMsg::kParamYSize:
fYSize = paramValue;
break;
// case plParticleUpdateMsg::kParamSizeRange:
// fSizeRange = paramValue;
// break;
case plParticleUpdateMsg::kParamScaleMin:
fScaleMin = paramValue;
break;
case plParticleUpdateMsg::kParamScaleMax:
fScaleMax = paramValue;
break;
case plParticleUpdateMsg::kParamGenLife:
fGenLife = paramValue;
if (fGenLife < 0)
fMiscFlags |= kImmortal;
else
fMiscFlags &= ~kImmortal;
break;
// case plParticleUpdateMsg::kParamPartLife:
// fPartLife = paramValue;
// if (fPartLife < 0)
// fPartLifeRange = 0;
// break;
// case plParticleUpdateMsg::kParamPartLifeRange:
// fPartLifeRange = paramValue;
// break;
case plParticleUpdateMsg::kParamPartLifeMin:
fPartLifeMin = paramValue;
break;
case plParticleUpdateMsg::kParamPartLifeMax:
fPartLifeMax = paramValue;
break;
case plParticleUpdateMsg::kParamEnabled:
if (paramValue == 0.f)
fMiscFlags |= kDisabled;
else
fMiscFlags &= ~kDisabled;
break;
default:
break;
}
}
void plSimpleParticleGenerator::Read(hsStream* s, hsResMgr *mgr)
{
hsScalar genLife = s->ReadSwapScalar();
hsScalar partLifeMin = s->ReadSwapScalar();
hsScalar partLifeMax = s->ReadSwapScalar();
hsScalar pps = s->ReadSwapScalar();
UInt32 numSources = s->ReadSwap32();
hsPoint3 *pos = TRACKED_NEW hsPoint3[numSources];
hsScalar *pitch = TRACKED_NEW hsScalar[numSources];
hsScalar *yaw = TRACKED_NEW hsScalar[numSources];
int i;
for (i = 0; i < numSources; i++)
{
pos[i].Read(s);
pitch[i] = s->ReadSwapScalar();
yaw[i] = s->ReadSwapScalar();
}
hsScalar angleRange = s->ReadSwapScalar();
hsScalar velMin = s->ReadSwapScalar();
hsScalar velMax = s->ReadSwapScalar();
hsScalar xSize = s->ReadSwapScalar();
hsScalar ySize = s->ReadSwapScalar();
hsScalar scaleMin = s->ReadSwapScalar();
hsScalar scaleMax = s->ReadSwapScalar();
hsScalar massRange = s->ReadSwapScalar();
hsScalar radsPerSec = s->ReadSwapScalar();
Init(genLife, partLifeMin, partLifeMax, pps, numSources, pos, pitch, yaw, angleRange, velMin, velMax,
xSize, ySize, scaleMin, scaleMax, massRange, radsPerSec);
}
void plSimpleParticleGenerator::Write(hsStream* s, hsResMgr *mgr)
{
s->WriteSwapScalar(fGenLife);
s->WriteSwapScalar(fPartLifeMin);
s->WriteSwapScalar(fPartLifeMax);
s->WriteSwapScalar(fParticlesPerSecond);
s->WriteSwap32(fNumSources);
int i;
for (i = 0; i < fNumSources; i++)
{
fInitPos[i].Write(s);
s->WriteSwapScalar(fInitPitch[i]);
s->WriteSwapScalar(fInitYaw[i]);
}
s->WriteSwapScalar(fAngleRange);
s->WriteSwapScalar(fVelMin);
s->WriteSwapScalar(fVelMax);
s->WriteSwapScalar(fXSize);
s->WriteSwapScalar(fYSize);
s->WriteSwapScalar(fScaleMin);
s->WriteSwapScalar(fScaleMax);
hsScalar massRange = 1.f / fPartInvMassMin - DEFAULT_INVERSE_MASS;
s->WriteSwapScalar(massRange);
s->WriteSwapScalar(fPartRadsPerSecRange);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
plOneTimeParticleGenerator::plOneTimeParticleGenerator()
{
}
plOneTimeParticleGenerator::~plOneTimeParticleGenerator()
{
delete [] fPosition;
delete [] fDirection;
}
void plOneTimeParticleGenerator::Init(hsScalar count, hsPoint3 *pointArray, hsVector3 *dirArray,
hsScalar xSize, hsScalar ySize, hsScalar scaleMin, hsScalar scaleMax, hsScalar radsPerSecRange)
{
fCount = count;
fPosition = pointArray;
fDirection = dirArray;
fXSize = xSize;
fYSize = ySize;
fScaleMin = scaleMin;
fScaleMax = scaleMax;
fPartRadsPerSecRange = radsPerSecRange;
}
// The numForced param is required by the parent class, but ignored by this particular generator
hsBool plOneTimeParticleGenerator::AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced /* = 0 */)
{
hsScalar currSizeVar;
hsScalar scale = (fScaleMax + fScaleMin) / 2;
hsScalar scaleRange = scale - fScaleMin;
hsScalar tile;
hsPoint3 currStart;
hsPoint3 orientation;
hsVector3 initDirection;
hsVector3 zeroVel(0.f, 0.f, 0.f);
hsScalar radsPerSec = 0;
int i;
for (i = 0; i < fCount; i++)
{
currStart = emitter->GetLocalToWorld() * fPosition[i];
initDirection = emitter->GetLocalToWorld() * fDirection[i];
if (emitter->fMiscFlags & emitter->kOrientationUp)
orientation.Set(0.0f, -1.0f, 0.0f);
else
orientation.Set(&initDirection);
tile = (hsScalar)(sRandom.Rand() % emitter->GetNumTiles());
currSizeVar = scale + scaleRange * sRandom.RandMinusOneToOne();
if( fPartRadsPerSecRange > 0 )
radsPerSec = fPartRadsPerSecRange * sRandom.RandMinusOneToOne();
emitter->AddParticle(currStart, zeroVel, (UInt32)tile, fXSize, fYSize, currSizeVar,
DEFAULT_INVERSE_MASS, -1, orientation, 0, radsPerSec);
}
emitter->fMiscFlags &= ~plParticleEmitter::kNeedsUpdate;
return false; // We've done our one-time job. Let the emitter know to delete us.
}
void plOneTimeParticleGenerator::Read(hsStream* s, hsResMgr *mgr)
{
UInt32 count = s->ReadSwap32();
hsScalar xSize = s->ReadSwapScalar();
hsScalar ySize = s->ReadSwapScalar();
hsScalar scaleMin = s->ReadSwapScalar();
hsScalar scaleMax = s->ReadSwapScalar();
hsScalar radsPerSecRange = s->ReadSwapScalar();
hsPoint3 *pos = TRACKED_NEW hsPoint3[count];
hsVector3 *dir = TRACKED_NEW hsVector3[count];
int i;
for (i = 0; i < count; i++)
{
pos[i].Read(s);
dir[i].Read(s);
}
Init((hsScalar)count, pos, dir, xSize, ySize, scaleMin, scaleMax, radsPerSecRange);
}
void plOneTimeParticleGenerator::Write(hsStream* s, hsResMgr *mgr)
{
s->WriteSwap32((UInt32)fCount);
s->WriteSwapScalar(fXSize);
s->WriteSwapScalar(fYSize);
s->WriteSwapScalar(fScaleMin);
s->WriteSwapScalar(fScaleMax);
s->WriteSwapScalar(fPartRadsPerSecRange);
int i;
for (i = 0; i < fCount; i++)
{
fPosition[i].Write(s);
fDirection[i].Write(s);
}
}

View File

@ -0,0 +1,148 @@
/*==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==*/
#ifndef plParticleGenerator_inc
#define plParticleGenerator_inc
#include "hsGeometry3.h"
#include "../pnNetCommon/plSynchedValue.h"
class plParticleEmitter;
class plScalarController;
// This class is responsible for all the details of automatically generating particles for a plParticleEmitter.
// It gets a change in time, and must do whatever necessary to generate the appropriate number of particles
// for that timespan
class plParticleGenerator : public plCreatable
{
public:
// returns false if it's done generating particles and is safe to delete.
virtual hsBool AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced = 0) = 0;
virtual void UpdateParam(UInt32 paramID, hsScalar paramValue) = 0;
CLASSNAME_REGISTER( plParticleGenerator );
GETINTERFACE_ANY( plParticleGenerator, plCreatable );
static const void ComputeDirection(float pitch, float yaw, hsVector3 &direction);
static const void ComputePitchYaw(float &pitch, float &yaw, const hsVector3 &dir);
static inline float GetRandomVar() { return 2.0f * (float)hsRand() / RAND_MAX - 1; } // returns a num between +/- 1.0
};
class plSimpleParticleGenerator : public plParticleGenerator
{
public:
plSimpleParticleGenerator();
~plSimpleParticleGenerator();
void Init(hsScalar genLife, hsScalar partLifeMin, hsScalar partLifeMax, hsScalar particlesPerSecond,
UInt32 numSources, hsPoint3 *pos, hsScalar *initPitch, hsScalar *initYaw, hsScalar angleRange,
hsScalar initVelMin, hsScalar initVelMax, hsScalar xSize, hsScalar ySize,
hsScalar scaleMin, hsScalar scaleMax,
hsScalar massRange, hsScalar radsPerSecRange);
CLASSNAME_REGISTER( plSimpleParticleGenerator );
GETINTERFACE_ANY( plSimpleParticleGenerator, plParticleGenerator);
virtual hsBool AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced);
virtual void UpdateParam(UInt32 paramID, hsScalar paramValue);
virtual void Read(hsStream* s, hsResMgr *mgr);
virtual void Write(hsStream* s, hsResMgr *mgr);
protected:
hsScalar fParticlesPerSecond;
UInt32 fNumSources;
hsPoint3 *fInitPos;
hsScalar *fInitPitch, *fInitYaw;
hsScalar fAngleRange;
hsScalar fVelMin, fVelMax;
hsScalar fXSize, fYSize, fScaleMin, fScaleMax;
hsScalar fGenLife; // How long shall we spit out particles from this location? When this time runs out, we stop
// spitting particles, but we don't actually die until all of our particles die naturally.
// (Even the ones that we feel are suffering needlessly.)
hsScalar fPartLifeMin; // lifespan for the particles we generate
hsScalar fPartLifeMax;
hsScalar fPartInvMassMin; // Doing a uniform variant over the inverse mass range (instead of over the mass range
hsScalar fPartInvMassRange; // and then inverting) will favor the low end of the mass range, but then again,
// it's just a freaking game. Note though that fPartInvMassMin == 1.f / massMAX.
hsScalar fPartRadsPerSecRange; // Zero means no rot, otherwise uniform random between [-range..range]
hsScalar fParticleSum;
enum
{
kImmortal = 0x1,
kDisabled = 0x2,
};
UInt32 fMiscFlags;
};
class plOneTimeParticleGenerator : public plParticleGenerator
{
public:
plOneTimeParticleGenerator();
~plOneTimeParticleGenerator();
void Init(hsScalar count, hsPoint3 *pointArray, hsVector3 *dirArray,
hsScalar xSize, hsScalar ySize, hsScalar scaleMin, hsScalar scaleMax, hsScalar radsPerSec);
CLASSNAME_REGISTER( plOneTimeParticleGenerator );
GETINTERFACE_ANY( plOneTimeParticleGenerator, plParticleGenerator);
virtual hsBool AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced = 0);
virtual void UpdateParam(UInt32 paramID, hsScalar paramValue) {}
virtual void Read(hsStream* s, hsResMgr *mgr);
virtual void Write(hsStream* s, hsResMgr *mgr);
protected:
hsScalar fCount;
hsPoint3 *fPosition;
hsVector3 *fDirection;
hsScalar fXSize, fYSize, fScaleMin, fScaleMax;
hsScalar fPartRadsPerSecRange; // Zero means no rot, otherwise uniform random between [-range..range]
};
#endif

View File

@ -0,0 +1,92 @@
/*==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 "plParticleSDLMod.h"
#include "plParticleSystem.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../plSDL/plSDL.h"
#include "../pnKeyedObject/plKey.h"
// static vars
char plParticleSDLMod::kStrNumParticles[]="numParticles";
void plParticleSDLMod::IPutCurrentStateIn(plStateDataRecord* dstState)
{
plSceneObject* sobj=GetTarget();
if (!sobj)
return;
UInt32 flags = sobj->GetKey()->GetUoid().GetLocation().GetFlags();
const plParticleSystem *sys = plParticleSystem::ConvertNoRef(sobj->GetModifierByType(plParticleSystem::Index()));
if (!sys)
return;
int num = sys->GetNumValidParticles(true);
dstState->FindVar(kStrNumParticles)->Set(num);
}
void plParticleSDLMod::ISetCurrentStateFrom(const plStateDataRecord* srcState)
{
plSceneObject* sobj=GetTarget();
if (!sobj)
return;
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(sobj->GetModifierByType(plParticleSystem::Index())));
if (!sys)
return;
int num;
srcState->FindVar(kStrNumParticles)->Get(&num);
if (num > sys->GetMaxTotalParticles())
num = sys->GetMaxTotalParticles();
sys->WipeExistingParticles();
sys->GenerateParticles(num);
}
UInt32 plParticleSDLMod::IApplyModFlags(UInt32 sendFlags)
{
if (fAttachedToAvatar)
return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState);
return sendFlags;
}

View File

@ -0,0 +1,76 @@
/*==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==*/
#ifndef plParticleSDLMod_inc
#define plParticleSDLMod_inc
#include "../plModifier/plSDLModifier.h"
//
// This modifier is responsible for sending and recving
// particle system state. Few need it. Little state is needed.
//
class plParticleSDLMod : public plSDLModifier
{
private:
bool fAttachedToAvatar;
protected:
void IPutCurrentStateIn(plStateDataRecord* dstState);
void ISetCurrentStateFrom(const plStateDataRecord* srcState);
UInt32 IApplyModFlags(UInt32 sendFlags);
public:
// var labels
static char kStrNumParticles[];
CLASSNAME_REGISTER( plParticleSDLMod );
GETINTERFACE_ANY( plParticleSDLMod, plSDLModifier);
plParticleSDLMod(bool attachedToAvatar = false): fAttachedToAvatar(attachedToAvatar) {}
void PutCurrentStateIn(plStateDataRecord* dstState);
const char* GetSDLName() const { return kSDLParticleSystem; }
void SetAttachedToAvatar(bool attached) {fAttachedToAvatar = attached;}
};
#endif // plParticleSDLMod_inc

View File

@ -0,0 +1,717 @@
/*==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 "plParticleSystem.h"
#include "plParticleEmitter.h"
#include "plParticleGenerator.h"
#include "plParticleEffect.h"
#include "plParticle.h"
#include "plParticleSDLMod.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnSceneObject/plDrawInterface.h"
#include "../pnSceneObject/plCoordinateInterface.h"
#include "../pnMessage/plTimeMsg.h"
#include "../plMessage/plRenderMsg.h"
#include "../plMessage/plAgeLoadedMsg.h"
#include "../plMessage/plParticleUpdateMsg.h"
#include "../plInterp/plController.h"
#include "../plSurface/hsGMaterial.h"
#include "plPipeline.h"
#include "hsTimer.h"
#include "plProfile.h"
#include "plTweak.h"
#include "../plDrawable/plParticleFiller.h"
plProfile_CreateCounter("Num Particles", "Particles", NumParticles);
const hsScalar plParticleSystem::GRAVITY_ACCEL_FEET_PER_SEC2 = 32.0f;
plParticleSystem::plParticleSystem() : fParticleSDLMod(nil), fAttachedToAvatar(false)
{
}
plParticleSystem::~plParticleSystem()
{
int i;
for (i = 0; i < fNumValidEmitters; i++)
{
delete fEmitters[i];
}
delete [] fEmitters;
delete fAmbientCtl;
delete fDiffuseCtl;
delete fOpacityCtl;
delete fWidthCtl;
delete fHeightCtl;
}
void plParticleSystem::Init(UInt32 xTiles, UInt32 yTiles, UInt32 maxTotalParticles, UInt32 maxEmitters,
plController *ambientCtl, plController *diffuseCtl, plController *opacityCtl,
plController *widthCtl, plController *heightCtl)
{
fTarget = nil;
fLastTime = 0;
fCurrTime = 0;
SetGravity(1.0f);
fDrag = 0;
fWindMult = 1.f;
fNumValidEmitters = 0;
fNextEmitterToGo = 0;
fMiscFlags = 0;
fForces.Reset();
fEffects.Reset();
fConstraints.Reset();
fXTiles = xTiles;
fYTiles = yTiles;
fMaxTotalParticles = fMaxTotalParticlesLeft = maxTotalParticles;
fMaxEmitters = maxEmitters;
fEmitters = TRACKED_NEW plParticleEmitter *[fMaxEmitters];
int i;
for (i = 0; i < maxEmitters; i++)
fEmitters[i] = nil;
fAmbientCtl = ambientCtl;
fDiffuseCtl = diffuseCtl;
fOpacityCtl = opacityCtl;
fWidthCtl = widthCtl;
fHeightCtl = heightCtl;
}
void plParticleSystem::IAddEffect(plParticleEffect *effect, UInt32 type)
{
switch(type)
{
case kEffectForce:
fForces.Append(effect);
break;
case kEffectMisc:
default:
fEffects.Append(effect);
break;
case kEffectConstraint:
fConstraints.Append(effect);
break;
}
}
plParticleEmitter* plParticleSystem::GetAvailEmitter()
{
if( !fNumValidEmitters ) // got to start with at least one.
return nil;
hsScalar minTTL = 1.e33;
int iMinTTL = -1;
int i;
for( i = 0; i < fNumValidEmitters; i++ )
{
if( fEmitters[i]->GetTimeToLive() < minTTL )
{
minTTL = fEmitters[i]->GetTimeToLive();
iMinTTL = i;
}
}
if( minTTL > 0 )
{
if( fNumValidEmitters < fMaxEmitters )
{
minTTL = 0;
iMinTTL = fNumValidEmitters++;
fEmitters[iMinTTL] = TRACKED_NEW plParticleEmitter();
fEmitters[iMinTTL]->Clone(fEmitters[0], iMinTTL);
fMaxTotalParticlesLeft -= fEmitters[iMinTTL]->fMaxParticles;
hsAssert(fMaxTotalParticlesLeft >= 0, "Should have planned better");
// Don't really use this. fEmitters[i]->GetSpanIndex() always == i.
fNextEmitterToGo = (fNextEmitterToGo + 1) % fMaxEmitters;
}
}
return fEmitters[iMinTTL];
}
UInt32 plParticleSystem::AddEmitter(UInt32 maxParticles, plParticleGenerator *gen, UInt32 emitterFlags)
{
if (fMaxEmitters == 0) // silly rabbit, Trix are for kids!
return 0;
UInt32 currEmitter;
if (fNumValidEmitters == fMaxEmitters) // No more free spots, snag the next in line.
{
int i;
for (i = 0; i < fMaxEmitters; i++)
{
if (fEmitters[i]->GetSpanIndex() == fNextEmitterToGo)
break;
}
currEmitter = i;
fMaxTotalParticlesLeft += fEmitters[currEmitter]->fMaxParticles;
hsAssert(fMaxTotalParticlesLeft <= fMaxTotalParticles, "Particle system somehow has more particles than it started with.");
delete fEmitters[currEmitter];
}
else
{
currEmitter = fNumValidEmitters;
fNumValidEmitters++;
}
if (maxParticles > fMaxTotalParticlesLeft)
maxParticles = fMaxTotalParticlesLeft;
if (maxParticles < 0)
maxParticles = 0;
fEmitters[currEmitter] = TRACKED_NEW plParticleEmitter();
fEmitters[currEmitter]->Init(this, maxParticles, fNextEmitterToGo, emitterFlags, gen);
fMaxTotalParticlesLeft -= fEmitters[currEmitter]->fMaxParticles;
fNextEmitterToGo = (fNextEmitterToGo + 1) % fMaxEmitters;
return maxParticles;
}
void plParticleSystem::AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex,
hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life,
hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec)
{
hsAssert(fNumValidEmitters > 0, "Trying to explicitly add particles to a system with no valid emitters.");
if (fNumValidEmitters == 0)
return;
fEmitters[0]->AddParticle(pos, velocity, tileIndex, hSize, vSize, scale, invMass, life, orientation, miscFlags, radsPerSec);
}
void plParticleSystem::GenerateParticles(UInt32 num, hsScalar dt /* = 0.f */)
{
if (num <= 0)
return;
if (fNumValidEmitters > 0 && fEmitters[0]->fGenerator)
fEmitters[0]->fGenerator->AddAutoParticles(fEmitters[0], dt, num);
GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0);
}
void plParticleSystem::WipeExistingParticles()
{
int i;
for (i = 0; i < fNumValidEmitters; i++)
fEmitters[i]->WipeExistingParticles();
}
void plParticleSystem::KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags)
{
if (fEmitters[0])
fEmitters[0]->KillParticles(num, timeToDie, flags);
GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0);
}
void plParticleSystem::TranslateAllParticles(hsPoint3 &amount)
{
int i;
for (i = 0; i < fNumValidEmitters; i++)
fEmitters[i]->TranslateAllParticles(amount);
}
void plParticleSystem::DisableGenerators()
{
int i;
for (i = 0; i < fNumValidEmitters; i++)
fEmitters[i]->UpdateGenerator(plParticleUpdateMsg::kParamEnabled, 0.f);
}
UInt16 plParticleSystem::StealParticlesFrom(plParticleSystem *victim, UInt16 num)
{
if (fNumValidEmitters <= 0)
return 0; // you just lose
if (victim)
{
UInt16 numStolen = fEmitters[0]->StealParticlesFrom(victim->fNumValidEmitters > 0 ? victim->fEmitters[0] : nil, num);
GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0);
victim->GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0);
return numStolen;
}
return 0;
}
plParticleGenerator *plParticleSystem::GetExportedGenerator() const
{
return (fNumValidEmitters > 0 ? fEmitters[0]->fGenerator : nil);
}
plParticleEffect *plParticleSystem::GetEffect(UInt16 type) const
{
int i;
for (i = 0; i < fForces.GetCount(); i++)
if (fForces[i]->ClassIndex() == type)
return fForces[i];
for (i = 0; i < fEffects.GetCount(); i++)
if (fEffects[i]->ClassIndex() == type)
return fEffects[i];
for (i = 0; i < fConstraints.GetCount(); i++)
if (fConstraints[i]->ClassIndex() == type)
return fConstraints[i];
return nil;
}
UInt32 plParticleSystem::GetNumValidParticles(hsBool immortalOnly /* = false */) const
{
UInt32 count = 0;
int i, j;
for (i = 0; i < fNumValidEmitters; i++)
{
if (immortalOnly)
{
for (j = 0; j < fEmitters[i]->fNumValidParticles; j++)
{
if (fEmitters[i]->fParticleExts[j].fMiscFlags & plParticleExt::kImmortal)
count++;
}
}
else
count += fEmitters[i]->fNumValidParticles;
}
return count;
}
const hsMatrix44 &plParticleSystem::GetLocalToWorld() const
{
return fTarget->GetCoordinateInterface()->GetLocalToWorld();
}
hsBool plParticleSystem::IEval(double secs, hsScalar del, UInt32 dirty)
{
return false;
}
hsBool plParticleSystem::IShouldUpdate(plPipeline* pipe) const
{
if (fMiscFlags & kParticleSystemAlwaysUpdate)
return true;
if (IGetTargetDrawInterface(0) && IGetTargetDrawInterface(0)->GetProperty(plDrawInterface::kDisable))
return false;
// First, what are the cumulative bounds for this system.
hsBounds3Ext wBnd;
wBnd.MakeEmpty();
int i;
for( i = 0; i < fNumValidEmitters; i++ )
{
if( fEmitters[i]->GetBoundingBox().GetType() == kBoundsNormal )
wBnd.Union(&fEmitters[i]->GetBoundingBox());
}
// Always update if we are currently empty
if( wBnd.GetType() == kBoundsEmpty )
{
return true;
}
// Now, are we visible?
hsBool isVisible = pipe->TestVisibleWorld(wBnd);
hsScalar delta = fLastTime > 0 ? hsScalar(fCurrTime - fLastTime) : hsTimer::GetDelSysSeconds();
if( isVisible )
{
// If we know how fast the fastest particle is moving, then we can
// decide if the system is too far away to need to update every single frame.
// In fact, based on the speed of the fastest particle, we can determine an
// exact update rate for a given error (say 3 pixels?).
// But we don't currently know the speed of the fastest particle, so
// until we do, I'm commenting this out. Most of the speed gain from
// culling particle updates was from not updating systems that aren't visible
// anyway.
#if 0 // ALWAYS_IF_VISIBLE
// We're in view, but how close are we to the camera? Look at closest point.
hsPoint2 depth;
wBnd.TestPlane(pipe->GetViewDirWorld(), depth);
hsScalar eyeDist = pipe->GetViewDirWorld().InnerProduct(pipe->GetViewPositionWorld());
hsScalar dist = depth.fX - eyeDist;
static hsScalar kUpdateCutoffDist = 100.f;
if( dist > kUpdateCutoffDist )
{
static hsScalar kDistantUpdateSecs = 0.1f;
return delta >= kDistantUpdateSecs;
}
#endif // ALWAYS_IF_VISIBLE
return true;
}
static hsScalar kOffscreenUpdateSecs = 1.f;
return delta >= kOffscreenUpdateSecs;
}
plDrawInterface* plParticleSystem::ICheckDrawInterface()
{
plDrawInterface* di = IGetTargetDrawInterface(0);
if( !di )
return nil;
if( di->GetDrawableMeshIndex(0) == UInt32(-1) )
{
di->SetUpForParticleSystem( fMaxEmitters + 1, fMaxTotalParticles, fTexture, fPermaLights );
hsAssert(di->GetDrawableMeshIndex( 0 ) != (UInt32)-1, "SetUpForParticleSystem should never fail"); // still invalid, didn't fix it.
}
return di;
}
void plParticleSystem::IHandleRenderMsg(plPipeline* pipe)
{
fCurrTime = hsTimer::GetSysSeconds();
hsScalar delta = hsScalar(fCurrTime - fLastTime);
if (delta == 0)
return;
plConst(hsScalar) kMaxDelta(0.3f);
if( delta > kMaxDelta )
delta = kMaxDelta;
plDrawInterface* di = ICheckDrawInterface();
if( !di )
return;
hsBool disabled = di->GetProperty(plDrawInterface::kDisable);
if (!IShouldUpdate(pipe))
{
if (disabled)
di->ResetParticleSystem(); // Need to call this, otherwise particles get drawn, even though the DI is disabled.
// (Yes, it's lame.)
// Otherwise, we leave the DI alone, and the particles draw in the same place they were last frame.
return;
}
fContext.fPipeline = pipe;
fContext.fSystem = this;
fContext.fSecs = fCurrTime;
fContext.fDelSecs = delta;
di->ResetParticleSystem();
if (fPreSim > 0)
IPreSim();
int i;
for (i = 0; i < fNumValidEmitters; i++)
{
fEmitters[i]->IUpdate(delta);
plProfile_IncCount(NumParticles, fEmitters[i]->fNumValidParticles);
if (!disabled)
{
if( fEmitters[ i ]->GetParticleCount() > 0 )
di->AssignEmitterToParticleSystem( fEmitters[ i ] ); // Go make those polys!
}
}
// plParticleFiller::FillParticlePolys(pipe, di);
fLastTime = fCurrTime;
}
#include "plProfile.h"
plProfile_CreateTimer("ParticleSys", "RenderSetup", ParticleSys);
hsBool plParticleSystem::MsgReceive(plMessage* msg)
{
plGenRefMsg* refMsg;
plParticleUpdateMsg *partMsg;
plParticleKillMsg *killMsg;
plSceneObject *scene;
plParticleEffect *effect;
hsGMaterial *mat;
plRenderMsg *rend;
plAgeLoadedMsg* ageLoaded;
if (rend = plRenderMsg::ConvertNoRef(msg))
{
plProfile_BeginLap(ParticleSys, this->GetKey()->GetUoid().GetObjectName());
IHandleRenderMsg(rend->Pipeline());
plProfile_EndLap(ParticleSys, this->GetKey()->GetUoid().GetObjectName());
return true;
}
else if (refMsg = plGenRefMsg::ConvertNoRef(msg))
{
if (scene = plSceneObject::ConvertNoRef(refMsg->GetRef()))
{
if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace))
AddTarget(scene);
else
RemoveTarget(scene);
return true;
}
if (mat = hsGMaterial::ConvertNoRef(refMsg->GetRef()))
{
if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace))
fTexture = mat;
else
fTexture = nil;
return true;
}
if (effect = plParticleEffect::ConvertNoRef(refMsg->GetRef()))
{
if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace))
IAddEffect(effect, refMsg->fType);
//else
// IRemoveEffect(effect, refMsg->fType);
return true;
}
}
else if (partMsg = plParticleUpdateMsg::ConvertNoRef(msg))
{
UpdateGenerator(partMsg->GetParamID(), partMsg->GetParamValue());
return true;
}
else if (killMsg = plParticleKillMsg::ConvertNoRef(msg))
{
KillParticles(killMsg->fNumToKill, killMsg->fTimeLeft, killMsg->fFlags);
return true;
}
else if( (ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg)) && ageLoaded->fLoaded )
{
ICheckDrawInterface();
return true;
}
return plModifier::MsgReceive(msg);
}
void plParticleSystem::UpdateGenerator(UInt32 paramID, hsScalar value)
{
int i;
for (i = 0; i < fNumValidEmitters; i++)
fEmitters[i]->UpdateGenerator(paramID, value);
}
void plParticleSystem::AddTarget(plSceneObject *so)
{
if (fTarget != nil)
RemoveTarget(fTarget);
fTarget = so;
plgDispatch::Dispatch()->RegisterForExactType(plTimeMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
delete fParticleSDLMod;
fParticleSDLMod = TRACKED_NEW plParticleSDLMod;
fParticleSDLMod->SetAttachedToAvatar(fAttachedToAvatar);
so->AddModifier(fParticleSDLMod);
}
void plParticleSystem::RemoveTarget(plSceneObject *so)
{
if (so == fTarget && so != nil)
{
if (fParticleSDLMod)
{
so->RemoveModifier(fParticleSDLMod);
delete fParticleSDLMod;
fParticleSDLMod = nil;
}
fTarget = nil;
}
}
// This can be done much faster, but it's only done on load, and very very clean as is. Saving optimization for
// when we observe that it's too slow.
void plParticleSystem::IPreSim()
{
const double PRESIM_UPDATE_TICK = 0.1;
int i;
for (i = 0; i < fNumValidEmitters; i++)
{
double secs = fPreSim;
while (secs > 0)
{
fEmitters[i]->IUpdateParticles((hsScalar)PRESIM_UPDATE_TICK);
secs -= PRESIM_UPDATE_TICK;
}
}
fPreSim = 0;
}
void plParticleSystem::IReadEffectsArray(hsTArray<plParticleEffect *> &effects, UInt32 type, hsStream *s, hsResMgr *mgr)
{
plGenRefMsg *msg;
effects.Reset();
UInt32 count = s->ReadSwap32();
int i;
for (i = 0; i < count; i++)
{
msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, (Int8)type);
mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
}
}
void plParticleSystem::Read(hsStream *s, hsResMgr *mgr)
{
plModifier::Read(s, mgr);
plGenRefMsg* msg;
msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0); // Material
mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
fAmbientCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
fDiffuseCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
fOpacityCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
fWidthCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
fHeightCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
UInt32 xTiles = s->ReadSwap32();
UInt32 yTiles = s->ReadSwap32();
UInt32 maxTotal = s->ReadSwap32();
UInt32 maxEmitters = s->ReadSwap32();
Init(xTiles, yTiles, maxTotal, maxEmitters, fAmbientCtl, fDiffuseCtl, fOpacityCtl, fWidthCtl, fHeightCtl);
fPreSim = s->ReadSwapScalar();
fAccel.Read(s);
fDrag = s->ReadSwapScalar();
fWindMult = s->ReadSwapScalar();
fNumValidEmitters = s->ReadSwap32();
int i;
for (i = 0; i < fNumValidEmitters; i++)
{
fEmitters[i] = plParticleEmitter::ConvertNoRef(mgr->ReadCreatable(s));
fEmitters[i]->ISetSystem(this);
}
IReadEffectsArray(fForces, kEffectForce, s, mgr);
IReadEffectsArray(fEffects, kEffectMisc, s, mgr);
IReadEffectsArray(fConstraints, kEffectConstraint, s, mgr);
int count = s->ReadSwap32();
fPermaLights.SetCount(count);
for( i = 0; i < count; i++ )
{
fPermaLights[i] = mgr->ReadKey(s);
}
}
void plParticleSystem::Write(hsStream *s, hsResMgr *mgr)
{
plModifier::Write(s, mgr);
int i;
mgr->WriteKey(s, fTexture);
// Arguments to the Init() function
mgr->WriteCreatable(s, fAmbientCtl);
mgr->WriteCreatable(s, fDiffuseCtl);
mgr->WriteCreatable(s, fOpacityCtl);
mgr->WriteCreatable(s, fWidthCtl);
mgr->WriteCreatable(s, fHeightCtl);
s->WriteSwap32(fXTiles);
s->WriteSwap32(fYTiles);
s->WriteSwap32(fMaxTotalParticles);
s->WriteSwap32(fMaxEmitters);
s->WriteSwapScalar(fPreSim);
fAccel.Write(s);
s->WriteSwapScalar(fDrag);
s->WriteSwapScalar(fWindMult);
s->WriteSwap32(fNumValidEmitters);
for (i = 0; i < fNumValidEmitters; i++)
{
mgr->WriteCreatable(s, fEmitters[i]);
}
int count;
count = fForces.GetCount();
s->WriteSwap32(count);
for (i = 0; i < count; i++)
mgr->WriteKey(s, fForces.Get(i));
count = fEffects.GetCount();
s->WriteSwap32(count);
for (i = 0; i < count; i++)
mgr->WriteKey(s, fEffects.Get(i));
count = fConstraints.GetCount();
s->WriteSwap32(count);
for (i = 0; i < count; i++)
mgr->WriteKey(s, fConstraints.Get(i));
count = fPermaLights.GetCount();
s->WriteSwap32(count);
for( i = 0; i < count; i++ )
mgr->WriteKey(s, fPermaLights[i]);
}
void plParticleSystem::SetAttachedToAvatar(bool attached)
{
fAttachedToAvatar = attached;
if (fParticleSDLMod)
fParticleSDLMod->SetAttachedToAvatar(attached);
}
void plParticleSystem::AddLight(plKey liKey)
{
fPermaLights.Append(liKey);
}

View File

@ -0,0 +1,201 @@
/*==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==*/
#ifndef plParticleSystem_inc
#define plParticleSystem_inc
#include "hsTemplates.h"
#include "hsGeometry3.h"
#include "../../NucleusLib/pnModifier/plModifier.h"
#include "../pnNetCommon/plSynchedValue.h"
#include "../CoreLib/hsColorRGBA.h"
#include "../CoreLib/hsMatrix44.h"
#include "plEffectTargetInfo.h"
class plPipeline;
class plParticle;
class plParticleGenerator;
class plSimpleParticleGenerator;
class plParticleEmitter;
class plParticleEffect;
class plParticleSDLMod;
class plController;
class hsGMaterial;
class plDrawableSpans;
class plDrawInterface;
class Mtl;
// This is responsible for creating and maintaining (allocation and update cycle) a related set of particles.
// Automatic particle generation is handled by the plParticleEmitters (one for each spawning location).
class plParticleSystem : public plModifier
{
friend plParticleEmitter;
friend plSimpleParticleGenerator;
protected:
static const hsScalar GRAVITY_ACCEL_FEET_PER_SEC2;
plSceneObject *fTarget;
hsGMaterial *fTexture; // One texture per system (Tiling is your friend!)
UInt32 fXTiles, fYTiles; // Width/height of the texture (in tiles) for determining a particle's UVs
double fCurrTime;
double fLastTime;
hsVector3 fAccel;
hsScalar fPreSim;
hsScalar fDrag;
hsScalar fWindMult;
bool fAttachedToAvatar;
UInt32 fMaxTotalParticles;
UInt32 fMaxTotalParticlesLeft;
UInt32 fNumValidEmitters;
UInt32 fMaxEmitters;
UInt32 fNextEmitterToGo;
plParticleEmitter **fEmitters; // Various locations we're emitting particles from (the first one is
// reserved for particles added explicitly (to keep all the bookkeeping
// in the hands of plParticleEmitter).
hsTArray<plParticleEffect *> fForces; // Global forces (wind/gravity/etc) that affect accelleration.
hsTArray<plParticleEffect *> fEffects; // Any other non-constraint effects.
hsTArray<plParticleEffect *> fConstraints; // Rigid body, collision, connectivity, etc.
plParticleContext fContext; // Rendering context passed to forces/effects/constraints.
hsTArray<plKey> fPermaLights; // Runtime lights assigned to this system. Currently don't support projected lights on particles.
// Material related animations, mapped over the course of a particle's life
plController *fAmbientCtl;
plController *fDiffuseCtl;
plController *fOpacityCtl;
plController *fWidthCtl;
plController *fHeightCtl;
plParticleSDLMod *fParticleSDLMod;
hsBool IShouldUpdate(plPipeline* pipe) const;
virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); // required by plModifier
void IHandleRenderMsg(plPipeline* pipe);
plDrawInterface* ICheckDrawInterface();
void IAddEffect(plParticleEffect *effect, UInt32 type);
void IReadEffectsArray(hsTArray<plParticleEffect *> &effects, UInt32 type, hsStream *s, hsResMgr *mgr);
void IPreSim();
public:
plParticleSystem();
virtual ~plParticleSystem();
void Init(UInt32 xTiles, UInt32 yTiles, UInt32 maxTotalParticles, UInt32 numEmitters,
plController *ambientCtl, plController *diffuseCtl, plController *opacityCtl,
plController *widthCtl, plController *heightCtl);
enum effectType
{
kEffectForce = 0x1,
kEffectMisc = 0x2,
kEffectConstraint = 0x4,
};
enum miscFlags
{
kParticleSystemAlwaysUpdate = 0x1
};
UInt8 fMiscFlags; // Not read/written (could be, but it's not needed yet.)
// There might not be enough particles available. this function returns the number of maxParticles that
// the emitter actually received.
UInt32 AddEmitter(UInt32 maxParticles, plParticleGenerator *gen, UInt32 emitterFlags);
plParticleEmitter* GetAvailEmitter();
CLASSNAME_REGISTER( plParticleSystem );
GETINTERFACE_ANY( plParticleSystem, plModifier);
// These are just public wrappers to the equivalent plParticleEmitter functions, provided for the purpose
// of adding particles to a system explicitly.
void AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex,
hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life,
hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec=0.f);
void GenerateParticles(UInt32 num, hsScalar dt = 0.f);
void WipeExistingParticles(); // Instant nuke
void KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags); // Sets a death timer. They'll get removed on the next update (if time has run out)
UInt16 StealParticlesFrom(plParticleSystem *victim, UInt16 num); // Returns the number of particles actually stolen
void TranslateAllParticles(hsPoint3 &amount); // Used to recenter the system when linking between ages.
void DisableGenerators();
UInt32 GetNumTiles() const { return fXTiles * fYTiles; }
void SetTileIndex(plParticle &particle, UInt32 index); // Sets the UV coordinates appropriate for the current texture
UInt32 GetNumValidParticles(hsBool immortalOnly = false) const; // Takes a bit longer if we want a count of immortal particles...
UInt32 GetMaxTotalParticles() const { return fMaxTotalParticles; }
const hsMatrix44 &GetLocalToWorld() const;
void SetAccel(const hsVector3& a) { fAccel = GRAVITY_ACCEL_FEET_PER_SEC2 * a; }
void SetGravity(hsScalar pct) { fAccel.Set(0, 0, -GRAVITY_ACCEL_FEET_PER_SEC2 * pct); }
void SetDrag(hsScalar d) { fDrag = -d; }
void SetWindMult(hsScalar m) { fWindMult = m; }
void SetPreSim(hsScalar time) { fPreSim = time; }
void UpdateGenerator(UInt32 paramID, hsScalar value);
plParticleGenerator *GetExportedGenerator() const;
const hsVector3& GetAccel() const { return fAccel; }
hsScalar GetDrag() const { return fDrag; }
hsScalar GetWindMult() const { return fWindMult; }
plParticleEffect *GetEffect(UInt16 type) const;
plParticleSDLMod* GetSDLMod() {return fParticleSDLMod;}
// Functions related to/required by plModifier
virtual int GetNumTargets() const { return fTarget ? 1 : 0; }
virtual plSceneObject* GetTarget(int w) const { hsAssert(w < 1, "Bad target"); return fTarget; }
virtual void AddTarget(plSceneObject* so);
virtual void RemoveTarget(plSceneObject* so);
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
virtual hsBool MsgReceive(plMessage* msg);
void SetAttachedToAvatar(bool attached);
// Export only functions for building the system. Not supported at runtime.
// AddLight allows the particle system to remain in ignorant bliss about runtime lights
void AddLight(plKey liKey);
};
#endif