Files
scummvm-cursorfix/engines/hpl1/engine/graphics/ParticleEmitter3D_UserData.cpp
2026-02-02 04:50:13 +01:00

1121 lines
36 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of HPL1 Engine.
*/
// NEW
#include "hpl1/engine/graphics/Graphics.h"
// ---
#include "hpl1/engine/graphics/ParticleEmitter3D_UserData.h"
//#include "hpl1/engine/graphics/Mesh.h"
//#include "hpl1/engine/graphics/SubMesh.h"
#include "hpl1/engine/graphics/ParticleSystem3D.h"
#include "hpl1/engine/graphics/RenderList.h"
#include "hpl1/engine/impl/tinyXML/tinyxml.h"
#include "hpl1/engine/resources/MaterialManager.h"
#include "hpl1/engine/resources/Resources.h"
//#include "hpl1/engine/resources/MeshManager.h"
#include "hpl1/engine/physics/PhysicsBody.h"
#include "hpl1/engine/physics/PhysicsWorld.h"
#include "hpl1/engine/scene/World3D.h"
#include "hpl1/engine/math/Math.h"
namespace hpl {
//////////////////////////////////////////////////////////////////////////
// LOADER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cParticleEmitterData3D_UserData::cParticleEmitterData3D_UserData(
const tString &asName, cResources *apResources, cGraphics *apGraphics)
: iParticleEmitterData(asName, apResources, apGraphics) {
}
//-----------------------------------------------------------------------
iParticleEmitter *cParticleEmitterData3D_UserData::Create(tString asName, cVector3f avSize) {
iParticleEmitter3D *pPE = hplNew(cParticleEmitter3D_UserData, (asName, &mvMaterials, avSize,
mpGraphics, mpResources, this));
cMatrixf mtxOffset = cMath::MatrixRotate(mvAngleOffset, eEulerRotationOrder_XYZ);
mtxOffset.SetTranslation(mvPosOffset);
pPE->SetMatrix(mtxOffset);
return pPE;
}
//////////////////////////
// NEW
static ePEType GetPEType(const char *apString) {
if (apString == NULL)
return ePEType_Normal;
tString sType = cString::ToLowerCase(apString);
if (sType == "beam")
return ePEType_Beam;
else if (sType == "normal")
return ePEType_Normal;
return ePEType_Normal;
}
// ---
/////////////////////////
static ePEStartPosType GetStartPosType(const char *apString) {
if (apString == NULL)
return ePEStartPosType_Box;
tString sType = cString::ToLowerCase(apString);
if (sType == "box")
return ePEStartPosType_Box;
else if (sType == "sphere")
return ePEStartPosType_Sphere;
return ePEStartPosType_Box;
}
/////////////////////////
static eParticleEmitter3DCoordSystem GetCoordSystem(const char *apString) {
if (apString == NULL)
return eParticleEmitter3DCoordSystem_World;
tString sType = cString::ToLowerCase(apString);
if (sType == "world")
return eParticleEmitter3DCoordSystem_World;
else if (sType == "local")
return eParticleEmitter3DCoordSystem_Local;
return eParticleEmitter3DCoordSystem_World;
}
/////////////////////////
static ePEDeathType GetDeathType(const char *apString) {
if (apString == NULL)
return ePEDeathType_Age;
tString sType = cString::ToLowerCase(apString);
if (sType == "age")
return ePEDeathType_Age;
return ePEDeathType_Age;
}
/////////////////////////
static eParticleEmitter3DType GetDrawType(const char *apString) {
if (apString == NULL)
return eParticleEmitter3DType_DynamicPoint;
tString sType = cString::ToLowerCase(apString);
if (sType == "point")
return eParticleEmitter3DType_DynamicPoint;
else if (sType == "line")
return eParticleEmitter3DType_Line;
else if (sType == "axis")
return eParticleEmitter3DType_Axis;
return eParticleEmitter3DType_DynamicPoint;
}
/////////////////////////
static ePEGravityType GetGravityType(const char *apString) {
if (apString == NULL)
return ePEGravityType_None;
tString sType = cString::ToLowerCase(apString);
if (sType == "none")
return ePEGravityType_None;
else if (sType == "vector")
return ePEGravityType_Vector;
else if (sType == "center")
return ePEGravityType_Center;
return ePEGravityType_None;
}
/////////////////////////
static ePESubDivType GetSubDivType(const char *apString) {
if (apString == NULL)
return ePESubDivType_Random;
tString sType = cString::ToLowerCase(apString);
if (sType == "random")
return ePESubDivType_Random;
else if (sType == "animation")
return ePESubDivType_Animation;
return ePESubDivType_Random;
}
////////////////////////
/// NEW
static ePEPartSpinType GetPartSpinType(const char *apString) {
if (apString == NULL)
return ePEPartSpinType_Constant;
tString sType = cString::ToLowerCase(apString);
if (sType == "constant")
return ePEPartSpinType_Constant;
else if (sType == "movement")
return ePEPartSpinType_Movement;
return ePEPartSpinType_Constant;
}
/// ---
/////////////////////////
void cParticleEmitterData3D_UserData::LoadFromElement(TiXmlElement *apElement) {
///////// GENERAL /////////////
msName = cString::ToString(apElement->Attribute("Name"), "");
// NEW
mPEType = GetPEType(apElement->Attribute("PEType"));
// ---
mlMaxParticleNum = cString::ToInt(apElement->Attribute("MaxParticleNum"), 1);
mbRespawn = cString::ToBool(apElement->Attribute("Respawn"), false);
mfParticlesPerSecond = cString::ToFloat(apElement->Attribute("ParticlesPerSecond"), 1);
mfStartTimeOffset = cString::ToFloat(apElement->Attribute("StartTimeOffset"), 0);
mfWarmUpTime = cString::ToFloat(apElement->Attribute("WarmUpTime"), 0);
mfWarmUpStepsPerSec = cString::ToFloat(apElement->Attribute("WarmUpStepsPerSec"), 60);
mfMinPauseLength = cString::ToFloat(apElement->Attribute("MinPauseLength"), 0);
mfMaxPauseLength = cString::ToFloat(apElement->Attribute("MaxPauseLength"), 0);
mfMinPauseInterval = cString::ToFloat(apElement->Attribute("MinPauseInterval"), 0);
mfMaxPauseInterval = cString::ToFloat(apElement->Attribute("MaxPauseInterval"), 0);
mvPosOffset = cString::ToVector3f(apElement->Attribute("PosOffset"), 0);
mvAngleOffset = cString::ToVector3f(apElement->Attribute("AngleOffset"), 0);
mvAngleOffset.x = cMath::ToRad(mvAngleOffset.x);
mvAngleOffset.y = cMath::ToRad(mvAngleOffset.y);
mvAngleOffset.z = cMath::ToRad(mvAngleOffset.z);
/////////// MATERIAL ////////
int lMaterialNum = cString::ToInt(apElement->Attribute("MaterialNum"), 1);
float fAnimationLength = cString::ToFloat(apElement->Attribute("AnimationLength"), 1);
tString sMaterial = cString::ToString(apElement->Attribute("Material"), "");
mvSubDiv = cString::ToVector2l(apElement->Attribute("SubDiv"), 1);
mSubDivType = GetSubDivType(apElement->Attribute("SubDivType"));
if (lMaterialNum <= 1) {
sMaterial = cString::SetFileExt(sMaterial, "mat");
iMaterial *pMaterial = mpResources->GetMaterialManager()->CreateMaterial(sMaterial);
if (pMaterial)
mvMaterials.push_back(pMaterial);
} else {
for (int i = 1; i < lMaterialNum + 1; ++i) {
tString sFileName;
if (i > 9)
sFileName = sMaterial + cString::ToString(i);
else
sFileName = sMaterial + "0" + cString::ToString(i);
sFileName = cString::SetFileExt(sFileName, "mat");
iMaterial *pMaterial = mpResources->GetMaterialManager()->CreateMaterial(sFileName);
if (pMaterial)
mvMaterials.push_back(pMaterial);
}
}
mfFrameStep = 1 / (fAnimationLength / (float)mvMaterials.size());
mfMaxFrameTime = ((float)mvMaterials.size()) - 0.0001f;
///////// START POS //////////
mStartPosType = GetStartPosType(apElement->Attribute("StartPosType"));
mvMinStartPos = cString::ToVector3f(apElement->Attribute("MinStartPos"), 0);
mvMaxStartPos = cString::ToVector3f(apElement->Attribute("MaxStartPos"), 0);
mvMinStartAngles = cString::ToVector2f(apElement->Attribute("MinStartAngles"), 0);
mvMaxStartAngles = cString::ToVector2f(apElement->Attribute("MaxStartAngles"), 0);
mvMinStartAngles.x = cMath::ToRad(mvMinStartAngles.x);
mvMinStartAngles.y = cMath::ToRad(mvMinStartAngles.y);
mvMaxStartAngles.x = cMath::ToRad(mvMaxStartAngles.x);
mvMaxStartAngles.y = cMath::ToRad(mvMaxStartAngles.y);
mfMinStartRadius = cString::ToFloat(apElement->Attribute("MinStartRadius"), 0);
mfMaxStartRadius = cString::ToFloat(apElement->Attribute("MaxStartRadius"), 0);
// NEW
// cMesh *mshTempMesh = mpResources->GetMeshManager()->CreateMesh("character_roach.dae");
// mVBMeshData = mshTempMesh->GetSubMeshName("polySurfaceShape38")->GetVertexBuffer();
// mpfMeshVtxData = mVBMeshData->GetArray(eVertexFlag_Position);
// ---
/////////// MOVEMENT ////////
mStartVelType = GetStartPosType(apElement->Attribute("StartVelType"));
mvMinStartVel = cString::ToVector3f(apElement->Attribute("MinStartVel"), 0);
mvMaxStartVel = cString::ToVector3f(apElement->Attribute("MaxStartVel"), 0);
mvMinStartVelAngles = cString::ToVector2f(apElement->Attribute("MinStartVelAngles"), 0);
mvMaxStartVelAngles = cString::ToVector2f(apElement->Attribute("MaxStartVelAngles"), 0);
mvMinStartVelAngles.x = cMath::ToRad(mvMinStartVelAngles.x);
mvMinStartVelAngles.y = cMath::ToRad(mvMinStartVelAngles.y);
mvMaxStartVelAngles.x = cMath::ToRad(mvMaxStartVelAngles.x);
mvMaxStartVelAngles.y = cMath::ToRad(mvMaxStartVelAngles.y);
mfMinStartVelSpeed = cString::ToFloat(apElement->Attribute("MinStartVelSpeed"), 0);
mfMaxStartVelSpeed = cString::ToFloat(apElement->Attribute("MaxStartVelSpeed"), 0);
mfMinSpeedMultiply = cString::ToFloat(apElement->Attribute("MinSpeedMultiply"), 0);
mfMaxSpeedMultiply = cString::ToFloat(apElement->Attribute("MaxSpeedMultiply"), 0);
mvMinStartAcc = cString::ToVector3f(apElement->Attribute("MinStartAcc"), 0);
mvMaxStartAcc = cString::ToVector3f(apElement->Attribute("MaxStartAcc"), 0);
mfMinVelMaximum = cString::ToFloat(apElement->Attribute("MinVelMaximum"), 0);
mfMaxVelMaximum = cString::ToFloat(apElement->Attribute("MaxVelMaximum"), 0);
mbUsesDirection = cString::ToBool(apElement->Attribute("UsesDirection"), false);
mGravityType = GetGravityType(apElement->Attribute("GravityType"));
mvGravityAcc = cString::ToVector3f(apElement->Attribute("GravityAcc"), 0);
mCoordSystem = GetCoordSystem(apElement->Attribute("CoordSystem"));
// NEW
mbUsePartSpin = cString::ToBool(apElement->Attribute("UsePartSpin"), false);
mPartSpinType = GetPartSpinType(apElement->Attribute("PartSpinType"));
mfMinSpinRange = cString::ToFloat(apElement->Attribute("MinSpinRange"), 0);
mfMaxSpinRange = cString::ToFloat(apElement->Attribute("MaxSpinRange"), 0);
mbUseRevolution = cString::ToBool(apElement->Attribute("UseRevolution"), false);
mvMinRevVel = cString::ToVector3f(apElement->Attribute("MinRevVel"), 0);
mvMaxRevVel = cString::ToVector3f(apElement->Attribute("MaxRevVel"), 0);
// ---
/////////// LIFESPAN ////////
mfMinLifeSpan = cString::ToFloat(apElement->Attribute("MinLifeSpan"), 0);
mfMaxLifeSpan = cString::ToFloat(apElement->Attribute("MaxLifeSpan"), 0);
mDeathType = GetDeathType(apElement->Attribute("DeathType"));
msDeathPS = cString::ToString(apElement->Attribute("DeathPS"), "");
/////////// RENDERING ////////
mDrawType = GetDrawType(apElement->Attribute("DrawType"));
// eParticleEmitter3DType Heading
// cVector2l subdivisions.
mvMinStartSize = cString::ToVector2f(apElement->Attribute("MinStartSize"), 1);
mvMaxStartSize = cString::ToVector2f(apElement->Attribute("MaxStartSize"), 1);
mfStartRelSize = cString::ToFloat(apElement->Attribute("StartRelSize"), 0);
mfMiddleRelSize = cString::ToFloat(apElement->Attribute("MiddleRelSize"), 0);
mfMiddleRelSizeTime = cString::ToFloat(apElement->Attribute("MiddleRelSizeTime"), 0);
mfMiddleRelSizeLength = cString::ToFloat(apElement->Attribute("MiddleRelSizeLength"), 0);
mfEndRelSize = cString::ToFloat(apElement->Attribute("EndRelSize"), 0);
mbMultiplyRGBWithAlpha = cString::ToBool(apElement->Attribute("MultiplyRGBWithAlpha"), false);
/////////// COLOR ////////
mMinStartColor = cString::ToColor(apElement->Attribute("MinStartColor"), cColor(1, 1));
mMaxStartColor = cString::ToColor(apElement->Attribute("MaxStartColor"), cColor(1, 1));
mStartRelColor = cString::ToColor(apElement->Attribute("StartRelColor"), cColor(1, 1));
mMiddleRelColor = cString::ToColor(apElement->Attribute("MiddleRelColor"), cColor(1, 1));
mfMiddleRelColorTime = cString::ToFloat(apElement->Attribute("MiddleRelColorTime"), 0);
mfMiddleRelColorLength = cString::ToFloat(apElement->Attribute("MiddleRelColorLength"), 0);
mEndRelColor = cString::ToColor(apElement->Attribute("EndRelColor"), cColor(1, 1));
/////////// COLLISION ////////
mbCollides = cString::ToBool(apElement->Attribute("Collides"), false);
mfMinBounceAmount = cString::ToFloat(apElement->Attribute("MinBounceAmount"), 0);
mfMaxBounceAmount = cString::ToFloat(apElement->Attribute("MaxBounceAmount"), 0);
mlMinCollisionMax = cString::ToInt(apElement->Attribute("MinCollisionMax"), 0);
mlMaxCollisionMax = cString::ToInt(apElement->Attribute("MaxCollisionMax"), 0);
mlCollisionUpdateRate = cString::ToInt(apElement->Attribute("CollisionUpdateRate"), 0);
// NEW
////////// BEAM SPECIFIC //////////
mbUseBeamNoise = cString::ToBool(apElement->Attribute("UseBeamNoise"), false);
mlLowFreqPoints = cString::ToInt(apElement->Attribute("LowFreqPoints"), 4);
mlHighFreqPoints = cString::ToInt(apElement->Attribute("HighFreqPoints"), 5);
mvMinLowFreqNoise = cString::ToVector3f(apElement->Attribute("MinLowFreqNoise"), 0);
mvMaxLowFreqNoise = cString::ToVector3f(apElement->Attribute("MaxLowFreqNoise"), 0);
mvMinHighFreqNoise = cString::ToVector3f(apElement->Attribute("MinHighFreqNoise"), 0);
mvMaxHighFreqNoise = cString::ToVector3f(apElement->Attribute("MaxHighFreqNoise"), 0);
// ---
}
//-----------------------------------------------------------------------
bool cParticleEmitterData3D_UserData::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
if (pBody->IsActive() == false || pBody->GetCollide() == false || pBody->IsCharacter()) {
return true;
}
if (mfShortestDist > apParams->mfDist) {
mbIntersected = true;
mfShortestDist = apParams->mfDist;
*mpIntersectPos = apParams->mvPoint;
*mpIntersectNormal = apParams->mvNormal;
}
return true;
}
bool cParticleEmitterData3D_UserData::CheckCollision(const cVector3f &avStart,
const cVector3f &avEnd,
iPhysicsWorld *apPhysicsWorld,
cVector3f *apNormalVec,
cVector3f *apPosVec) {
mfShortestDist = 99999.0f;
mbIntersected = false;
mpIntersectPos = apPosVec;
mpIntersectNormal = apNormalVec;
apPhysicsWorld->CastRay(this, avStart, avEnd, true, true, true);
return mbIntersected;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cParticleEmitter3D_UserData::cParticleEmitter3D_UserData(
tString asName, tMaterialVec *apMaterials,
cVector3f avSize, cGraphics *apGraphics, cResources *apResources,
cParticleEmitterData3D_UserData *apData)
: iParticleEmitter3D(asName, apMaterials, apData->mlMaxParticleNum,
avSize, apGraphics, apResources) {
mpData = apData;
mvDrawSize = 1;
mfTime = 0;
mfCreateCount = 0;
mfPauseCount = 0;
mfPauseWaitCount = 0;
mbPaused = false;
mfCollideCount = 1.0f / (float)mpData->mlCollisionUpdateRate;
mCoordSystem = apData->mCoordSystem;
mDrawType = apData->mDrawType;
mbUsesDirection = apData->mbUsesDirection;
// mbDying = !apData->mbRespawn;
mbRespawn = apData->mbRespawn;
SetSubDivUV(mpData->mvSubDiv);
// NEW
mbUsePartSpin = apData->mbUsePartSpin;
mbUseRevolution = apData->mbUseRevolution;
mPEType = apData->mPEType;
// Calculate max size of particles.
float fSizeMul = apData->mfStartRelSize;
if (fSizeMul < apData->mfMiddleRelSize)
fSizeMul = apData->mfMiddleRelSize;
if (fSizeMul < apData->mfEndRelSize)
fSizeMul = apData->mfEndRelSize;
mvMaxDrawSize = apData->mvMaxStartSize * fSizeMul;
/* int i = 0;
int j = 0;
int k = 0;
switch ( mPEType )
{
case ePEType_Beam:
hplDelete(mpVtxBuffer); // Destroy the current Vertex Buffer, as it's filled with quads we don't need now :)
if ( apData->mbUseBeamNoise )
{
// Calculate the Frequency Points Lookup Table, this is needed to create the Vertex Buffer.It is also needed by every beam particle created,
// as it needs to know the type and index of each when updating.
while ( (i < apData->mlLowFreqPoints) && (j < apData->mlHighFreqPoints) )
{
float fLowRelPos = (float) i / (apData->mlLowFreqPoints -1 );
float fHighRelPos = (float) j / (apData->mlHighFreqPoints -1 );
float fLastLowRelPos = 0.0f;
tBeamNoisePoint tempBNP;
// tempBNP.lLowFreqNoiseIdx = i;
// tempBNP.lHighFreqNoiseIdx = j;
tempBNP.fRelToBendPos = (fHighRelPos-fLastLowRelPos)/(fLowRelPos-fLastLowRelPos);
if ( fLowRelPos <= fHighRelPos )
{
if ( fLowRelPos == fHighRelPos )
{
tempBNP.noiseType = ePENoiseType_Both;
++i;
++j;
}
else
{
tempBNP.noiseType = ePENoiseType_LowFreq;
++i;
}
tempBNP.fRelToBeamPos = fLowRelPos;
apData->mvLFIndices.push_back (k);
}
else
{
tempBNP.fRelToBeamPos = fHighRelPos;
tempBNP.noiseType = ePENoiseType_HighFreq;
++j;
}
fLastLowRelPos = fLowRelPos;
mpData->mvBeamNoisePoints.push_back(tempBNP);
++k;
}
}
else
{
tBeamNoisePoint tempBNP;
tempBNP.fRelToBeamPos = 0.0f;
tempBNP.fRelToBendPos = 0.0f;
tempBNP.noiseType = ePENoiseType_None;
mpData->mvBeamNoisePoints.push_back(tempBNP);
tempBNP.fRelToBeamPos = 1.0f;
tempBNP.fRelToBendPos = 1.0f;
tempBNP.noiseType = ePENoiseType_None;
mpData->mvBeamNoisePoints.push_back(tempBNP);
}
mpVtxBuffer = apGraphics->GetLowLevel()->CreateVertexBuffer ( eVertexFlag_Position, // Now that we know how many points we have, we create the Vertex Buffer
eVertexBufferDrawType_Tri, eVertexBufferUsageType_Stream,
mlMaxParticles*mpData->mvBeamNoisePoints.size()*2, mlMaxParticles*(mpData->mvBeamNoisePoints.size()-1)*6);
for ( int i = 0; i < (int) mlMaxParticles; ++i )
{
for ( int j = 0; j < (int) mpData->mvBeamNoisePoints.size()-1; ++j )
{
int lStart = j*2;
mpVtxBuffer->AddIndex(lStart); // Half quad ( tri )
mpVtxBuffer->AddIndex(lStart+1);
mpVtxBuffer->AddIndex(lStart+3);
mpVtxBuffer->AddIndex(lStart+3); // The other half
mpVtxBuffer->AddIndex(lStart+2);
mpVtxBuffer->AddIndex(lStart);
}
}
for ( int i = 0; i < (int) mlMaxParticles; ++i )
{
for (int j = 0; j < (int) mpData->mvBeamNoisePoints.size(); ++j )
{
mpVtxBuffer->AddVertex(eVertexFlag_Texture0, cVector2f(mpData->mvBeamNoisePoints[j].fRelToBeamPos,1));
mpVtxBuffer->AddVertex(eVertexFlag_Texture0, cVector2f(mpData->mvBeamNoisePoints[j].fRelToBeamPos,0));
}
}
for ( int i = 0; i < (int) mlMaxParticles; ++i )
{
for (int j = 0; j < (int) mpData->mvBeamNoisePoints.size()*2; ++j )
{
mpVtxBuffer->AddVertex(eVertexFlag_Position, 0);
mpVtxBuffer->AddColor(eVertexFlag_Color0, cColor(1,1));
}
}
mpVtxBuffer->Compile(0);
break;
case ePEType_Normal:
break;
default:
break;
}
*/
// ---
}
//-----------------------------------------------------------------------
cParticleEmitter3D_UserData::~cParticleEmitter3D_UserData() {
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
bool cParticleEmitter3D_UserData::IsDying() {
if (mbRespawn == false || mbDying)
return true;
return false;
}
//-----------------------------------------------------------------------
void cParticleEmitter3D_UserData::Kill() {
mbRespawn = false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cParticleEmitter3D_UserData::SetParticleDefaults(cParticle *apParticle) {
///////////////////////////////////
// Start Color
apParticle->mStartColor = cMath::RandRectColor(mpData->mMinStartColor, mpData->mMaxStartColor);
apParticle->mColor = apParticle->mStartColor * mpData->mStartRelColor;
///////////////////////////////////
// Start Size
if (mpData->mvMinStartSize.y == 0 && mpData->mvMaxStartSize.y == 0)
apParticle->mvStartSize = cMath::RandRectf(mpData->mvMinStartSize.x, mpData->mvMaxStartSize.x);
else
apParticle->mvStartSize = cMath::RandRectVector2f(mpData->mvMinStartSize, mpData->mvMaxStartSize);
apParticle->mvSize = apParticle->mvStartSize * mpData->mfStartRelSize;
////////////////////////////////////
// Start sub division
if (mvSubDivUV.size() > 1) {
if (mpData->mSubDivType == ePESubDivType_Animation) {
apParticle->mlSubDivNum = 0;
} else {
apParticle->mlSubDivNum = cMath::RandRectl(0, (int)mvSubDivUV.size() - 1);
}
}
////////////////////////////////////
// Start collision
apParticle->mfBounceAmount = cMath::RandRectf(mpData->mfMinBounceAmount, mpData->mfMaxBounceAmount);
apParticle->mlBounceCount = cMath::RandRectl(mpData->mlMinCollisionMax, mpData->mlMaxCollisionMax);
////////////////////////////////////
// Start Position
// Matrix to start from
cMatrixf mtxStart;
if (mpData->mCoordSystem == eParticleEmitter3DCoordSystem_World) {
mtxStart = GetWorldMatrix();
} else {
// Not what should be here, perhaps only identity.
mtxStart = GetLocalMatrix();
}
// Sphere or box start
if (mpData->mStartPosType == ePEStartPosType_Box) {
apParticle->mvPos = mtxStart.GetTranslation() +
cMath::RandRectVector3f(mpData->mvMinStartPos, mpData->mvMaxStartPos);
} else if (mpData->mStartPosType == ePEStartPosType_Sphere) {
cVector2f vRot = cMath::RandRectVector2f(mpData->mvMinStartAngles,
mpData->mvMaxStartAngles);
cMatrixf mtxRot = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
cVector3f vPos = cVector3f(0, cMath::RandRectf(mpData->mfMinStartRadius, mpData->mfMaxStartRadius), 0);
apParticle->mvPos = mtxStart.GetTranslation() + cMath::MatrixMul(mtxRot, vPos);
}
// NEW
// if (mpData->mVBMeshData)
// {
/*
cVector3f vTrans = mtxStart.GetTranslation();
posI = cMath::RandRectl(0,mpData->mVBMeshData->GetVertexNum()-1);
vTrans.x += mpData->mpfMeshVtxData[posI*4];
vTrans.y += mpData->mpfMeshVtxData[posI*4 + 1];
vTrans.z += mpData->mpfMeshVtxData[posI*4 + 2];
apParticle->mvPos = vTrans;
*/
// apParticle->mvPos = mtxStart.GetTranslation() + mpData->mVBMeshData->GetVector3( eVertexFlag_Position, cMath::RandRectl(0,mpData->mVBMeshData->GetVertexNum()-1) );
// ++posI;
// if ( posI == mpData->mVBMeshData->GetVertexNum() )
// posI = 0;
// }
// ---
apParticle->mvLastPos = apParticle->mvPos;
apParticle->mvLastCollidePos = apParticle->mvPos;
////////////////////////////////////
// Start Velocity
// Sphere or box start
if (mpData->mStartVelType == ePEStartPosType_Box) {
apParticle->mvVel = cMath::RandRectVector3f(mpData->mvMinStartVel, mpData->mvMaxStartVel);
} else if (mpData->mStartVelType == ePEStartPosType_Sphere) {
cVector2f vRot = cMath::RandRectVector2f(mpData->mvMinStartVelAngles,
mpData->mvMaxStartVelAngles);
cMatrixf mtxRot = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
cVector3f vPos = cVector3f(0, cMath::RandRectf(mpData->mfMinStartVelSpeed, mpData->mfMaxStartVelSpeed), 0);
apParticle->mvVel = cMath::MatrixMul(mtxRot, vPos);
}
// If it uses the direction,
if (mpData->mbUsesDirection && mpData->mCoordSystem == eParticleEmitter3DCoordSystem_World) {
apParticle->mvVel = cMath::MatrixMul(mtxStart.GetRotation(), apParticle->mvVel);
}
apParticle->mfMaxSpeed = cMath::RandRectf(mpData->mfMinVelMaximum, mpData->mfMaxVelMaximum);
apParticle->mfSpeedMul = cMath::RandRectf(mpData->mfMinSpeedMultiply, mpData->mfMaxSpeedMultiply);
////////////////////////////////////
// Start Acceleration
apParticle->mvAcc = cMath::RandRectVector3f(mpData->mvMinStartAcc, mpData->mvMaxStartAcc);
// NEW
////////////////////////////////////
// Start Spin Velocity
if (mpData->mPartSpinType == ePEPartSpinType_Constant) {
apParticle->mfSpinVel = cMath::RandRectf(mpData->mfMinSpinRange, mpData->mfMaxSpinRange);
} else if (mpData->mPartSpinType == ePEPartSpinType_Movement) {
apParticle->mfSpinFactor = cMath::RandRectf(mpData->mfMinSpinRange, mpData->mfMaxSpinRange);
apParticle->mfSpinVel = 0.0f;
}
apParticle->mfSpin = cMath::RandRectf(0.0f, k2Pif);
////////////////////////////////////
// Start Revolution Velocity
apParticle->mvRevolutionVel = cMath::RandRectVector3f(mpData->mvMinRevVel, mpData->mvMaxRevVel);
// ---
///////////////////////////////////
// Life Span
apParticle->mfStartLife = cMath::RandRectf(mpData->mfMinLifeSpan, mpData->mfMaxLifeSpan);
apParticle->mfLife = apParticle->mfStartLife;
apParticle->mfLifeSize_MiddleStart = apParticle->mfLife * (1 - mpData->mfMiddleRelSizeTime);
apParticle->mfLifeSize_MiddleEnd = apParticle->mfLife * (1 - (mpData->mfMiddleRelSizeTime +
mpData->mfMiddleRelSizeLength));
apParticle->mfLifeColor_MiddleStart = apParticle->mfLife * (1 - mpData->mfMiddleRelColorTime);
apParticle->mfLifeColor_MiddleEnd = apParticle->mfLife * (1 - (mpData->mfMiddleRelColorTime +
mpData->mfMiddleRelColorLength));
/*Log("Created particle with Pos: (%s) Color: (%s) Size (%s) Vel: (%s) Acc: (%s) Life: %f SizeMiddleStart: %f SizeMiddleEnd: %f\n",
apParticle->mvPos.ToString().c_str(),
apParticle->mColor.ToString().c_str(),
apParticle->mvSize.ToString().c_str(),
apParticle->mvVel.ToString().c_str(),
apParticle->mvAcc.ToString().c_str(),
apParticle->mfLife,
apParticle->mfLifeSize_MiddleStart,
apParticle->mfLifeSize_MiddleEnd);*/
// NEW
/////////////////////////////////////
// Beam Specific
if (mPEType == ePEType_Beam) {
/*
if (apParticle->mvBeamPoints.size() == 0 )
{
for (int i = 0; i < (int) mpData->mvBeamNoisePoints.size(); ++i)
apParticle->mvBeamPoints.push_back(cVector(0.0f));
}
int j = 1;
cVector3f vLFPointPos = apParticle->mvPos + apParticle->mvVel * mpData->mvBeamNoisePoints.at(mpData->mvLFIndices[j]).fRelToBeamPos;
cVector3f vLastLFPointPos = apParticle->mvPos;
cVector3f vDir;
for ( int i = 1; i < (int) mpData->mvBeamNoisePoints.size(); ++i )
{
apParticle->mvBeamPoints[i] = vLastLFPointPos + vDir * mpData->mvBeamNoisePoints[i].fRelToBendPos;
switch (mpData->mvBeamNoisePoints[i].noiseType)
{
case ePENoiseType_HighFreq:
apParticle->mvBeamPoints[i] += cMath::RandRectVector3f(mpData->mvMinHighFreqNoise, mpData->mvMaxHighFreqNoise);
break;
case ePENoiseType_LowFreq:
++j;
vLastLFPointPos = vLFPointPos;
vLFPointPos = apParticle->mvPos + apParticle->mvVel * mpData->mvBeamNoisePoints.at(mpData->mvLFIndices[j]).fRelToBeamPos + cMath::RandRectVector3f(mpData->mvMinLowFreqNoise, mpData->mvMaxLowFreqNoise);
vDir = vLFPointPos - vLastLFPointPos;
break;
case ePENoiseType_Both:
apParticle->mvBeamPoints[i] += cMath::RandRectVector3f(mpData->mvMinHighFreqNoise, mpData->mvMaxHighFreqNoise);
++j;
vLastLFPointPos = vLFPointPos;
vLFPointPos = apParticle->mvPos + apParticle->mvVel * mpData->mvBeamNoisePoints.at(mpData->mvLFIndices[j]).fRelToBeamPos + cMath::RandRectVector3f(mpData->mvMinLowFreqNoise, mpData->mvMaxLowFreqNoise);
vDir = vLFPointPos - vLastLFPointPos;
break;
default:
break;
}
}
*/
}
// ---
}
//-----------------------------------------------------------------------
void cParticleEmitter3D_UserData::UpdateMotion(float afTimeStep) {
///////////////////////////////////////////////
// If not rendered this previous frame check if it should be paused.
// To be safe skip systems that has a parent...
if (mbDying == false && mbRespawn && mpParentSystem->GetParent() == NULL) {
if (GetGlobalRenderCount() != cRenderList::GetGlobalRenderCount()) {
if (mlUpdateCount > 120) {
return;
}
mlUpdateCount++;
} else {
mlUpdateCount = 0;
}
}
///////////////////////////////////////////
// Check if the emitter is ready to start.
if (mfTime < mpData->mfStartTimeOffset) {
mfTime += afTimeStep;
return;
}
///////////////////////////////////////////
// Delay check
if (mbPaused) {
mfPauseCount -= afTimeStep;
if (mfPauseCount <= 0) {
mbPaused = false;
mfPauseWaitCount = cMath::RandRectf(mpData->mfMinPauseInterval, mpData->mfMaxPauseInterval);
}
} else if (mpData->mfMinPauseLength > 0 && mpData->mfMinPauseInterval > 0) {
mfPauseWaitCount -= afTimeStep;
if (mfPauseWaitCount <= 0) {
mfPauseCount = cMath::RandRectf(mpData->mfMinPauseLength, mpData->mfMaxPauseLength);
mbPaused = true;
}
}
///////////////////////////////////////////
// Particle creation
if (mbPaused == false && mlNumOfParticles < mlMaxParticles) {
mfCreateCount += mpData->mfParticlesPerSecond * afTimeStep;
while (mfCreateCount >= 0.99999f && (mlNumOfParticles < mlMaxParticles)) {
SetParticleDefaults(CreateParticle());
mfCreateCount -= 1.0f;
}
}
///////////////////////////////////////////
// Collision Update
bool bColliding = false;
if (mpData->mbCollides) {
mfCollideCount -= afTimeStep;
if (mfCollideCount <= 0) {
mfCollideCount = 1.0f / (float)mpData->mlCollisionUpdateRate;
bColliding = true;
}
}
///////////////////////////////////////////
// Particle update
for (unsigned int i = 0; i < mlNumOfParticles; ++i) {
cParticle *pParticle = mvParticles[i];
////////////
// Position Update
pParticle->mvLastPos = pParticle->mvPos;
pParticle->mvPos += pParticle->mvVel * afTimeStep;
////////////
// Speed Update
pParticle->mvVel += pParticle->mvAcc * afTimeStep;
// gravity
switch (mpData->mGravityType) {
case ePEGravityType_None: {
break;
}
case ePEGravityType_Vector: {
pParticle->mvVel += mpData->mvGravityAcc * afTimeStep;
break;
}
case ePEGravityType_Center: {
cVector3f vDir;
cMatrixf mtxStart;
if (mpData->mCoordSystem == eParticleEmitter3DCoordSystem_World) {
vDir = pParticle->mvPos - GetWorldMatrix().GetTranslation();
} else {
// Perhaps on mvPos is needed.. and no subtraction.
vDir = pParticle->mvPos - GetLocalMatrix().GetTranslation();
}
vDir.Normalise();
pParticle->mvVel += vDir * mpData->mvGravityAcc.y * afTimeStep;
break;
}
case ePEGravityType_LastEnum:
break;
}
if (pParticle->mfMaxSpeed > 0) {
float fSpeed = pParticle->mvVel.Length();
if (fSpeed > pParticle->mfMaxSpeed) {
pParticle->mvVel = (pParticle->mvVel / fSpeed) * pParticle->mfMaxSpeed;
}
}
if (pParticle->mfSpeedMul != 0 && pParticle->mfSpeedMul != 1) {
pParticle->mvVel = pParticle->mvVel * pow(pParticle->mfSpeedMul, afTimeStep);
}
// NEW
///////////
// Spin Update
if (mpData->mbUsePartSpin) {
pParticle->mfSpin += pParticle->mfSpinVel * afTimeStep;
if (mpData->mPartSpinType == ePEPartSpinType_Movement)
pParticle->mfSpinVel = pParticle->mvVel.Length() * pParticle->mfSpinFactor;
if (pParticle->mfSpin >= k2Pif)
pParticle->mfSpin -= k2Pif;
else if (pParticle->mfSpin <= -k2Pif)
pParticle->mfSpin += k2Pif;
}
// ---
// NEW
// Revolution
if (mbUseRevolution) {
// pParticle->mvRevolution += pParticle->mvRevolutionVel * afTimeStep;
cMatrixf mtxRotationMatrix = cMath::MatrixRotate(pParticle->mvRevolutionVel * afTimeStep, eEulerRotationOrder_XYZ);
pParticle->mvPos = cMath::MatrixMul(mtxRotationMatrix, pParticle->mvPos);
pParticle->mvVel = cMath::MatrixMul(mtxRotationMatrix, pParticle->mvVel);
}
// ---
////////////
// Collison update
if (bColliding) {
cVector3f vPos, vNormal;
if (mpData->CheckCollision(pParticle->mvLastCollidePos, pParticle->mvPos,
mpWorld->GetPhysicsWorld(),
&vNormal, &vPos)) {
// Log("Coll pos: %s\n",vPos.ToString().c_str());
// Log("Coll normal: %s\n",vNormal.ToString().c_str());
pParticle->mvPos = vPos;
float fSpeed = pParticle->mvVel.Length();
cVector3f vReflection = pParticle->mvVel -
(vNormal * 2 * cMath::Vector3Dot(pParticle->mvVel, vNormal));
vReflection.Normalise();
pParticle->mvVel = vReflection * (fSpeed * pParticle->mfBounceAmount);
pParticle->mlBounceCount--;
if (pParticle->mlBounceCount <= 0) {
pParticle->mfLife = 0;
}
}
pParticle->mvLastCollidePos = pParticle->mvPos;
}
////////////
// Life Update
pParticle->mfLife -= afTimeStep;
if (pParticle->mfLife <= 0) {
if (mbRespawn) {
if (mbPaused)
SwapRemove(i);
else
SetParticleDefaults(pParticle);
} else {
SwapRemove(i);
mlMaxParticles--;
if (mlMaxParticles <= 0) {
mbDying = true;
}
}
}
////////////
// Subdiv Update
if (mpData->mSubDivType == ePESubDivType_Animation) {
float fLifePercent = (1.0f - (pParticle->mfLife / pParticle->mfStartLife));
pParticle->mlSubDivNum = (int)(fLifePercent * (float)mvSubDivUV.size() - 0.0001f);
}
////////////
// Color Update
// Start
if (pParticle->mfLife > pParticle->mfLifeColor_MiddleStart) {
float fT = (pParticle->mfLife - pParticle->mfLifeColor_MiddleStart) /
(pParticle->mfStartLife - pParticle->mfLifeColor_MiddleStart);
pParticle->mColor = (pParticle->mStartColor * mpData->mStartRelColor * fT) +
(pParticle->mStartColor * mpData->mMiddleRelColor * (1 - fT));
}
// Middle
else if (pParticle->mfLife > pParticle->mfLifeColor_MiddleEnd) {
pParticle->mColor = pParticle->mStartColor * mpData->mMiddleRelColor;
}
// End
else {
float fT = pParticle->mfLife / pParticle->mfLifeColor_MiddleEnd;
pParticle->mColor = (pParticle->mStartColor * mpData->mMiddleRelColor * fT) +
(pParticle->mStartColor * mpData->mEndRelColor * (1 - fT));
}
if (mpData->mbMultiplyRGBWithAlpha) {
pParticle->mColor.r *= pParticle->mColor.a;
pParticle->mColor.g *= pParticle->mColor.a;
pParticle->mColor.b *= pParticle->mColor.a;
}
////////////
// Size Update
// Start
if (pParticle->mfLife > pParticle->mfLifeSize_MiddleStart) {
float fT = (pParticle->mfLife - pParticle->mfLifeSize_MiddleStart) /
(pParticle->mfStartLife - pParticle->mfLifeSize_MiddleStart);
pParticle->mvSize = (pParticle->mvStartSize * mpData->mfStartRelSize * fT) +
(pParticle->mvStartSize * mpData->mfMiddleRelSize * (1 - fT));
}
// Middle
else if (pParticle->mfLife > pParticle->mfLifeSize_MiddleEnd) {
pParticle->mvSize = pParticle->mvStartSize * mpData->mfMiddleRelSize;
}
// End
else {
float fT = pParticle->mfLife / pParticle->mfLifeSize_MiddleEnd;
pParticle->mvSize = (pParticle->mvStartSize * mpData->mfMiddleRelSize * fT) +
(pParticle->mvStartSize * mpData->mfEndRelSize * (1 - fT));
}
}
///////////////////////////////////////////
// Frame Update
if (mvMaterials->size() > 1) {
mfFrame += mpData->mfFrameStep * afTimeStep;
if (mfFrame >= mpData->mfMaxFrameTime) {
mfFrame = 0;
}
}
}
//-----------------------------------------------------------------------
} // namespace hpl