MegaGlest/source/shared_lib/sources/graphics/particle.cpp

2571 lines
86 KiB
C++

// ==============================================================
// This file is part of Glest Shared Library (www.glest.org)
//
// Copyright (C) 2001-2008 Martiño Figueroa
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#include "math_wrapper.h"
#include "particle.h"
#include <stdexcept>
#include <cassert>
#include <algorithm>
#include "util.h"
#include "particle_renderer.h"
#include "math_util.h"
#include "platform_common.h"
#include "conversion.h"
#include "model.h"
#include "texture.h"
#include "platform_util.h"
#include "leak_dumper.h"
using namespace std;
using namespace Shared::Util;
using namespace Shared::PlatformCommon;
namespace Shared {
namespace Graphics {
// =====================================================
// class ParticleSystem
// =====================================================
const bool checkMemory = false;
static map<void *,int> memoryObjectList;
void Particle::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *particleNode = rootNode->addChild("Particle");
// Vec3f pos;
particleNode->addAttribute("pos",pos.getString(), mapTagReplacements);
// Vec3f lastPos;
particleNode->addAttribute("lastPos",lastPos.getString(), mapTagReplacements);
// Vec3f speed;
particleNode->addAttribute("speed",speed.getString(), mapTagReplacements);
// Vec3f speedUpRelative;
particleNode->addAttribute("speedUpRelative",floatToStr(speedUpRelative,6), mapTagReplacements);
// Vec3f speedUpConstant;
particleNode->addAttribute("speedUpConstant",speedUpConstant.getString(), mapTagReplacements);
// Vec3f accel;
particleNode->addAttribute("accel",accel.getString(), mapTagReplacements);
// Vec4f color;
particleNode->addAttribute("color",color.getString(), mapTagReplacements);
// float size;
particleNode->addAttribute("size",floatToStr(size,6), mapTagReplacements);
// int energy;
particleNode->addAttribute("energy",intToStr(energy), mapTagReplacements);
}
void Particle::loadGame(const XmlNode *rootNode) {
const XmlNode *particleNode = rootNode;
//particleNode = aiNode->getAttribute("startLoc")->getIntValue();
// Vec3f pos;
pos = Vec3f::strToVec3(particleNode->getAttribute("pos")->getValue());
// Vec3f lastPos;
lastPos = Vec3f::strToVec3(particleNode->getAttribute("lastPos")->getValue());
// Vec3f speed;
speed = Vec3f::strToVec3(particleNode->getAttribute("speed")->getValue());
// Vec3f speed;
speedUpRelative = particleNode->getAttribute("speedUpRelative")->getFloatValue();
// Vec3f speed;
speedUpConstant = Vec3f::strToVec3(particleNode->getAttribute("speedUpConstant")->getValue());
// Vec3f accel;
accel = Vec3f::strToVec3(particleNode->getAttribute("accel")->getValue());
// Vec4f color;
color = Vec4f::strToVec4(particleNode->getAttribute("color")->getValue());
// float size;
size = particleNode->getAttribute("size")->getFloatValue();
// int energy;
energy = particleNode->getAttribute("energy")->getIntValue();
}
ParticleSystem::ParticleSystem(int particleCount) {
if(checkMemory) {
printf("++ Create ParticleSystem [%p]\n",this);
memoryObjectList[this]++;
}
textureFileLoadDeferred = "";
textureFileLoadDeferredSystemId = 0;
textureFileLoadDeferredFormat = Texture::fAuto;
textureFileLoadDeferredComponents = 0;
//init particle vector
blendMode= bmOne;
//particles= new Particle[particleCount];
particles.clear();
//particles.reserve(particleCount);
particles.resize(particleCount);
state= sPlay;
aliveParticleCount= 0;
active= true;
visible= true;
//vars
texture= NULL;
particleObserver= NULL;
//params
this->particleCount= particleCount;
//this->particleCount= particles.size();
maxParticleEnergy= 250;
varParticleEnergy= 50;
pos= Vec3f(0.0f);
color= Vec4f(1.0f);
colorNoEnergy= Vec4f(0.0f);
emissionRate= 15.0f;
emissionState= 1.0f; // initialized with 1 because we must have at least one particle in the beginning!
speed= 1.0f;
teamcolorNoEnergy= false;
teamcolorEnergy= false;
alternations= 0;
particleSystemStartDelay= 0;
this->particleOwner = NULL;
this->particleSize = 0.0f;
}
ParticleSystem::~ParticleSystem() {
if(checkMemory) {
printf("-- Delete ParticleSystem [%p]\n",this);
memoryObjectList[this]--;
assert(memoryObjectList[this] == 0);
}
//delete [] particles;
particles.clear();
delete particleObserver;
particleObserver = NULL;
}
void ParticleSystem::callParticleOwnerEnd(ParticleSystem *particleSystem) {
if(this->particleOwner != NULL) {
this->particleOwner->end(particleSystem);
}
}
Checksum ParticleSystem::getCRC() {
Checksum crcForParticleSystem;
//std::vector<Particle> particles;
crcForParticleSystem.addInt(random.getLastNumber());
crcForParticleSystem.addInt(blendMode);
crcForParticleSystem.addInt(state);
crcForParticleSystem.addInt(active);
//crcForParticleSystem.addInt(visible);
crcForParticleSystem.addInt(aliveParticleCount);
crcForParticleSystem.addInt(particleCount);
//string textureFileLoadDeferred;
//int textureFileLoadDeferredSystemId;
//Texture::Format textureFileLoadDeferredFormat;
//int textureFileLoadDeferredComponents;
//Texture *texture;
//Vec3f pos;
//Vec4f color;
//Vec4f colorNoEnergy;
//float emissionRate;
//float emissionState;
crcForParticleSystem.addInt(maxParticleEnergy);
crcForParticleSystem.addInt(varParticleEnergy);
//float particleSize;
//float speed;
//Vec3f factionColor;
crcForParticleSystem.addInt(teamcolorNoEnergy);
crcForParticleSystem.addInt(teamcolorEnergy);
crcForParticleSystem.addInt(alternations);
crcForParticleSystem.addInt(particleSystemStartDelay);
//ParticleObserver *particleObserver;
return crcForParticleSystem;
}
// =============== VIRTUAL ======================
//updates all living particles and creates new ones
void ParticleSystem::update() {
if(aliveParticleCount > (int) particles.size()) {
throw megaglest_runtime_error("aliveParticleCount >= particles.size()");
}
if(particleSystemStartDelay > 0) {
particleSystemStartDelay--;
}
else if(state != sPause) {
for(int i= 0; i < aliveParticleCount; ++i) {
updateParticle(&particles[i]);
if(deathTest(&particles[i])) {
//kill the particle
killParticle(&particles[i]);
//maintain alive particles at front of the array
if(aliveParticleCount > 0) {
particles[i]= particles[aliveParticleCount];
}
}
}
if(state != ParticleSystem::sFade) {
emissionState= emissionState + emissionRate;
int emissionIntValue= (int) emissionState;
for(int i= 0; i < emissionIntValue; i++){
Particle *p= createParticle();
initParticle(p, i);
}
emissionState = emissionState - (float) emissionIntValue;
emissionState = truncateDecimal<float>(emissionState,6);
}
}
}
void ParticleSystem::render(ParticleRenderer *pr, ModelRenderer *mr){
if(active) {
pr->renderSystem(this);
}
}
ParticleSystem::BlendMode ParticleSystem::strToBlendMode(const string &str){
if(str == "normal") {
return bmOne;
}
else if(str == "black") {
return bmOneMinusAlpha;
}
else{
throw megaglest_runtime_error("Unknown particle mode: " + str);
}
}
// =============== SET ==========================
void ParticleSystem::setState(State state){
this->state= state;
for(int i=getChildCount()-1; i>=0; i--)
getChild(i)->setState(state);
}
void ParticleSystem::setTexture(Texture *texture){
this->texture= texture;
}
void ParticleSystem::setPos(Vec3f pos){
this->pos= pos;
for(int i=getChildCount()-1; i>=0; i--)
getChild(i)->setPos(pos);
}
void ParticleSystem::setColor(Vec4f color){
this->color= color;
}
void ParticleSystem::setColorNoEnergy(Vec4f colorNoEnergy){
this->colorNoEnergy= colorNoEnergy;
}
void ParticleSystem::setEmissionRate(float emissionRate){
this->emissionRate= emissionRate;
this->emissionRate = truncateDecimal<float>(this->emissionRate,6);
}
void ParticleSystem::setMaxParticleEnergy(int maxParticleEnergy){
this->maxParticleEnergy= maxParticleEnergy;
}
void ParticleSystem::setVarParticleEnergy(int varParticleEnergy){
this->varParticleEnergy= varParticleEnergy;
}
void ParticleSystem::setParticleSize(float particleSize){
this->particleSize= particleSize;
this->particleSize = truncateDecimal<float>(this->particleSize,6);
}
void ParticleSystem::setSpeed(float speed){
this->speed= speed;
this->speed = truncateDecimal<float>(this->speed,6);
}
void ParticleSystem::setSpeedUpRelative(float speedUpRelative){
this->speedUpRelative= speedUpRelative;
this->speedUpRelative = truncateDecimal<float>(this->speedUpRelative,6);
}
void ParticleSystem::setSpeedUpConstant(float speedUpConstant){
this->speedUpConstant= speedUpConstant;
this->speedUpConstant = truncateDecimal<float>(this->speedUpConstant,6);
}
void ParticleSystem::setActive(bool active){
this->active= active;
for(int i=getChildCount()-1; i>=0; i--)
getChild(i)->setActive(active);
}
void ParticleSystem::setObserver(ParticleObserver *particleObserver){
this->particleObserver= particleObserver;
}
ParticleSystem* ParticleSystem::getChild(int i){
throw std::out_of_range("ParticleSystem::getChild bad");
}
void ParticleSystem::setVisible(bool visible){
this->visible= visible;
for(int i=getChildCount()-1; i>=0; i--) {
getChild(i)->setVisible(visible);
}
}
string ParticleSystem::toString() const {
string result = "ParticleSystem ";
result += "particles = " + intToStr(particles.size());
// for(unsigned int i = 0; i < particles.size(); ++i) {
// Particle &particle = particles[i];
//
// }
result += "\nrandom = " + intToStr(random.getLastNumber());
result += "\nblendMode = " + intToStr(blendMode);
result += "\nstate = " + intToStr(state);
result += "\nactive = " + intToStr(active);
//result += "\nvisible = " + intToStr(visible);
result += "\naliveParticleCount = " + intToStr(aliveParticleCount);
result += "\nparticleCount = " + intToStr(particleCount);
result += "\ntextureFileLoadDeferred = " + textureFileLoadDeferred;
result += "\ntextureFileLoadDeferredFormat = " + intToStr(textureFileLoadDeferredFormat);
result += "\ntextureFileLoadDeferredComponents = " + intToStr(textureFileLoadDeferredComponents);
if(texture != NULL) {
result += "\ntexture = " + extractFileFromDirectoryPath(texture->getPath());
}
result += "\npos = " + pos.getString();
result += "\ncolor = " + color.getString();
result += "\ncolorNoEnergy = " + colorNoEnergy.getString();
result += "\nemissionRate = " + floatToStr(emissionRate,6);
result += "\nemissionState = " + floatToStr(emissionState,6);
result += "\nmaxParticleEnergy = " + intToStr(maxParticleEnergy);
result += "\nvarParticleEnergy = " + intToStr(varParticleEnergy);
result += "\nparticleSize = " + floatToStr(particleSize,6);
result += "\nspeed = " + floatToStr(speed,6);
result += "\nfactionColor = " + factionColor.getString();
result += "\nteamcolorNoEnergy = " + intToStr(teamcolorNoEnergy);
result += "\nteamcolorEnergy = " + intToStr(teamcolorEnergy);
result += "\nalternations = " + intToStr(alternations);
result += "\nparticleSystemStartDelay = " + intToStr(particleSystemStartDelay);
//ParticleObserver *particleObserver;
return result;
}
void ParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *particleSystemNode = rootNode->addChild("ParticleSystem");
// std::vector<Particle> particles;
// for(unsigned int i = 0; i < particles.size(); ++i) {
// Particle &particle = particles[i];
// particle.saveGame(particleSystemNode);
// }
// RandomGen random;
particleSystemNode->addAttribute("random",intToStr(random.getLastNumber()), mapTagReplacements);
// BlendMode blendMode;
particleSystemNode->addAttribute("blendMode",intToStr(blendMode), mapTagReplacements);
// State state;
particleSystemNode->addAttribute("state",intToStr(state), mapTagReplacements);
// bool active;
particleSystemNode->addAttribute("active",intToStr(active), mapTagReplacements);
// bool visible;
particleSystemNode->addAttribute("visible",intToStr(visible), mapTagReplacements);
// int aliveParticleCount;
particleSystemNode->addAttribute("aliveParticleCount",intToStr(aliveParticleCount), mapTagReplacements);
// int particleCount;
particleSystemNode->addAttribute("particleCount",intToStr(particleCount), mapTagReplacements);
//
// Texture *texture;
if(texture != NULL) {
particleSystemNode->addAttribute("texture",texture->getPath(), mapTagReplacements);
particleSystemNode->addAttribute("textureid",intToStr(texture->getTextureSystemId()), mapTagReplacements);
particleSystemNode->addAttribute("textureFormat",intToStr(texture->getFormat()), mapTagReplacements);
Texture2D *t2d = dynamic_cast<Texture2D *>(texture);
if(t2d != NULL && t2d->getPixmapConst() != NULL) {
particleSystemNode->addAttribute("textureComponents",intToStr(t2d->getPixmapConst()->getComponents()), mapTagReplacements);
}
}
// Vec3f pos;
particleSystemNode->addAttribute("pos",pos.getString(), mapTagReplacements);
// Vec4f color;
particleSystemNode->addAttribute("color",color.getString(), mapTagReplacements);
// Vec4f colorNoEnergy;
particleSystemNode->addAttribute("colorNoEnergy",colorNoEnergy.getString(), mapTagReplacements);
// float emissionRate;
particleSystemNode->addAttribute("emissionRate",floatToStr(emissionRate,6), mapTagReplacements);
// float emissionState;
particleSystemNode->addAttribute("emissionState",floatToStr(emissionState,6), mapTagReplacements);
// int maxParticleEnergy;
particleSystemNode->addAttribute("maxParticleEnergy",intToStr(maxParticleEnergy), mapTagReplacements);
// int varParticleEnergy;
particleSystemNode->addAttribute("varParticleEnergy",intToStr(varParticleEnergy), mapTagReplacements);
// float particleSize;
particleSystemNode->addAttribute("particleSize",floatToStr(particleSize,6), mapTagReplacements);
// float speed;
particleSystemNode->addAttribute("speed",floatToStr(speed,6), mapTagReplacements);
// Vec3f factionColor;
particleSystemNode->addAttribute("factionColor",factionColor.getString(), mapTagReplacements);
// bool teamcolorNoEnergy;
particleSystemNode->addAttribute("teamcolorNoEnergy",intToStr(teamcolorNoEnergy), mapTagReplacements);
// bool teamcolorEnergy;
particleSystemNode->addAttribute("teamcolorEnergy",intToStr(teamcolorEnergy), mapTagReplacements);
// int alternations;
particleSystemNode->addAttribute("alternations",intToStr(alternations), mapTagReplacements);
// int particleSystemStartDelay;
particleSystemNode->addAttribute("particleSystemStartDelay",intToStr(particleSystemStartDelay), mapTagReplacements);
// ParticleObserver *particleObserver;
if(particleObserver != NULL) {
particleObserver->saveGame(particleSystemNode);
}
}
string ParticleSystem::getTextureFileLoadDeferred() {
return textureFileLoadDeferred;
}
int ParticleSystem::getTextureFileLoadDeferredSystemId() {
return textureFileLoadDeferredSystemId;
}
Texture::Format ParticleSystem::getTextureFileLoadDeferredFormat() {
return textureFileLoadDeferredFormat;
}
int ParticleSystem::getTextureFileLoadDeferredComponents() {
return textureFileLoadDeferredComponents;
}
void ParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *particleSystemNode = rootNode->getChild("ParticleSystem");
particleCount = particleSystemNode->getAttribute("particleCount")->getIntValue();
//printf("Load Smoke particle (ParticleSystem)\n");
// std::vector<Particle> particles;
// for(unsigned int i = 0; i < particles.size(); ++i) {
// Particle &particle = particles[i];
// particle.saveGame(particleSystemNode);
// }
particles.clear();
particles.resize(particleCount);
// vector<XmlNode *> particleNodeList = particleSystemNode->getChildList("Particle");
// for(unsigned int i = 0; i < particleNodeList.size(); ++i) {
// XmlNode *node = particleNodeList[i];
//
// //printf("Load Smoke particle (Particle = %d)\n",i);
//
// Particle particle;
// particle.loadGame(node);
// particles.push_back(particle);
// }
// RandomGen random;
random.setLastNumber(particleSystemNode->getAttribute("random")->getIntValue());
// BlendMode blendMode;
blendMode = static_cast<BlendMode>(particleSystemNode->getAttribute("blendMode")->getIntValue());
// State state;
state = static_cast<State>(particleSystemNode->getAttribute("state")->getIntValue());
// bool active;
active = particleSystemNode->getAttribute("active")->getIntValue() != 0;
// bool visible;
visible = particleSystemNode->getAttribute("visible")->getIntValue() != 0;
// int aliveParticleCount;
aliveParticleCount = particleSystemNode->getAttribute("aliveParticleCount")->getIntValue();
// int particleCount;
particleCount = particleSystemNode->getAttribute("particleCount")->getIntValue();
//
// Texture *texture;
if(particleSystemNode->hasAttribute("texture") == true) {
textureFileLoadDeferred = particleSystemNode->getAttribute("texture")->getValue();
textureFileLoadDeferredSystemId = particleSystemNode->getAttribute("textureid")->getIntValue();
textureFileLoadDeferredFormat = static_cast<Texture::Format>(particleSystemNode->getAttribute("textureFormat")->getIntValue());
if(particleSystemNode->hasAttribute("textureComponents") == true) {
textureFileLoadDeferredComponents = particleSystemNode->getAttribute("textureComponents")->getIntValue();
}
}
// Vec3f pos;
pos = Vec3f::strToVec3(particleSystemNode->getAttribute("pos")->getValue());
// Vec4f color;
color = Vec4f::strToVec4(particleSystemNode->getAttribute("color")->getValue());
// Vec4f colorNoEnergy;
colorNoEnergy = Vec4f::strToVec4(particleSystemNode->getAttribute("colorNoEnergy")->getValue());
// float emissionRate;
emissionRate = particleSystemNode->getAttribute("emissionRate")->getFloatValue();
// float emissionState;
emissionState = particleSystemNode->getAttribute("emissionState")->getFloatValue();
// int maxParticleEnergy;
maxParticleEnergy = particleSystemNode->getAttribute("maxParticleEnergy")->getIntValue();
// int varParticleEnergy;
varParticleEnergy = particleSystemNode->getAttribute("varParticleEnergy")->getIntValue();
// float particleSize;
particleSize = particleSystemNode->getAttribute("particleSize")->getFloatValue();
// float speed;
speed = particleSystemNode->getAttribute("speed")->getFloatValue();
// Vec3f factionColor;
factionColor = Vec3f::strToVec3(particleSystemNode->getAttribute("factionColor")->getValue());
// bool teamcolorNoEnergy;
teamcolorNoEnergy = particleSystemNode->getAttribute("teamcolorNoEnergy")->getIntValue() != 0;
// bool teamcolorEnergy;
teamcolorEnergy = particleSystemNode->getAttribute("teamcolorEnergy")->getIntValue() != 0;
// int alternations;
alternations = particleSystemNode->getAttribute("alternations")->getIntValue();
// int particleSystemStartDelay;
particleSystemStartDelay = particleSystemNode->getAttribute("particleSystemStartDelay")->getIntValue();
// ParticleObserver *particleObserver;
//if(particleObserver != NULL) {
// particleObserver->loadGame(particleSystemNode);
//}
}
// =============== MISC =========================
void ParticleSystem::fade(){
//printf("**************Fading particle System:\n[%s]\n",this->toString().c_str());
bool alreadyFading = (state == sFade);
if(particleObserver != NULL){
if(state != sPlay) {
char szBuf[8096]="";
snprintf(szBuf,8096,"state != sPlay, state = [%d]\n",state);
//throw megaglest_runtime_error(szBuf);
//printf(szBuf);
SystemFlags::OutputDebug(SystemFlags::debugError,"%s",szBuf);
}
//assert(state == sPlay);
}
state= sFade;
if(alreadyFading == false) {
if(particleObserver != NULL){
particleObserver->update(this);
particleObserver=NULL;
}
for(int i=getChildCount()-1; i>=0; i--) {
getChild(i)->fade();
}
}
}
int ParticleSystem::isEmpty() const {
//assert(aliveParticleCount>=0);
return aliveParticleCount == 0 && state != sPause;
}
// =============== PROTECTED =========================
// if there is one dead particle it returns it else, return the particle with
// less energy
Particle * ParticleSystem::createParticle() {
//if any dead particles
if(aliveParticleCount < particleCount) {
++aliveParticleCount;
return &particles[aliveParticleCount - 1];
}
//if not
int minEnergy= particles[0].energy;
int minEnergyParticle= 0;
for(int i= 0; i < particleCount; ++i){
if(particles[i].energy < minEnergy){
minEnergy= particles[i].energy;
minEnergyParticle= i;
}
}
return &particles[minEnergyParticle];
}
void ParticleSystem::initParticle(Particle *p, int particleIndex) {
p->pos= pos;
p->lastPos= p->pos;
p->speed= Vec3f(0.0f);
p->accel= Vec3f(0.0f);
p->color= Vec4f(1.0f, 1.0f, 1.0f, 1.0);
p->size= particleSize;
p->size = truncateDecimal<float>(p->size,6);
p->energy= maxParticleEnergy + random.randRange(-varParticleEnergy, varParticleEnergy);
}
void ParticleSystem::updateParticle(Particle *p) {
p->lastPos= p->pos;
p->pos= p->pos + p->speed;
p->speed= p->speed + p->accel;
p->energy--;
}
bool ParticleSystem::deathTest(Particle *p) {
return p->energy <= 0;
}
void ParticleSystem::killParticle(Particle *p) {
aliveParticleCount--;
}
void ParticleSystem::setFactionColor(Vec3f factionColor){
this->factionColor= factionColor;
Vec3f tmpCol;
if(teamcolorEnergy){
this->color= Vec4f(factionColor.x, factionColor.y, factionColor.z, this->color.w);
}
if(teamcolorNoEnergy){
this->colorNoEnergy= Vec4f(factionColor.x, factionColor.y, factionColor.z, this->colorNoEnergy.w);
}
for(int i=getChildCount()-1; i>=0; i--)
getChild(i)->setFactionColor(factionColor);
}
// ===========================================================================
// FireParticleSystem
// ===========================================================================
FireParticleSystem::FireParticleSystem(int particleCount) :
ParticleSystem(particleCount){
radius= 0.5f;
speed= 0.01f;
windSpeed= Vec3f(0.0f);
setParticleSize(0.6f);
setColorNoEnergy(Vec4f(1.0f, 0.5f, 0.0f, 1.0f));
}
void FireParticleSystem::initParticle(Particle *p, int particleIndex){
ParticleSystem::initParticle(p, particleIndex);
float ang= random.randRange(-2.0f * pi, 2.0f * pi);
#ifdef USE_STREFLOP
float mod= streflop::fabsf(static_cast<streflop::Simple>(random.randRange(-radius, radius)));
float x= streflop::sinf(static_cast<streflop::Simple>(ang))*mod;
float y= streflop::cosf(static_cast<streflop::Simple>(ang))*mod;
float radRatio= streflop::sqrtf(static_cast<streflop::Simple>(mod/radius));
#else
float mod= fabsf(random.randRange(-radius, radius));
float x= sinf(ang) * mod;
float y= cosf(ang) * mod;
float radRatio= sqrtf((mod / radius));
#endif
p->color= colorNoEnergy * 0.5f + colorNoEnergy * 0.5f * radRatio;
p->energy= static_cast<int> (maxParticleEnergy * radRatio)
+ random.randRange(-varParticleEnergy, varParticleEnergy);
p->pos= Vec3f(pos.x + x, pos.y + random.randRange(-radius / 2, radius / 2), pos.z + y);
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
p->lastPos= pos;
p->size= particleSize;
p->speed= Vec3f(0, speed + speed * random.randRange(-0.5f, 0.5f), 0) + windSpeed;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
}
void FireParticleSystem::updateParticle(Particle *p){
p->lastPos= p->pos;
p->pos= p->pos + p->speed;
p->energy--;
if(p->color.x > 0.0f)
p->color.x*= 0.98f;
if(p->color.y > 0.0f)
p->color.y*= 0.98f;
if(p->color.w > 0.0f)
p->color.w*= 0.98f;
p->speed.x*= 1.001f;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
}
string FireParticleSystem::toString() const {
string result = ParticleSystem::toString();
result += "\nFireParticleSystem ";
result += "\nradius = " + floatToStr(radius);
result += "\nwindSpeed = " + windSpeed.getString();
return result;
}
// ================= SET PARAMS ====================
void FireParticleSystem::setRadius(float radius){
this->radius= radius;
}
void FireParticleSystem::setWind(float windAngle, float windSpeed) {
#ifdef USE_STREFLOP
this->windSpeed.x= streflop::sinf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= streflop::cosf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
#else
this->windSpeed.x= sinf(degToRad(windAngle)) * windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= cosf(degToRad(windAngle)) * windSpeed;
#endif
this->windSpeed.x = truncateDecimal<float>(this->windSpeed.x,6);
this->windSpeed.y = truncateDecimal<float>(this->windSpeed.y,6);
this->windSpeed.z = truncateDecimal<float>(this->windSpeed.z,6);
}
void FireParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *fireParticleSystemNode = rootNode->addChild("FireParticleSystem");
ParticleSystem::saveGame(fireParticleSystemNode);
// float radius;
fireParticleSystemNode->addAttribute("radius",floatToStr(radius,6), mapTagReplacements);
// Vec3f windSpeed;
fireParticleSystemNode->addAttribute("windSpeed",windSpeed.getString(), mapTagReplacements);
}
void FireParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *fireParticleSystemNode = rootNode;
ParticleSystem::loadGame(fireParticleSystemNode);
// float radius;
radius = fireParticleSystemNode->getAttribute("radius")->getFloatValue();
// Vec3f windSpeed;
windSpeed = Vec3f::strToVec3(fireParticleSystemNode->getAttribute("windSpeed")->getValue());
}
Checksum FireParticleSystem::getCRC() {
Checksum crcForParticleSystem = ParticleSystem::getCRC();
//float radius;
//Vec3f windSpeed;
return crcForParticleSystem;
}
// ===========================================================================
// GameParticleSystem
// ===========================================================================
GameParticleSystem::GameParticleSystem(int particleCount):
ParticleSystem(particleCount),
primitive(pQuad),
model(NULL),
modelCycle(0.0f),
offset(0.0f),
direction(0.0f, 1.0f, 0.0f),
tween(0.0f)
{}
GameParticleSystem::~GameParticleSystem(){
for(Children::iterator it= children.begin(); it != children.end(); ++it){
(*it)->setParent(NULL);
(*it)->fade();
}
}
GameParticleSystem::Primitive GameParticleSystem::strToPrimitive(const string &str){
if(str == "quad"){
return pQuad;
}
else if(str == "line"){
return pLine;
}
else{
throw megaglest_runtime_error("Unknown particle primitive: " + str);
}
}
int GameParticleSystem::getChildCount(){
return (int)children.size();
}
ParticleSystem* GameParticleSystem::getChild(int i){
return children.at(i); // does bounds checking
}
void GameParticleSystem::addChild(UnitParticleSystem* child) {
assert(!child->getParent());
child->setParent(this);
children.push_back(child);
}
void GameParticleSystem::removeChild(UnitParticleSystem* child){
assert(this == child->getParent());
Children::iterator it = std::find(children.begin(),children.end(),child);
assert(it != children.end());
children.erase(it);
}
void GameParticleSystem::setPos(Vec3f pos){
this->pos= pos;
positionChildren();
}
void GameParticleSystem::positionChildren() {
Vec3f child_pos = pos - offset;
for(int i=getChildCount()-1; i>=0; i--)
getChild(i)->setPos(child_pos);
}
void GameParticleSystem::setOffset(Vec3f offset){
this->offset= offset;
positionChildren();
}
void GameParticleSystem::render(ParticleRenderer *pr, ModelRenderer *mr){
if(active){
if(model != NULL){
pr->renderModel(this, mr);
}
switch(primitive){
case pQuad:
pr->renderSystem(this);
break;
case pLine:
pr->renderSystemLine(this);
break;
default:
assert(false);
break;
}
}
}
void GameParticleSystem::setTween(float relative,float absolute) {
if(model) {
// animation?
//printf("#1 Particle model meshcount [%d] modelCycle = %f, relative = %f, absolute = %f\n",model->getMeshCount(),modelCycle,relative,absolute);
if(modelCycle == 0.0f) {
tween= relative;
}
else {
#ifdef USE_STREFLOP
if(streflop::fabs(static_cast<streflop::Simple>(absolute)) <= 0.00001f){
#else
if(fabs(absolute) <= 0.00001f) {
#endif
tween = 0.0f;
}
else {
#ifdef USE_STREFLOP
tween= streflop::fmod(static_cast<streflop::Simple>(absolute), static_cast<streflop::Simple>(modelCycle));
#else
tween= fmod(absolute, modelCycle);
#endif
tween /= modelCycle;
}
}
tween = truncateDecimal<float>(tween,6);
if(tween < 0.0f || tween > 1.0f) {
//printf("In [%s::%s Line: %d] WARNING setting tween to [%f] clamping tween, modelCycle [%f] absolute [%f] relative [%f]\n",__FILE__,__FUNCTION__,__LINE__,tween,modelCycle,absolute,relative);
//assert(tween >= 0.0f && tween <= 1.0f);
}
tween= clamp(tween, 0.0f, 1.0f);
//printf("#2 Particle model meshcount [%d] modelCycle = %f, relative = %f, absolute = %f\n",model->getMeshCount(),modelCycle,relative,absolute);
}
for(Children::iterator it= children.begin(); it != children.end(); ++it) {
(*it)->setTween(relative,absolute);
}
}
string GameParticleSystem::getModelFileLoadDeferred() {
return modelFileLoadDeferred;
}
void GameParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *gameParticleSystemNode = rootNode->addChild("GameParticleSystem");
ParticleSystem::saveGame(gameParticleSystemNode);
// Children children;
for(unsigned int i = 0; i < children.size(); ++i) {
if(children[i] != NULL) {
children[i]->saveGame(gameParticleSystemNode);
}
}
// Primitive primitive;
gameParticleSystemNode->addAttribute("primitive",intToStr(primitive), mapTagReplacements);
// Model *model;
if(model != NULL) {
gameParticleSystemNode->addAttribute("model",model->getFileName(), mapTagReplacements);
}
// float modelCycle;
gameParticleSystemNode->addAttribute("modelCycle",floatToStr(modelCycle,6), mapTagReplacements);
// Vec3f offset;
gameParticleSystemNode->addAttribute("offset",offset.getString(), mapTagReplacements);
// Vec3f direction;
gameParticleSystemNode->addAttribute("direction",direction.getString(), mapTagReplacements);
// float tween;
gameParticleSystemNode->addAttribute("tween",floatToStr(tween,6), mapTagReplacements);
}
void GameParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *gameParticleSystemNode = rootNode->getChild("GameParticleSystem");
//printf("Load Smoke particle (GameParticleSystem)\n");
ParticleSystem::loadGame(gameParticleSystemNode);
// Children children;
// for(unsigned int i = 0; i < children.size(); ++i) {
// children[i]->saveGame(gameParticleSystemNode);
// }
vector<XmlNode *> childrenNodeList = gameParticleSystemNode->getChildList("UnitParticleSystem");
for(unsigned int i = 0; i < childrenNodeList.size(); ++i) {
XmlNode *node = childrenNodeList[i];
UnitParticleSystem *ups = new UnitParticleSystem();
//ups->setParticleOwner(!!!);
ups->loadGame(node);
//children.push_back(ups);
addChild(ups);
}
// Primitive primitive;
primitive = static_cast<Primitive>(gameParticleSystemNode->getAttribute("primitive")->getIntValue());
// Model *model;
//if(model != NULL) {
// gameParticleSystemNode->addAttribute("model",model->getFileName(), mapTagReplacements);
//}
if(gameParticleSystemNode->hasAttribute("model") == true) {
modelFileLoadDeferred = gameParticleSystemNode->getAttribute("model")->getValue();
}
// float modelCycle;
//gameParticleSystemNode->addAttribute("modelCycle",floatToStr(modelCycle), mapTagReplacements);
modelCycle = gameParticleSystemNode->getAttribute("modelCycle")->getFloatValue();
// Vec3f offset;
offset = Vec3f::strToVec3(gameParticleSystemNode->getAttribute("offset")->getValue());
// Vec3f direction;
direction = Vec3f::strToVec3(gameParticleSystemNode->getAttribute("direction")->getValue());
// float tween;
tween = gameParticleSystemNode->getAttribute("tween")->getFloatValue();
}
Checksum GameParticleSystem::getCRC() {
Checksum crcForParticleSystem = ParticleSystem::getCRC();
return crcForParticleSystem;
}
string GameParticleSystem::toString() const {
string result = ParticleSystem::toString();
result += "\nGameParticleSystem ";
result += "\nchildren = " + intToStr(children.size());
result += "\nprimitive = " + intToStr(primitive);
//string modelFileLoadDeferred;
//Model *model;
result += "\nmodelCycle = " + floatToStr(modelCycle);
result += "\noffset = " + offset.getString();
result += "\ndirection = " + direction.getString();
result += "\ntween = " + floatToStr(tween);
return result;
}
// ===========================================================================
// UnitParticleSystem
// ===========================================================================
bool UnitParticleSystem::isNight= false;
Vec3f UnitParticleSystem::lightColor=Vec3f(1.0f,1.0f,1.0f);
UnitParticleSystem::UnitParticleSystem(int particleCount) :
GameParticleSystem(particleCount), parent(NULL) {
particleSystemType = NULL;
radius= 0.5f;
speed= 0.01f;
windSpeed= Vec3f(0.0f);
minRadius = 0.0;
setParticleSize(0.6f);
setColorNoEnergy(Vec4f(1.0f, 0.5f, 0.0f, 1.0f));
sizeNoEnergy=1.0f;
primitive= pQuad;
gravity= 0.0f;
fixed= false;
shape = UnitParticleSystem::sLinear;
angle= 0.0f;
rotation= 0.0f;
relativeDirection= true;
relative= false;
staticParticleCount= 0;
isVisibleAtNight= true;
isVisibleAtDay= true;
isDaylightAffected= false;
cRotation= Vec3f(1.0f, 1.0f, 1.0f);
fixedAddition= Vec3f(0.0f, 0.0f, 0.0f);
//prepare system for given staticParticleCount
if(staticParticleCount > 0){
emissionState= (float) staticParticleCount;
}
energyUp= false;
delay = 0; // none
lifetime = -1; // forever
emissionRateFade=0.0f;
startTime = 0;
endTime = 1;
unitModel=NULL;
meshName="";
radiusBasedStartenergy = false;
}
UnitParticleSystem::~UnitParticleSystem(){
if(parent){
parent->removeChild(this);
}
}
bool UnitParticleSystem::getVisible() const{
if((isNight==true) && (isVisibleAtNight==true)){
return visible;
}
else if((isNight==false) && (isVisibleAtDay==true)){
return visible;
}
else
return false;
}
void UnitParticleSystem::render(ParticleRenderer *pr, ModelRenderer *mr) {
GameParticleSystem::render(pr,mr);
}
void UnitParticleSystem::setRotation(float rotation){
this->rotation= rotation;
for(Children::iterator it= children.begin(); it != children.end(); ++it)
(*it)->setRotation(rotation);
}
void UnitParticleSystem::fade(){
if(!parent || (lifetime<=0 && !(emissionRateFade && emissionRate > 0))){ // particle has its own lifetime?
unitModel=NULL;
GameParticleSystem::fade();
}
}
UnitParticleSystem::Shape UnitParticleSystem::strToShape(const string& str){
if(str == "spherical"){
return sSpherical;
}
else if(str == "conical"){
return sConical;
}
else if(str == "linear"){
return sLinear;
}
else{
throw megaglest_runtime_error("Unknown particle shape: " + str);
}
}
void UnitParticleSystem::initParticle(Particle *p, int particleIndex){
ParticleSystem::initParticle(p, particleIndex);
const float ang= random.randRange(-2.0f * pi, 2.0f * pi);
#ifdef USE_STREFLOP
const float mod= streflop::fabsf(static_cast<streflop::Simple>(random.randRange(-radius, radius)));
const float radRatio= streflop::sqrtf(static_cast<streflop::Simple>(mod/radius));
#else
const float mod= fabsf(random.randRange(-radius, radius));
const float radRatio= sqrtf(mod / radius);
#endif
p->color= color;
if(isDaylightAffected==true) {
p->color.x=p->color.x*lightColor.x;
p->color.y=p->color.y*lightColor.y;
p->color.z=p->color.z*lightColor.z;
}
if(radiusBasedStartenergy == true) {
p->energy= static_cast<int> (maxParticleEnergy * radRatio) +
random.randRange(-varParticleEnergy,varParticleEnergy);
}
else {
p->energy= static_cast<int> (maxParticleEnergy) +
random.randRange(-varParticleEnergy, varParticleEnergy);
}
p->lastPos= pos;
oldPosition= pos;
p->size= particleSize;
p->speedUpRelative= speedUpRelative;
p->accel= Vec3f(0.0f, -gravity, 0.0f);
p->accel.x = truncateDecimal<float>(p->accel.x,6);
p->accel.y = truncateDecimal<float>(p->accel.y,6);
p->accel.z = truncateDecimal<float>(p->accel.z,6);
// work out where we start for our shape (set speed and pos)
switch(shape){
case sSpherical:
angle = (float)random.randRange(0,360);
// fall through
case sConical:{
Vec2f horiz = Vec2f(1,0).rotate(ang);
Vec2f vert = Vec2f(1,0).rotate(degToRad(angle));
Vec3f start = Vec3f(horiz.x*vert.y,vert.x,horiz.y).getNormalized(); // close enough
p->speed = start * speed;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
start = start * random.randRange(minRadius,radius);
p->pos = pos + offset + start;
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
} break;
case sLinear:{
#ifdef USE_STREFLOP
float x= streflop::sinf(static_cast<streflop::Simple>(ang))*mod;
float y= streflop::cosf(static_cast<streflop::Simple>(ang))*mod;
#else
float x= sinf(ang) * mod;
float y= cosf(ang) * mod;
#endif
const float rad= degToRad(rotation);
if(!relative){
p->pos= Vec3f(pos.x + x + offset.x, pos.y +
random.randRange(-radius / 2, radius / 2) + offset.y,
pos.z + y + offset.z);
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
}
else {// rotate it according to rotation
Vec3f combinedOffset=Vec3f(offset);
if(meshName!="" && unitModel!=NULL){
//printf("meshName set unitModel given\n");
bool foundMesh=false;
for(unsigned int i=0; i<unitModel->getMeshCount() ; i++){
//printf("meshName=%s\n",unitModel->getMesh(i)->getName().c_str());
if(unitModel->getMesh(i)->getName()==meshName){
const InterpolationData *data=unitModel->getMesh(i)->getInterpolationData();
const Vec3f *verticepos=data->getVertices();
//printf("verticepos %f %f %f\n",verticepos->x,verticepos->y,verticepos->z);
combinedOffset.x+=verticepos->x;
combinedOffset.y+=verticepos->y;
combinedOffset.z+=verticepos->z;
foundMesh=true;
break;
}
}
if( foundMesh == false ) {
string meshesFound="";
for(unsigned i=0; i<unitModel->getMeshCount() ; i++){
meshesFound+= unitModel->getMesh(i)->getName()+", ";
}
string errorString = "Warning: Particle system is trying to find mesh'"+meshName+"', but just found:\n'"+meshesFound+"' in file:\n'"+unitModel->getFileName()+"'\n";
//throw megaglest_runtime_error(errorString);
printf("%s",errorString.c_str());
}
}
#ifdef USE_STREFLOP
p->pos= Vec3f(pos.x+x+combinedOffset.z*streflop::sinf(static_cast<streflop::Simple>(rad))+combinedOffset.x*streflop::cosf(static_cast<streflop::Simple>(rad)), pos.y+random.randRange(-radius/2, radius/2)+combinedOffset.y, pos.z+y+(combinedOffset.z*streflop::cosf(static_cast<streflop::Simple>(rad))-combinedOffset.x*streflop::sinf(static_cast<streflop::Simple>(rad))));
#else
p->pos= Vec3f(pos.x + x + combinedOffset.z * sinf(rad) + combinedOffset.x * cosf(rad), pos.y + random.randRange(-radius / 2,
radius / 2) + combinedOffset.y, pos.z + y + (combinedOffset.z * cosf(rad) - combinedOffset.x * sinf(rad)));
#endif
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
}
p->speed= Vec3f(direction.x + direction.x * random.randRange(-0.5f, 0.5f), direction.y + direction.y
* random.randRange(-0.5f, 0.5f), direction.z + direction.z * random.randRange(-0.5f, 0.5f));
p->speed= p->speed * speed;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
if(relative && relativeDirection) {
#ifdef USE_STREFLOP
p->speed=Vec3f(p->speed.z*streflop::sinf(
static_cast<streflop::Simple>(rad)) + p->speed.x *
streflop::cosf(static_cast<streflop::Simple>(rad)),
p->speed.y, (p->speed.z * streflop::cosf(
static_cast<streflop::Simple>(rad)) - p->speed.x *
streflop::sinf(static_cast<streflop::Simple>(rad))));
#else
p->speed= Vec3f(p->speed.z * sinf(rad) + p->speed.x * cosf(rad),
p->speed.y, (p->speed.z * cosf(rad) - p->speed.x * sinf(rad)));
#endif
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
}
} break;
default: throw megaglest_runtime_error("bad shape");
}
//need to do that down here because we need p->speed for it.
p->speedUpConstant= Vec3f(speedUpConstant)*p->speed;
}
void UnitParticleSystem::update(){
// delay and timeline are only applicable for child particles
if(parent && delay > 0 && delay--) {
return;
}
if(parent && lifetime > 0 && !--lifetime) {
fade();
}
if(state != sPause) {
emissionRate-= emissionRateFade;
emissionRate = truncateDecimal<float>(emissionRate,6);
if(parent && emissionRate < 0.0f) {
fade();
}
}
if(fixed) {
fixedAddition= Vec3f(pos.x - oldPosition.x, pos.y - oldPosition.y, pos.z - oldPosition.z);
fixedAddition.x = truncateDecimal<float>(fixedAddition.x,6);
fixedAddition.y = truncateDecimal<float>(fixedAddition.y,6);
fixedAddition.z = truncateDecimal<float>(fixedAddition.z,6);
oldPosition= pos;
}
ParticleSystem::update();
}
void UnitParticleSystem::updateParticle(Particle *p){
float energyRatio;
if(alternations > 0){
int interval= (maxParticleEnergy / alternations);
float moduloValue= (float)((int)(static_cast<float> (p->energy)) % interval);
if(moduloValue < interval / 2){
energyRatio= (interval - moduloValue) / interval;
}
else{
energyRatio= moduloValue / interval;
}
energyRatio= clamp(energyRatio, 0.f, 1.f);
}
else{
energyRatio= clamp(static_cast<float> (p->energy) / maxParticleEnergy, 0.f, 1.f);
}
energyRatio = truncateDecimal<float>(energyRatio,6);
p->lastPos += p->speed;
p->lastPos.x = truncateDecimal<float>(p->lastPos.x,6);
p->lastPos.y = truncateDecimal<float>(p->lastPos.y,6);
p->lastPos.z = truncateDecimal<float>(p->lastPos.z,6);
p->pos += p->speed;
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
if(fixed) {
p->lastPos += fixedAddition;
p->lastPos.x = truncateDecimal<float>(p->lastPos.x,6);
p->lastPos.y = truncateDecimal<float>(p->lastPos.y,6);
p->lastPos.z = truncateDecimal<float>(p->lastPos.z,6);
p->pos += fixedAddition;
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
}
p->speed += p->accel;
p->speed += p->speedUpConstant;
p->speed=p->speed*(1+p->speedUpRelative);
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->color= color * energyRatio + colorNoEnergy * (1.0f - energyRatio);
if(isDaylightAffected==true) {
p->color.x=p->color.x*lightColor.x;
p->color.y=p->color.y*lightColor.y;
p->color.z=p->color.z*lightColor.z;
}
p->size= particleSize * energyRatio + sizeNoEnergy * (1.0f - energyRatio);
p->size = truncateDecimal<float>(p->size,6);
if(state == ParticleSystem::sFade || staticParticleCount < 1){
p->energy--;
}
else{
if(maxParticleEnergy > 2){
if(energyUp){
p->energy++;
}
else{
p->energy--;
}
if(p->energy == 1){
energyUp= true;
}
if(p->energy == maxParticleEnergy){
energyUp= false;
}
}
}
}
// ================= SET PARAMS ====================
void UnitParticleSystem::setWind(float windAngle, float windSpeed){
#ifdef USE_STREFLOP
this->windSpeed.x= streflop::sinf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= streflop::cosf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
#else
this->windSpeed.x= sinf(degToRad(windAngle)) * windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= cosf(degToRad(windAngle)) * windSpeed;
#endif
this->windSpeed.x = truncateDecimal<float>(this->windSpeed.x,6);
this->windSpeed.y = truncateDecimal<float>(this->windSpeed.y,6);
this->windSpeed.z = truncateDecimal<float>(this->windSpeed.z,6);
}
void UnitParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *unitParticleSystemNode = rootNode->addChild("UnitParticleSystem");
GameParticleSystem::saveGame(unitParticleSystemNode);
// float radius;
unitParticleSystemNode->addAttribute("radius",floatToStr(radius,6), mapTagReplacements);
// float minRadius;
unitParticleSystemNode->addAttribute("minRadius",floatToStr(minRadius,6), mapTagReplacements);
// Vec3f windSpeed;
unitParticleSystemNode->addAttribute("windSpeed",windSpeed.getString(), mapTagReplacements);
// Vec3f cRotation;
unitParticleSystemNode->addAttribute("cRotation",cRotation.getString(), mapTagReplacements);
// Vec3f fixedAddition;
unitParticleSystemNode->addAttribute("fixedAddition",fixedAddition.getString(), mapTagReplacements);
// Vec3f oldPosition;
unitParticleSystemNode->addAttribute("oldPosition",oldPosition.getString(), mapTagReplacements);
// bool energyUp;
unitParticleSystemNode->addAttribute("energyUp",intToStr(energyUp), mapTagReplacements);
// float startTime;
unitParticleSystemNode->addAttribute("startTime",floatToStr(startTime,6), mapTagReplacements);
// float endTime;
unitParticleSystemNode->addAttribute("endTime",floatToStr(endTime,6), mapTagReplacements);
// bool relative;
unitParticleSystemNode->addAttribute("relative",intToStr(relative), mapTagReplacements);
// bool relativeDirection;
unitParticleSystemNode->addAttribute("relativeDirection",intToStr(relativeDirection), mapTagReplacements);
// bool fixed;
unitParticleSystemNode->addAttribute("fixed",intToStr(fixed), mapTagReplacements);
// Shape shape;
unitParticleSystemNode->addAttribute("shape",intToStr(shape), mapTagReplacements);
// float angle;
unitParticleSystemNode->addAttribute("angle",floatToStr(angle,6), mapTagReplacements);
// float sizeNoEnergy;
unitParticleSystemNode->addAttribute("sizeNoEnergy",floatToStr(sizeNoEnergy,6), mapTagReplacements);
// float gravity;
unitParticleSystemNode->addAttribute("gravity",floatToStr(gravity,6), mapTagReplacements);
// float rotation;
unitParticleSystemNode->addAttribute("rotation",floatToStr(rotation,6), mapTagReplacements);
// bool isVisibleAtNight;
unitParticleSystemNode->addAttribute("isVisibleAtNight",intToStr(isVisibleAtNight), mapTagReplacements);
// bool isVisibleAtDay;
unitParticleSystemNode->addAttribute("isVisibleAtDay",intToStr(isVisibleAtDay), mapTagReplacements);
// bool isDaylightAffected;
unitParticleSystemNode->addAttribute("isDaylightAffected",intToStr(isDaylightAffected), mapTagReplacements);
// bool radiusBasedStartenergy;
unitParticleSystemNode->addAttribute("radiusBasedStartenergy",intToStr(radiusBasedStartenergy), mapTagReplacements);
// int staticParticleCount;
unitParticleSystemNode->addAttribute("staticParticleCount",intToStr(staticParticleCount), mapTagReplacements);
// int delay;
unitParticleSystemNode->addAttribute("delay",intToStr(delay), mapTagReplacements);
// int lifetime;
unitParticleSystemNode->addAttribute("lifetime",intToStr(lifetime), mapTagReplacements);
// float emissionRateFade;
unitParticleSystemNode->addAttribute("emissionRateFade",floatToStr(emissionRateFade,6), mapTagReplacements);
// GameParticleSystem* parent;
//if(parent != NULL) {
// parent->saveGame(unitParticleSystemNode);
//}
}
void UnitParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *unitParticleSystemNode = rootNode;
//printf("Load Smoke particle (UnitParticleSystem)\n");
GameParticleSystem::loadGame(unitParticleSystemNode);
// float radius;
radius = unitParticleSystemNode->getAttribute("radius")->getFloatValue();
// float minRadius;
minRadius = unitParticleSystemNode->getAttribute("minRadius")->getFloatValue();
// Vec3f windSpeed;
windSpeed = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("windSpeed")->getValue());
// Vec3f cRotation;
windSpeed = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("cRotation")->getValue());
// Vec3f fixedAddition;
fixedAddition = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("fixedAddition")->getValue());
// Vec3f oldPosition;
oldPosition = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("oldPosition")->getValue());
// bool energyUp;
energyUp = unitParticleSystemNode->getAttribute("energyUp")->getIntValue() != 0;
// float startTime;
startTime = unitParticleSystemNode->getAttribute("startTime")->getFloatValue();
// float endTime;
endTime = unitParticleSystemNode->getAttribute("endTime")->getFloatValue();
// bool relative;
relative = unitParticleSystemNode->getAttribute("relative")->getIntValue() != 0;
// bool relativeDirection;
relativeDirection = unitParticleSystemNode->getAttribute("relativeDirection")->getIntValue() != 0;
// bool fixed;
fixed = unitParticleSystemNode->getAttribute("fixed")->getIntValue() != 0;
// Shape shape;
shape = static_cast<Shape>(unitParticleSystemNode->getAttribute("shape")->getIntValue());
// float angle;
angle = unitParticleSystemNode->getAttribute("angle")->getFloatValue();
// float sizeNoEnergy;
sizeNoEnergy = unitParticleSystemNode->getAttribute("sizeNoEnergy")->getFloatValue();
// float gravity;
gravity = unitParticleSystemNode->getAttribute("gravity")->getFloatValue();
// float rotation;
rotation = unitParticleSystemNode->getAttribute("rotation")->getFloatValue();
// bool isVisibleAtNight;
isVisibleAtNight = unitParticleSystemNode->getAttribute("isVisibleAtNight")->getIntValue() != 0;
// bool isVisibleAtDay;
isVisibleAtDay = unitParticleSystemNode->getAttribute("isVisibleAtDay")->getIntValue() != 0;
// bool isDaylightAffected;
isDaylightAffected = unitParticleSystemNode->getAttribute("isDaylightAffected")->getIntValue() != 0;
// bool radiusBasedStartenergy;
radiusBasedStartenergy = unitParticleSystemNode->getAttribute("radiusBasedStartenergy")->getIntValue() != 0;
// int staticParticleCount;
staticParticleCount = unitParticleSystemNode->getAttribute("staticParticleCount")->getIntValue();
// int delay;
delay = unitParticleSystemNode->getAttribute("delay")->getIntValue();
// int lifetime;
lifetime = unitParticleSystemNode->getAttribute("lifetime")->getIntValue();
// float emissionRateFade;
emissionRateFade = unitParticleSystemNode->getAttribute("emissionRateFade")->getFloatValue();
// GameParticleSystem* parent;
parent = NULL;
//if(parent != NULL) {
// parent->saveGame(unitParticleSystemNode);
//}
//if(unitParticleSystemNode->hasChild("GameParticleSystem") == true) {
// void GameParticleSystem::saveGame(XmlNode *rootNode)
// std::map<string,string> mapTagReplacements;
// XmlNode *gameParticleSystemNode = rootNode->addChild("GameParticleSystem");
//XmlNode *gameParticleSystemNode = unitParticleSystemNode->getChild("GameParticleSystem");
//!!!
//}
}
Checksum UnitParticleSystem::getCRC() {
Checksum crcForParticleSystem = GameParticleSystem::getCRC();
return crcForParticleSystem;
}
string UnitParticleSystem::toString() const {
string result = GameParticleSystem::toString();
result += "\nUnitParticleSystem ";
result += "\nradius = " + floatToStr(radius);
result += "\nminRadius = " + floatToStr(minRadius);
result += "\nwindSpeed = " + windSpeed.getString();
result += "\ncRotation = " + cRotation.getString();
result += "\nfixedAddition = " + fixedAddition.getString();
result += "\noldPosition = " + oldPosition.getString();
result += "\nenergyUp = " + intToStr(energyUp);
result += "\nstartTime = " + floatToStr(startTime);
result += "\nendTime = " + floatToStr(endTime);
result += "\nrelative = " + intToStr(relative);
result += "\nrelativeDirection = " + intToStr(relativeDirection);
result += "\nfixed = " + intToStr(fixed);
result += "\nshape = " + intToStr(shape);
result += "\nangle = " + floatToStr(angle);
result += "\nsizeNoEnergy = " + floatToStr(sizeNoEnergy);
result += "\ngravity = " + floatToStr(gravity);
result += "\nrotation = " + floatToStr(rotation);
result += "\nisVisibleAtNight = " + intToStr(isVisibleAtNight);
result += "\nisVisibleAtDay = " + intToStr(isVisibleAtDay);
result += "\nisDaylightAffected = " + intToStr(isDaylightAffected);
result += "\nradiusBasedStartenergy = " + intToStr(radiusBasedStartenergy);
result += "\nstaticParticleCount = " + intToStr(staticParticleCount);
result += "\ndelay = " + intToStr(delay);
result += "\nlifetime = " + intToStr(lifetime);
result += "\nemissionRateFade = " + floatToStr(emissionRateFade);
//GameParticleSystem* parent;
return result;
}
// ===========================================================================
// RainParticleSystem
// ===========================================================================
RainParticleSystem::RainParticleSystem(int particleCount) :
ParticleSystem(particleCount){
setWind(0.0f, 0.0f);
setRadius(20.0f);
setEmissionRate(25.0f);
setParticleSize(3.0f);
setColor(Vec4f(0.5f, 0.5f, 0.5f, 0.3f));
setSpeed(0.2f);
}
void RainParticleSystem::render(ParticleRenderer *pr, ModelRenderer *mr){
pr->renderSystemLineAlpha(this);
}
void RainParticleSystem::initParticle(Particle *p, int particleIndex){
ParticleSystem::initParticle(p, particleIndex);
float x= random.randRange(-radius, radius);
float y= random.randRange(-radius, radius);
p->color= color;
p->energy= 10000;
p->pos= Vec3f(pos.x + x, pos.y, pos.z + y);
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
p->lastPos= p->pos;
p->speed= Vec3f(random.randRange(-speed / 10, speed / 10), -speed,
random.randRange(-speed / 10, speed / 10)) + windSpeed;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
}
bool RainParticleSystem::deathTest(Particle *p){
return p->pos.y < 0;
}
void RainParticleSystem::setRadius(float radius) {
this->radius= radius;
}
void RainParticleSystem::setWind(float windAngle, float windSpeed){
#ifdef USE_STREFLOP
this->windSpeed.x= streflop::sinf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= streflop::cosf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
#else
this->windSpeed.x= sinf(degToRad(windAngle)) * windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= cosf(degToRad(windAngle)) * windSpeed;
#endif
this->windSpeed.x = truncateDecimal<float>(this->windSpeed.x,6);
this->windSpeed.y = truncateDecimal<float>(this->windSpeed.y,6);
this->windSpeed.z = truncateDecimal<float>(this->windSpeed.z,6);
}
Checksum RainParticleSystem::getCRC() {
Checksum crcForParticleSystem = ParticleSystem::getCRC();
return crcForParticleSystem;
}
string RainParticleSystem::toString() const {
string result = ParticleSystem::toString();
result += "\nRainParticleSystem ";
result += "\nwindSpeed = " + windSpeed.getString();
result += "\nradius = " + floatToStr(radius);
return result;
}
// ===========================================================================
// SnowParticleSystem
// ===========================================================================
SnowParticleSystem::SnowParticleSystem(int particleCount) :
ParticleSystem(particleCount){
setWind(0.0f, 0.0f);
setRadius(30.0f);
setEmissionRate(2.0f);
setParticleSize(0.2f);
setColor(Vec4f(0.8f, 0.8f, 0.8f, 0.8f));
setSpeed(0.05f);
}
void SnowParticleSystem::initParticle(Particle *p, int particleIndex){
ParticleSystem::initParticle(p, particleIndex);
float x= random.randRange(-radius, radius);
float y= random.randRange(-radius, radius);
p->color= color;
p->energy= 10000;
p->pos= Vec3f(pos.x + x, pos.y, pos.z + y);
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
p->lastPos= p->pos;
p->speed= Vec3f(0.0f, -speed, 0.0f) + windSpeed;
p->speed.x+= random.randRange(-0.005f, 0.005f);
p->speed.y+= random.randRange(-0.005f, 0.005f);
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
}
bool SnowParticleSystem::deathTest(Particle *p){
return p->pos.y < 0;
}
void SnowParticleSystem::setRadius(float radius){
this->radius= radius;
}
void SnowParticleSystem::setWind(float windAngle, float windSpeed){
#ifdef USE_STREFLOP
this->windSpeed.x= streflop::sinf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= streflop::cosf(static_cast<streflop::Simple>(degToRad(windAngle)))*windSpeed;
#else
this->windSpeed.x= sinf(degToRad(windAngle)) * windSpeed;
this->windSpeed.y= 0.0f;
this->windSpeed.z= cosf(degToRad(windAngle)) * windSpeed;
#endif
this->windSpeed.x = truncateDecimal<float>(this->windSpeed.x,6);
this->windSpeed.y = truncateDecimal<float>(this->windSpeed.y,6);
this->windSpeed.z = truncateDecimal<float>(this->windSpeed.z,6);
}
Checksum SnowParticleSystem::getCRC() {
Checksum crcForParticleSystem = ParticleSystem::getCRC();
return crcForParticleSystem;
}
string SnowParticleSystem::toString() const {
string result = ParticleSystem::toString();
result += "\nSnowParticleSystem ";
result += "\nwindSpeed = " + windSpeed.getString();
result += "\nradius = " + floatToStr(radius);
return result;
}
// ===========================================================================
// AttackParticleSystem
// ===========================================================================
AttackParticleSystem::AttackParticleSystem(int particleCount) :
GameParticleSystem(particleCount){
primitive= pQuad;
gravity= 0.0f;
sizeNoEnergy = 0.0;
}
void AttackParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *attackParticleSystemNode = rootNode->addChild("AttackParticleSystem");
GameParticleSystem::saveGame(attackParticleSystemNode);
// float sizeNoEnergy;
attackParticleSystemNode->addAttribute("sizeNoEnergy",floatToStr(sizeNoEnergy,6), mapTagReplacements);
// float gravity;
attackParticleSystemNode->addAttribute("gravity",floatToStr(gravity,6), mapTagReplacements);
}
void AttackParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *attackParticleSystemNode = rootNode;
GameParticleSystem::loadGame(attackParticleSystemNode);
// float sizeNoEnergy;
sizeNoEnergy = attackParticleSystemNode->getAttribute("sizeNoEnergy")->getFloatValue();
// float gravity;
gravity = attackParticleSystemNode->getAttribute("gravity")->getFloatValue();
}
Checksum AttackParticleSystem::getCRC() {
Checksum crcForParticleSystem = GameParticleSystem::getCRC();
return crcForParticleSystem;
}
string AttackParticleSystem::toString() const {
string result = GameParticleSystem::toString();
result += "\nAttackParticleSystem ";
result += "\nsizeNoEnergy = " + floatToStr(sizeNoEnergy);
result += "\ngravity = " + floatToStr(gravity);
return result;
}
// ===========================================================================
// ProjectileParticleSystem
// ===========================================================================
ProjectileParticleSystem::ProjectileParticleSystem(int particleCount) :
AttackParticleSystem(particleCount){
setEmissionRate(20.0f);
setColor(Vec4f(1.0f, 0.3f, 0.0f, 0.5f));
setMaxParticleEnergy(100);
setVarParticleEnergy(50);
setParticleSize(0.4f);
setSpeed(0.14f);
trajectory= tLinear;
trajectorySpeed= 1.0f;
trajectoryScale= 1.0f;
trajectoryFrequency= 1.0f;
modelCycle=0.0f;
nextParticleSystem= NULL;
arriveDestinationDistance = 0.0f;
//printf("#aXX trajectorySpeed = %f\n",trajectorySpeed);
}
ProjectileParticleSystem::~ProjectileParticleSystem(){
if(nextParticleSystem != NULL){
nextParticleSystem->prevParticleSystem= NULL;
}
}
void ProjectileParticleSystem::link(SplashParticleSystem *particleSystem){
nextParticleSystem= particleSystem;
nextParticleSystem->setVisible(false);
nextParticleSystem->setState(sPause);
nextParticleSystem->prevParticleSystem= this;
}
void ProjectileParticleSystem::update(){
//printf("Projectile particle system updating...\n");
if(state == sPlay){
lastPos = pos;
flatPos += zVector * truncateDecimal<float>(trajectorySpeed,6);
flatPos.x = truncateDecimal<float>(flatPos.x,6);
flatPos.y = truncateDecimal<float>(flatPos.y,6);
flatPos.z = truncateDecimal<float>(flatPos.z,6);
Vec3f targetVector = endPos - startPos;
targetVector.x = truncateDecimal<float>(targetVector.x,6);
targetVector.y = truncateDecimal<float>(targetVector.y,6);
targetVector.z = truncateDecimal<float>(targetVector.z,6);
Vec3f currentVector = flatPos - startPos;
currentVector.x = truncateDecimal<float>(currentVector.x,6);
currentVector.y = truncateDecimal<float>(currentVector.y,6);
currentVector.z = truncateDecimal<float>(currentVector.z,6);
// ratio
float relative= clamp(currentVector.length() / targetVector.length(), 0.0f, 1.0f);
relative = truncateDecimal<float>(relative,6);
float absolute = relative;
setTween(relative,absolute);
// trajectory
switch(trajectory) {
case tLinear: {
pos= flatPos;
}
break;
case tParabolic: {
float scaledT = truncateDecimal<float>(2.0f * (relative - 0.5f),6);
float paraboleY = truncateDecimal<float>((1.0f - scaledT * scaledT) * trajectoryScale,6);
pos= flatPos;
pos.y += paraboleY;
pos.y = truncateDecimal<float>(pos.y,6);
}
break;
case tSpiral: {
pos= flatPos;
#ifdef USE_STREFLOP
pos+= xVector * streflop::cos(static_cast<streflop::Simple>(relative * trajectoryFrequency * targetVector.length())) * trajectoryScale;
pos.x = truncateDecimal<float>(pos.x,6);
pos.y = truncateDecimal<float>(pos.y,6);
pos.z = truncateDecimal<float>(pos.z,6);
pos+= yVector * streflop::sin(static_cast<streflop::Simple>(relative * trajectoryFrequency * targetVector.length())) * trajectoryScale;
#else
pos+= xVector * cos(relative * trajectoryFrequency * targetVector.length()) * trajectoryScale;
pos.x = truncateDecimal<float>(pos.x,6);
pos.y = truncateDecimal<float>(pos.y,6);
pos.z = truncateDecimal<float>(pos.z,6);
pos+= yVector * sin(relative * trajectoryFrequency * targetVector.length()) * trajectoryScale;
#endif
pos.x = truncateDecimal<float>(pos.x,6);
pos.y = truncateDecimal<float>(pos.y,6);
pos.z = truncateDecimal<float>(pos.z,6);
}
break;
default:
assert(false);
break;
}
direction= (pos - lastPos);
direction.x = truncateDecimal<float>(direction.x,6);
direction.y = truncateDecimal<float>(direction.y,6);
direction.z = truncateDecimal<float>(direction.z,6);
direction.normalize();
direction.x = truncateDecimal<float>(direction.x,6);
direction.y = truncateDecimal<float>(direction.y,6);
direction.z = truncateDecimal<float>(direction.z,6);
// trigger update of child particles
positionChildren();
rotateChildren();
//arrive destination
arriveDestinationDistance = truncateDecimal<float>(flatPos.dist(endPos),6);
if(this->particleOwner != NULL) {
char szBuf[8096]="";
snprintf(szBuf,8095,"LINE: %d arriveDestinationDistance = %f",__LINE__,arriveDestinationDistance);
this->particleOwner->logParticleInfo(szBuf);
}
if(arriveDestinationDistance < 0.5f) {
fade();
model= NULL;
if(particleObserver != NULL){
particleObserver->update(this);
particleObserver=NULL;
}
if(nextParticleSystem != NULL){
nextParticleSystem->setVisible(getVisible());
nextParticleSystem->setState(sPlay);
nextParticleSystem->setPos(endPos);
}
}
}
ParticleSystem::update();
}
void ProjectileParticleSystem::rotateChildren() {
//### only on horizontal plane :(
#ifdef USE_STREFLOP
float rotation = truncateDecimal<float>(streflop::atan2(static_cast<streflop::Simple>(direction.x), static_cast<streflop::Simple>(direction.z)),6);
#else
float rotation = truncateDecimal<float>(atan2(direction.x, direction.z),6);
#endif
rotation = truncateDecimal<float>(radToDeg(rotation),6);
for(Children::iterator it = children.begin(); it != children.end(); ++it)
(*it)->setRotation(rotation);
}
void ProjectileParticleSystem::initParticle(Particle *p, int particleIndex){
ParticleSystem::initParticle(p, particleIndex);
float t= static_cast<float> (particleIndex) / emissionRate;
t = truncateDecimal<float>(t,6);
p->pos= pos + (lastPos - pos) * t;
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
p->lastPos= lastPos;
p->speed= Vec3f(random.randRange(-0.1f, 0.1f), random.randRange(-0.1f, 0.1f),
random.randRange(-0.1f, 0.1f)) * speed;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->accel= Vec3f(0.0f, -gravity, 0.0f);
p->accel.x = truncateDecimal<float>(p->accel.x,6);
p->accel.y = truncateDecimal<float>(p->accel.y,6);
p->accel.z = truncateDecimal<float>(p->accel.z,6);
updateParticle(p);
}
void ProjectileParticleSystem::updateParticle(Particle *p){
float energyRatio= clamp(static_cast<float> (p->energy) / maxParticleEnergy, 0.f, 1.f);
energyRatio = truncateDecimal<float>(energyRatio,6);
p->lastPos += p->speed;
p->lastPos.x = truncateDecimal<float>(p->lastPos.x,6);
p->lastPos.y = truncateDecimal<float>(p->lastPos.y,6);
p->lastPos.z = truncateDecimal<float>(p->lastPos.z,6);
p->pos += p->speed;
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
p->speed += p->accel;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->color= color * energyRatio + colorNoEnergy * (1.0f - energyRatio);
p->size = particleSize * energyRatio + sizeNoEnergy * (1.0f - energyRatio);
p->size = truncateDecimal<float>(p->size,6);
p->energy--;
}
void ProjectileParticleSystem::setPath(Vec3f startPos, Vec3f endPos) {
startPos.x = truncateDecimal<float>(startPos.x,6);
startPos.y = truncateDecimal<float>(startPos.y,6);
startPos.z = truncateDecimal<float>(startPos.z,6);
endPos.x = truncateDecimal<float>(endPos.x,6);
endPos.y = truncateDecimal<float>(endPos.y,6);
endPos.z = truncateDecimal<float>(endPos.z,6);
//compute axis
zVector= endPos - startPos;
zVector.x = truncateDecimal<float>(zVector.x,6);
zVector.y = truncateDecimal<float>(zVector.y,6);
zVector.z = truncateDecimal<float>(zVector.z,6);
zVector.normalize();
zVector.x = truncateDecimal<float>(zVector.x,6);
zVector.y = truncateDecimal<float>(zVector.y,6);
zVector.z = truncateDecimal<float>(zVector.z,6);
yVector= Vec3f(0.0f, 1.0f, 0.0f);
xVector= zVector.cross(yVector);
xVector.x = truncateDecimal<float>(xVector.x,6);
xVector.y = truncateDecimal<float>(xVector.y,6);
xVector.z = truncateDecimal<float>(xVector.z,6);
//apply offset
startPos += xVector * offset.x;
startPos.x = truncateDecimal<float>(startPos.x,6);
startPos.y = truncateDecimal<float>(startPos.y,6);
startPos.z = truncateDecimal<float>(startPos.z,6);
startPos+= yVector * offset.y;
startPos.x = truncateDecimal<float>(startPos.x,6);
startPos.y = truncateDecimal<float>(startPos.y,6);
startPos.z = truncateDecimal<float>(startPos.z,6);
startPos+= zVector * offset.z;
startPos.x = truncateDecimal<float>(startPos.x,6);
startPos.y = truncateDecimal<float>(startPos.y,6);
startPos.z = truncateDecimal<float>(startPos.z,6);
pos= startPos;
lastPos= startPos;
flatPos= startPos;
//recompute axis
zVector= endPos - startPos;
zVector.x = truncateDecimal<float>(zVector.x,6);
zVector.y = truncateDecimal<float>(zVector.y,6);
zVector.z = truncateDecimal<float>(zVector.z,6);
zVector.normalize();
zVector.x = truncateDecimal<float>(zVector.x,6);
zVector.y = truncateDecimal<float>(zVector.y,6);
zVector.z = truncateDecimal<float>(zVector.z,6);
yVector= Vec3f(0.0f, 1.0f, 0.0f);
xVector= zVector.cross(yVector);
xVector.x = truncateDecimal<float>(xVector.x,6);
xVector.y = truncateDecimal<float>(xVector.y,6);
xVector.z = truncateDecimal<float>(xVector.z,6);
// set members
this->startPos= startPos;
this->endPos= endPos;
// direction
direction = (endPos - lastPos);
direction.x = truncateDecimal<float>(direction.x,6);
direction.y = truncateDecimal<float>(direction.y,6);
direction.z = truncateDecimal<float>(direction.z,6);
direction.normalize();
direction.x = truncateDecimal<float>(direction.x,6);
direction.y = truncateDecimal<float>(direction.y,6);
direction.z = truncateDecimal<float>(direction.z,6);
rotateChildren();
}
ProjectileParticleSystem::Trajectory ProjectileParticleSystem::strToTrajectory(const string &str){
if(str == "linear"){
return tLinear;
}
else if(str == "parabolic"){
return tParabolic;
}
else if(str == "spiral"){
return tSpiral;
}
else{
throw megaglest_runtime_error("Unknown particle system trajectory: " + str);
}
}
void ProjectileParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *projectileParticleSystemNode = rootNode->addChild("ProjectileParticleSystem");
AttackParticleSystem::saveGame(projectileParticleSystemNode);
// SplashParticleSystem *nextParticleSystem;
if(nextParticleSystem != NULL) {
nextParticleSystem->saveGame(projectileParticleSystemNode);
}
// Vec3f lastPos;
projectileParticleSystemNode->addAttribute("lastPos",lastPos.getString(), mapTagReplacements);
// Vec3f startPos;
projectileParticleSystemNode->addAttribute("startPos",startPos.getString(), mapTagReplacements);
// Vec3f endPos;
projectileParticleSystemNode->addAttribute("endPos",endPos.getString(), mapTagReplacements);
// Vec3f flatPos;
projectileParticleSystemNode->addAttribute("flatPos",flatPos.getString(), mapTagReplacements);
//
// Vec3f xVector;
projectileParticleSystemNode->addAttribute("xVector",xVector.getString(), mapTagReplacements);
// Vec3f yVector;
projectileParticleSystemNode->addAttribute("yVector",yVector.getString(), mapTagReplacements);
// Vec3f zVector;
projectileParticleSystemNode->addAttribute("zVector",zVector.getString(), mapTagReplacements);
// Trajectory trajectory;
projectileParticleSystemNode->addAttribute("trajectory",intToStr(trajectory), mapTagReplacements);
// float trajectorySpeed;
projectileParticleSystemNode->addAttribute("trajectorySpeed",floatToStr(trajectorySpeed,6), mapTagReplacements);
// //parabolic
// float trajectoryScale;
projectileParticleSystemNode->addAttribute("trajectoryScale",floatToStr(trajectoryScale,6), mapTagReplacements);
// float trajectoryFrequency;
projectileParticleSystemNode->addAttribute("trajectoryFrequency",floatToStr(trajectoryFrequency,6), mapTagReplacements);
}
void ProjectileParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *projectileParticleSystemNode = rootNode;
AttackParticleSystem::loadGame(projectileParticleSystemNode);
// SplashParticleSystem *nextParticleSystem;
// if(nextParticleSystem != NULL) {
// nextParticleSystem->saveGame(projectileParticleSystemNode);
// }
if(projectileParticleSystemNode->hasChild("SplashParticleSystem") == true) {
XmlNode *splashParticleSystemNode = projectileParticleSystemNode->getChild("SplashParticleSystem");
nextParticleSystem = new SplashParticleSystem();
nextParticleSystem->setParticleOwner(this->getParticleOwner());
nextParticleSystem->loadGame(splashParticleSystemNode);
}
// Vec3f lastPos;
lastPos = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("lastPos")->getValue());
// Vec3f startPos;
startPos = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("startPos")->getValue());
// Vec3f endPos;
endPos = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("endPos")->getValue());
// Vec3f flatPos;
flatPos = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("flatPos")->getValue());
//
// Vec3f xVector;
xVector = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("xVector")->getValue());
// Vec3f yVector;
yVector = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("yVector")->getValue());
// Vec3f zVector;
zVector = Vec3f::strToVec3(projectileParticleSystemNode->getAttribute("zVector")->getValue());
// Trajectory trajectory;
trajectory = static_cast<Trajectory>(projectileParticleSystemNode->getAttribute("trajectory")->getIntValue());
// float trajectorySpeed;
trajectorySpeed = projectileParticleSystemNode->getAttribute("trajectorySpeed")->getFloatValue();
// //parabolic
// float trajectoryScale;
trajectoryScale = projectileParticleSystemNode->getAttribute("trajectoryScale")->getFloatValue();
// float trajectoryFrequency;
trajectoryFrequency = projectileParticleSystemNode->getAttribute("trajectoryFrequency")->getFloatValue();
}
Checksum ProjectileParticleSystem::getCRC() {
Checksum crcForParticleSystem = AttackParticleSystem::getCRC();
return crcForParticleSystem;
}
string ProjectileParticleSystem::toString() const {
string result = AttackParticleSystem::toString();
result += "\nProjectileParticleSystem ";
if(nextParticleSystem != NULL) {
//result += "\nnextParticleSystem = " + nextParticleSystem->toString() + "\n";
result += "\nnextParticleSystem = NOT NULL\n";
}
result += "\nlastPos = " + lastPos.getString();
result += "\nstartPos = " + startPos.getString();
result += "\nendPos = " + endPos.getString();
result += "\nflatPos = " + flatPos.getString();
result += "\nxVector = " + xVector.getString();
result += "\nyVector = " + yVector.getString();
result += "\nzVector = " + zVector.getString();
result += "\ntrajectory = " + intToStr(trajectory);
result += "\ntrajectorySpeed = " + floatToStr(trajectorySpeed);
result += "\ntrajectoryScale = " + floatToStr(trajectoryScale);
result += "\ntrajectoryFrequency = " + floatToStr(trajectoryFrequency);
result += "\narriveDestinationDistance = " + floatToStr(arriveDestinationDistance);
return result;
}
// ===========================================================================
// SplashParticleSystem
// ===========================================================================
SplashParticleSystem::SplashParticleSystem(int particleCount) :
AttackParticleSystem(particleCount){
setColor(Vec4f(1.0f, 0.3f, 0.0f, 0.8f));
setMaxParticleEnergy(100);
setVarParticleEnergy(50);
setParticleSize(1.0f);
setSpeed(0.003f);
prevParticleSystem= NULL;
emissionRateFade= 1.0f;
verticalSpreadA= 1.0f;
verticalSpreadB= 0.0f;
horizontalSpreadA= 1.0f;
horizontalSpreadB= 0.0f;
startEmissionRate= 0.0f;
}
SplashParticleSystem::~SplashParticleSystem(){
if(prevParticleSystem != NULL){
prevParticleSystem->nextParticleSystem= NULL;
}
}
void SplashParticleSystem::initParticleSystem() {
startEmissionRate = emissionRate;
}
void SplashParticleSystem::update() {
ParticleSystem::update();
if(state != sPause) {
emissionRate -= emissionRateFade;
emissionRate = truncateDecimal<float>(emissionRate,6);
float t = 1.0f - ((emissionRate + startEmissionRate) / (startEmissionRate * 2.0f));
t = truncateDecimal<float>(t,6);
t= clamp(t, 0.0f, 1.0f);
setTween(t,t);
if(this->particleOwner != NULL) {
char szBuf[8096]="";
snprintf(szBuf,8095,"LINE: %d emissionRate = %f",__LINE__,emissionRate);
this->particleOwner->logParticleInfo(szBuf);
}
if(emissionRate < 0.0f) {//otherwise this system lives forever!
fade();
}
}
}
void SplashParticleSystem::initParticle(Particle *p, int particleIndex){
p->pos= pos;
p->lastPos= p->pos;
p->energy= maxParticleEnergy;
p->size= particleSize;
p->color= color;
p->speedUpRelative= speedUpRelative;
p->speed= Vec3f(horizontalSpreadA * random.randRange(-1.0f, 1.0f) + horizontalSpreadB, verticalSpreadA
* random.randRange(-1.0f, 1.0f) + verticalSpreadB, horizontalSpreadA * random.randRange(-1.0f, 1.0f)
+ horizontalSpreadB);
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->speed.normalize();
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->speed= p->speed * speed;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->accel= Vec3f(0.0f, -gravity, 0.0f);
p->accel.x = truncateDecimal<float>(p->accel.x,6);
p->accel.y = truncateDecimal<float>(p->accel.y,6);
p->accel.z = truncateDecimal<float>(p->accel.z,6);
p->speedUpConstant= Vec3f(speedUpConstant)*p->speed;
}
void SplashParticleSystem::updateParticle(Particle *p){
float energyRatio= clamp(static_cast<float> (p->energy) / maxParticleEnergy, 0.f, 1.f);
p->lastPos= p->pos;
p->pos= p->pos + p->speed;
p->pos.x = truncateDecimal<float>(p->pos.x,6);
p->pos.y = truncateDecimal<float>(p->pos.y,6);
p->pos.z = truncateDecimal<float>(p->pos.z,6);
p->speed += p->speedUpConstant;
p->speed=p->speed*(1+p->speedUpRelative);
p->speed= p->speed + p->accel;
p->speed.x = truncateDecimal<float>(p->speed.x,6);
p->speed.y = truncateDecimal<float>(p->speed.y,6);
p->speed.z = truncateDecimal<float>(p->speed.z,6);
p->energy--;
p->color= color * energyRatio + colorNoEnergy * (1.0f - energyRatio);
p->size= particleSize * energyRatio + sizeNoEnergy * (1.0f - energyRatio);
p->size = truncateDecimal<float>(p->size,6);
}
void SplashParticleSystem::saveGame(XmlNode *rootNode) {
std::map<string,string> mapTagReplacements;
XmlNode *splashParticleSystemNode = rootNode->addChild("SplashParticleSystem");
AttackParticleSystem::saveGame(splashParticleSystemNode);
// ProjectileParticleSystem *prevParticleSystem;
if(prevParticleSystem != NULL) {
prevParticleSystem->saveGame(splashParticleSystemNode);
}
// float emissionRateFade;
splashParticleSystemNode->addAttribute("emissionRateFade",floatToStr(emissionRateFade,6), mapTagReplacements);
// float verticalSpreadA;
splashParticleSystemNode->addAttribute("verticalSpreadA",floatToStr(verticalSpreadA,6), mapTagReplacements);
// float verticalSpreadB;
splashParticleSystemNode->addAttribute("verticalSpreadB",floatToStr(verticalSpreadB,6), mapTagReplacements);
// float horizontalSpreadA;
splashParticleSystemNode->addAttribute("horizontalSpreadA",floatToStr(horizontalSpreadA,6), mapTagReplacements);
// float horizontalSpreadB;
splashParticleSystemNode->addAttribute("horizontalSpreadB",floatToStr(horizontalSpreadB,6), mapTagReplacements);
//
// float startEmissionRate;
splashParticleSystemNode->addAttribute("startEmissionRate",floatToStr(startEmissionRate,6), mapTagReplacements);
}
void SplashParticleSystem::loadGame(const XmlNode *rootNode) {
const XmlNode *splashParticleSystemNode = rootNode;
AttackParticleSystem::loadGame(splashParticleSystemNode);
// ProjectileParticleSystem *prevParticleSystem;
// if(nextParticleSystem != NULL) {
// nextParticleSystem->saveGame(projectileParticleSystemNode);
// }
if(splashParticleSystemNode->hasChild("ProjectileParticleSystem") == true) {
XmlNode *projectileParticleSystemNode = splashParticleSystemNode->getChild("ProjectileParticleSystem");
prevParticleSystem = new ProjectileParticleSystem();
prevParticleSystem->setParticleOwner(this->getParticleOwner());
prevParticleSystem->loadGame(projectileParticleSystemNode);
}
// float emissionRateFade;
emissionRateFade = splashParticleSystemNode->getAttribute("emissionRateFade")->getFloatValue();
// float verticalSpreadA;
verticalSpreadA = splashParticleSystemNode->getAttribute("verticalSpreadA")->getFloatValue();
// float verticalSpreadB;
verticalSpreadB = splashParticleSystemNode->getAttribute("verticalSpreadB")->getFloatValue();
// float horizontalSpreadA;
horizontalSpreadA = splashParticleSystemNode->getAttribute("horizontalSpreadA")->getFloatValue();
// float horizontalSpreadB;
horizontalSpreadB = splashParticleSystemNode->getAttribute("horizontalSpreadB")->getFloatValue();
// float startEmissionRate;
startEmissionRate = splashParticleSystemNode->getAttribute("startEmissionRate")->getFloatValue();
}
Checksum SplashParticleSystem::getCRC() {
Checksum crcForParticleSystem = AttackParticleSystem::getCRC();
return crcForParticleSystem;
}
string SplashParticleSystem::toString() const {
string result = AttackParticleSystem::toString();
result += "\nSplashParticleSystem ";
if(prevParticleSystem != NULL) {
//result += "\nprevParticleSystem = " + prevParticleSystem->toString() + "\n";
result += "\nprevParticleSystem = NOT NULL\n";
}
result += "\nemissionRateFade = " + floatToStr(emissionRateFade);
result += "\nverticalSpreadA = " + floatToStr(verticalSpreadA);
result += "\nverticalSpreadB = " + floatToStr(verticalSpreadB);
result += "\nhorizontalSpreadA = " + floatToStr(horizontalSpreadA);
result += "\nhorizontalSpreadB = " + floatToStr(horizontalSpreadB);
result += "\nstartEmissionRate = " + floatToStr(startEmissionRate);
return result;
}
// ===========================================================================
// ParticleManager
// ===========================================================================
ParticleManager::ParticleManager() {
}
ParticleManager::~ParticleManager() {
end();
}
void ParticleManager::render(ParticleRenderer *pr, ModelRenderer *mr) const{
for(unsigned int i= 0; i < particleSystems.size(); i++){
ParticleSystem *ps= particleSystems[i];
if(ps != NULL && ps->getVisible()){
ps->render(pr, mr);
}
}
}
bool ParticleManager::hasActiveParticleSystem(ParticleSystem::ParticleSystemType type) const{
bool result= false;
//size_t particleSystemCount= particleSystems.size();
//int currentParticleCount= 0;
//vector<ParticleSystem *> cleanupParticleSystemsList;
for(unsigned int i= 0; i < particleSystems.size(); i++){
ParticleSystem *ps= particleSystems[i];
if(ps != NULL){
//currentParticleCount+= ps->getAliveParticleCount();
bool showParticle= true;
if(dynamic_cast<UnitParticleSystem *> (ps) != NULL ||
dynamic_cast<FireParticleSystem *> (ps) != NULL) {
showParticle= ps->getVisible() || (ps->getState() == ParticleSystem::sFade);
}
if(showParticle == true){
//printf("Looking for [%d] current id [%d] i = %d\n",type,ps->getParticleSystemType(),i);
if(type == ParticleSystem::pst_All || type == ps->getParticleSystemType()){
//printf("FOUND particle system type match for [%d] current id [%d] i = %d\n",type,ps->getParticleSystemType(),i);
result= true;
break;
}
}
}
}
return result;
}
void ParticleManager::update(int renderFps){
Chrono chrono;
if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
size_t particleSystemCount= particleSystems.size();
int currentParticleCount= 0;
vector<ParticleSystem *> cleanupParticleSystemsList;
for(unsigned int i= 0; i < particleSystems.size(); i++){
ParticleSystem *ps= particleSystems[i];
if(ps != NULL && validateParticleSystemStillExists(ps) == true) {
currentParticleCount+= ps->getAliveParticleCount();
bool showParticle= true;
if( dynamic_cast<UnitParticleSystem *> (ps) != NULL ||
dynamic_cast<FireParticleSystem *> (ps) != NULL) {
showParticle = ps->getVisible() || (ps->getState() == ParticleSystem::sFade);
}
if(showParticle == true){
ps->update();
if(ps->isEmpty() && ps->getState() == ParticleSystem::sFade) {
cleanupParticleSystemsList.push_back(ps);
}
}
}
}
//particleSystems.remove(NULL);
cleanupParticleSystems(cleanupParticleSystemsList);
if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0)
SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld, particleSystemCount = %d, currentParticleCount = %d\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),particleSystemCount,currentParticleCount);
}
bool ParticleManager::validateParticleSystemStillExists(ParticleSystem * particleSystem) const{
int index= findParticleSystems(particleSystem, this->particleSystems);
return (index >= 0);
}
void ParticleManager::removeParticleSystemsForParticleOwner(ParticleOwner *particleOwner) {
if(particleOwner != NULL && particleSystems.empty() == false) {
vector<ParticleSystem *> cleanupParticleSystemsList;
for(unsigned int index = 0; index < particleSystems.size(); ++index) {
ParticleSystem *ps= particleSystems[index];
if(ps != NULL && ps->getParticleOwner() == particleOwner) {
cleanupParticleSystemsList.push_back(ps);
}
}
if(cleanupParticleSystemsList.empty() == false) {
cleanupParticleSystems(cleanupParticleSystemsList);
}
}
}
int ParticleManager::findParticleSystems(ParticleSystem *psFind, const vector<ParticleSystem *> &particleSystems) const{
int result= -1;
for(unsigned int i= 0; i < particleSystems.size(); i++){
ParticleSystem *ps= particleSystems[i];
if(ps != NULL && psFind != NULL && psFind == ps){
result= i;
break;
}
}
return result;
}
void ParticleManager::cleanupParticleSystems(ParticleSystem *ps) {
int index= findParticleSystems(ps, this->particleSystems);
if(ps != NULL && index >= 0) {
// printf("-- Delete cleanupParticleSystems [%p]\n",ps);
// static map<void *,int> deleteList;
// if(deleteList.find(ps) != deleteList.end()) {
// assert(deleteList.find(ps) == deleteList.end());
// }
// deleteList[ps]++;
// This code causes segfault on game end, no need to fade, just delete
//if(ps->getState() != ParticleSystem::sFade) {
// ps->fade();
//}
if(ps != NULL) {
ps->callParticleOwnerEnd(ps);
}
delete ps;
this->particleSystems.erase(this->particleSystems.begin() + index);
}
}
void ParticleManager::cleanupParticleSystems(vector<ParticleSystem *> &cleanupParticleSystemsList){
if(cleanupParticleSystemsList.empty() == false) {
for(int i= (int)cleanupParticleSystemsList.size()-1; i >= 0; i--) {
ParticleSystem *ps= cleanupParticleSystemsList[i];
cleanupParticleSystems(ps);
}
cleanupParticleSystemsList.clear();
}
}
void ParticleManager::cleanupUnitParticleSystems(vector<UnitParticleSystem *> &cleanupParticleSystemsList){
if(cleanupParticleSystemsList.empty() == false) {
for(int i= (int)cleanupParticleSystemsList.size()-1; i >= 0; i--) {
ParticleSystem *ps= cleanupParticleSystemsList[i];
cleanupParticleSystems(ps);
}
cleanupParticleSystemsList.clear();
}
}
void ParticleManager::manage(ParticleSystem *ps){
assert((std::find(particleSystems.begin(),particleSystems.end(),ps) == particleSystems.end()) && "particle cannot be added twice");
particleSystems.push_back(ps);
for(int i = ps->getChildCount() - 1; i >= 0; i--) {
manage(ps->getChild(i));
}
}
void ParticleManager::end(){
while(particleSystems.empty() == false){
ParticleSystem *ps = particleSystems.back();
// printf("-- Delete end() [%p]\n",ps);
// static map<void *,int> deleteList;
// if(deleteList.find(ps) != deleteList.end()) {
// assert(deleteList.find(ps) == deleteList.end());
// }
// deleteList[ps]++;
if(ps != NULL) {
ps->callParticleOwnerEnd(ps);
}
delete ps;
particleSystems.pop_back();
}
}
}
}//end namespace