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