123
This commit is contained in:
257
CryAnimation/CryCharParticleManager.cpp
Normal file
257
CryAnimation/CryCharParticleManager.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "stdafx.h"
|
||||
#include "drand.h"
|
||||
#include "MathUtils.h"
|
||||
#include "CryCharParticleManager.h"
|
||||
#include "CVars.h"
|
||||
|
||||
CryCharParticleManager::CryCharParticleManager():
|
||||
m_numActive (0),
|
||||
m_nLastFrame (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// adds a particle spawn task, returns a handle to be used to
|
||||
int CryCharParticleManager::add (const ParticleParams& rParticleInfo, const CryParticleSpawnInfo& rSpawnInfo)
|
||||
{
|
||||
validateThis();
|
||||
// only 1 emitter is supported
|
||||
unsigned nHandle = 0;
|
||||
while (nHandle < m_arrEmitters.size() && m_arrEmitters[nHandle].m_bActive)
|
||||
++nHandle;
|
||||
|
||||
if (nHandle >= m_arrEmitters.size())
|
||||
m_arrEmitters.resize(nHandle + 1);
|
||||
m_arrEmitters[nHandle].m_ParticleInfo = rParticleInfo;
|
||||
m_arrEmitters[nHandle].m_SpawnInfo = rSpawnInfo;
|
||||
m_arrEmitters[nHandle].m_bActive = true;
|
||||
|
||||
++m_numActive;
|
||||
validateThis();
|
||||
|
||||
return nHandle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// deletes a particle spawn task by the handle
|
||||
bool CryCharParticleManager::remove (int nHandle)
|
||||
{
|
||||
validateThis();
|
||||
if (nHandle == -1)
|
||||
{
|
||||
bool bResult = m_arrEmitters.empty();
|
||||
m_arrEmitters.clear();
|
||||
m_numActive = 0;
|
||||
return bResult;
|
||||
}
|
||||
|
||||
if (nHandle >= (int)m_arrEmitters.size() || nHandle < 0)
|
||||
return false;
|
||||
|
||||
if (!m_arrEmitters[nHandle].m_bActive)
|
||||
return false; // already deactivated
|
||||
|
||||
m_arrEmitters[nHandle].m_bActive = false;
|
||||
|
||||
size_t nSize = m_arrEmitters.size();
|
||||
while (nSize > 0 && !m_arrEmitters[nSize-1].m_bActive)
|
||||
--nSize;
|
||||
m_arrEmitters.resize (nSize);
|
||||
|
||||
--m_numActive;
|
||||
validateThis();
|
||||
|
||||
if (empty())
|
||||
m_nLastFrame = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if there are no emitters
|
||||
bool CryCharParticleManager::empty() const
|
||||
{
|
||||
return m_arrEmitters.empty();
|
||||
}
|
||||
|
||||
|
||||
// spawn the particles (using the external tangent info and mapping)
|
||||
void CryCharParticleManager::spawn (const SpawnParams& params)
|
||||
{
|
||||
float fTime = g_GetTimer()->GetCurrTime();
|
||||
if (g_nFrameID == m_nLastFrame)
|
||||
return;
|
||||
m_nLastFrame = g_nFrameID;
|
||||
|
||||
float fDeltaTime = g_GetTimer()->GetFrameTime();
|
||||
|
||||
for (unsigned i = 0; i < m_arrEmitters.size(); ++i)
|
||||
{
|
||||
Emitter& emitter = m_arrEmitters[i];
|
||||
if (emitter.m_bActive)
|
||||
{
|
||||
emitter.spawn (params, fDeltaTime);
|
||||
if (emitter.m_SpawnInfo.nFlags & CryParticleSpawnInfo::FLAGS_ONE_TIME_SPAWN)
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// evaluates if a vertex with such base is valid for spawning a particle
|
||||
bool CryCharParticleManager::Emitter::isValid (const SPipTangents& rBase)
|
||||
{
|
||||
if (!(m_SpawnInfo.nFlags & CryParticleSpawnInfo::FLAGS_RAIN_MODE))
|
||||
return true;
|
||||
|
||||
return (rBase.m_TNormal * m_ParticleInfo.vGravity < 0);
|
||||
}
|
||||
|
||||
|
||||
void CryCharParticleManager::Emitter::spawnFromBone(const SpawnParams& params)
|
||||
{
|
||||
// both position and normal (and later the gravity/wind direction) are in WCS
|
||||
|
||||
ParticleParams Particle = m_ParticleInfo;
|
||||
Particle.vPosition = m_SpawnInfo.vBonePos;
|
||||
|
||||
if ((unsigned)m_SpawnInfo.nBone < params.numBoneMatrices)
|
||||
{
|
||||
const Matrix44& mxBone = params.pBoneGlobalMatrices[m_SpawnInfo.nBone];
|
||||
Particle.vPosition = mxBone.TransformPointOLD(Particle.vPosition);
|
||||
Particle.vDirection = mxBone.TransformVectorOLD(Particle.vDirection);
|
||||
}
|
||||
|
||||
Particle.vPosition = params.pModelMatrix->TransformPointOLD (Particle.vPosition);
|
||||
Particle.vDirection = params.pModelMatrix->TransformVectorOLD (Particle.vDirection);
|
||||
|
||||
/*
|
||||
float fPitchCos = (float)(drand()*2-1);
|
||||
float fPitchSin = (float)sqrt(1-fPitchCos*fPitchCos);
|
||||
float fYaw = (float)(drand() * 2 * gPi);
|
||||
struct {float fCos, fSin;} Yaw;
|
||||
CosSin (fYaw, &Yaw.fCos);
|
||||
|
||||
m_ParticleInfo.vDirection.x = Yaw.fSin * fPitchSin;
|
||||
m_ParticleInfo.vDirection.y = fPitchCos;
|
||||
m_ParticleInfo.vDirection.z = Yaw.fCos * fPitchSin;
|
||||
*/
|
||||
|
||||
Get3DEngine()->SpawnParticles(Particle);
|
||||
}
|
||||
|
||||
// spawns one particle from the skin
|
||||
void CryCharParticleManager::Emitter::spawnFromSkin(const SpawnParams& params)
|
||||
{
|
||||
// find the face that's ok for spawning the particle
|
||||
Vec3 arrFace[2][4]; // the first [0..2] are the vertices of the face, the [3] one is the normal
|
||||
Vec3* pBestFace = arrFace[0], *pTempFace = arrFace[1];
|
||||
|
||||
Vec3 vWind = m_ParticleInfo.vGravity;
|
||||
|
||||
if (m_SpawnInfo.nFlags & m_SpawnInfo.FLAGS_RAIN_MODE)
|
||||
{
|
||||
float fBestBet; // minimize this dot product of gravity and normal
|
||||
Vec3 vWindLCS = UntransformVector (*params.pModelMatrix, vWind); // gravity in the LCS of the character
|
||||
|
||||
int nRainPower = g_GetCVars()->ca_RainPower_Clamp (3,40);
|
||||
|
||||
// attempt #0
|
||||
params.getFaceVN (irand() % params.numFaces, pBestFace);
|
||||
fBestBet = vWindLCS * pTempFace[3];
|
||||
|
||||
for (int nAttempt = 1; nAttempt < nRainPower; ++nAttempt)
|
||||
{
|
||||
params.getFaceVN(irand() % params.numFaces, pTempFace);
|
||||
|
||||
float fContraWind = vWindLCS * pTempFace[3];
|
||||
if (fContraWind < fBestBet)
|
||||
{
|
||||
fBestBet = fContraWind;
|
||||
std::swap (pBestFace, pTempFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
params.getFaceVN (irand() % params.numFaces, pBestFace);
|
||||
}
|
||||
|
||||
// get random point (in barycentric coordinates) on the triangle
|
||||
Vec3 vBR ((float)drand()+0.001f,(float)drand(),(float)drand());
|
||||
vBR /= vBR.x+vBR.y+vBR.z;
|
||||
Vec3 vSpawnPoint = vBR.x * pBestFace[0] + vBR.y * pBestFace[1] + vBR.z * pBestFace[2];
|
||||
|
||||
// both position and normal (and later the gravity/wind direction) are in WCS
|
||||
m_ParticleInfo.vPosition = params.pModelMatrix->TransformPointOLD (vSpawnPoint);
|
||||
|
||||
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
|
||||
Vec3 vNormal = params.pModelMatrix->TransformVectorOLD(pBestFace[3]);
|
||||
|
||||
// we've found something appropriate
|
||||
if (m_SpawnInfo.nFlags & m_SpawnInfo.FLAGS_RAIN_MODE)
|
||||
{
|
||||
m_ParticleInfo.vDirection =
|
||||
(2*(vNormal*vWind))*vNormal - vWind
|
||||
//vNormal
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParticleInfo.vDirection = vNormal;
|
||||
}
|
||||
|
||||
Get3DEngine()->SpawnParticles(m_ParticleInfo);
|
||||
}
|
||||
|
||||
// spawn the particles (using the external tangent info and mapping)
|
||||
void CryCharParticleManager::Emitter::spawn (const SpawnParams& params, float fDeltaTime)
|
||||
{
|
||||
if (!m_bActive)
|
||||
return;
|
||||
for (m_fParticleAccumulator += fDeltaTime * m_SpawnInfo.fSpawnRate; m_fParticleAccumulator > 1; m_fParticleAccumulator -= 1)
|
||||
{
|
||||
spawnSingleParticle(params);
|
||||
}
|
||||
|
||||
if (m_fParticleAccumulator >= 0)
|
||||
{
|
||||
// dither the particles by time: if there are 1.9 particles to be spawned, the 1st particle is spawned and
|
||||
// the second is spawned in 90% of cases. If this emitter is re-added on the next frame anew, this will look in
|
||||
// the average as good as if it's here all the time. If it's kept, then the next frame it'll be spawned with less probability
|
||||
// (because of negative accumulator number)
|
||||
if (drand () < m_fParticleAccumulator)
|
||||
{
|
||||
m_fParticleAccumulator -= 1;
|
||||
spawnSingleParticle (params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spawns only one particle with the params
|
||||
void CryCharParticleManager::Emitter::spawnSingleParticle (const SpawnParams& params)
|
||||
{
|
||||
if (m_SpawnInfo.nFlags & CryParticleSpawnInfo::FLAGS_SPAWN_FROM_BONE)
|
||||
spawnFromBone (params);
|
||||
else
|
||||
spawnFromSkin(params);
|
||||
}
|
||||
|
||||
void CryCharParticleManager::validateThis()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// check the coherence of the number of active emitters
|
||||
unsigned numActiveEmitters = 0;
|
||||
for (unsigned i = 0; i < m_arrEmitters.size(); ++i)
|
||||
if (m_arrEmitters[i].m_bActive)
|
||||
++numActiveEmitters;
|
||||
assert (numActiveEmitters == m_numActive);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CryCharParticleManager::GetMemoryUsage (ICrySizer* pSizer)
|
||||
{
|
||||
size_t nSize = sizeof(*this);
|
||||
nSize += m_arrEmitters.size() * sizeof(Emitter);
|
||||
pSizer->AddObject(this, nSize);
|
||||
}
|
||||
Reference in New Issue
Block a user