// ============================================================== // 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 #include #include #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 memoryObjectList; void Particle::saveGame(XmlNode *rootNode) { std::map 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; speedUpRelative = 0; speedUpConstant = 0; 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 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(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(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(this->particleSize, 6); } void ParticleSystem::setSpeed(float speed) { this->speed = speed; this->speed = truncateDecimal(this->speed, 6); } void ParticleSystem::setSpeedUpRelative(float speedUpRelative) { this->speedUpRelative = speedUpRelative; this->speedUpRelative = truncateDecimal(this->speedUpRelative, 6); } void ParticleSystem::setSpeedUpConstant(float speedUpConstant) { this->speedUpConstant = speedUpConstant; this->speedUpConstant = truncateDecimal(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 mapTagReplacements; XmlNode *particleSystemNode = rootNode->addChild("ParticleSystem"); // std::vector 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(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 particles; // for(unsigned int i = 0; i < particles.size(); ++i) { // Particle &particle = particles[i]; // particle.saveGame(particleSystemNode); // } particles.clear(); particles.resize(particleCount); // vector 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(particleSystemNode->getAttribute("blendMode")->getIntValue()); // State state; state = static_cast(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(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(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(random.randRange(-radius, radius))); float x = streflop::sinf(static_cast(ang))*mod; float y = streflop::cosf(static_cast(ang))*mod; float radRatio = streflop::sqrtf(static_cast(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 (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(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(degToRad(windAngle)))*windSpeed; this->windSpeed.y = 0.0f; this->windSpeed.z = streflop::cosf(static_cast(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(this->windSpeed.x, 6); this->windSpeed.y = truncateDecimal(this->windSpeed.y, 6); this->windSpeed.z = truncateDecimal(this->windSpeed.z, 6); } void FireParticleSystem::saveGame(XmlNode *rootNode) { std::map 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(absolute)) <= 0.00001f) { #else if (fabs(absolute) <= 0.00001f) { #endif tween = 0.0f; } else { #ifdef USE_STREFLOP tween = streflop::fmod(static_cast(absolute), static_cast(modelCycle)); #else tween = fmod(absolute, modelCycle); #endif tween /= modelCycle; } } tween = truncateDecimal(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 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 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(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 = ""; meshPos = Vec3f(0, 0, 0); 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(random.randRange(-radius, radius))); const float radRatio = streflop::sqrtf(static_cast(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 (maxParticleEnergy * radRatio) + random.randRange(-varParticleEnergy, varParticleEnergy); } else { p->energy = static_cast (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(p->accel.x, 6); p->accel.y = truncateDecimal(p->accel.y, 6); p->accel.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); start = start * random.randRange(minRadius, radius); p->pos = pos + offset + start; p->pos.x = truncateDecimal(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(p->pos.z, 6); } break; case sLinear: { #ifdef USE_STREFLOP float x = streflop::sinf(static_cast(ang))*mod; float y = streflop::cosf(static_cast(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(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(p->pos.z, 6); } else { Vec3f combinedOffset = Vec3f(offset); if (meshName != "") { combinedOffset.x += meshPos.x; combinedOffset.y += meshPos.y; combinedOffset.z += meshPos.z; if (meshPos.x == 0 && meshPos.y == 0 && meshPos.z == 0) { printf("meshPosFail\n"); } } // rotate it according to rotation #ifdef USE_STREFLOP p->pos = Vec3f(pos.x + x + combinedOffset.z*streflop::sinf(static_cast(rad)) + combinedOffset.x*streflop::cosf(static_cast(rad)), pos.y + random.randRange(-radius / 2, radius / 2) + combinedOffset.y, pos.z + y + (combinedOffset.z*streflop::cosf(static_cast(rad)) - combinedOffset.x*streflop::sinf(static_cast(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(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); if (relative && relativeDirection) { #ifdef USE_STREFLOP p->speed = Vec3f(p->speed.z*streflop::sinf( static_cast(rad)) + p->speed.x * streflop::cosf(static_cast(rad)), p->speed.y, (p->speed.z * streflop::cosf( static_cast(rad)) - p->speed.x * streflop::sinf(static_cast(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(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(fixedAddition.x, 6); fixedAddition.y = truncateDecimal(fixedAddition.y, 6); fixedAddition.z = truncateDecimal(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 (p->energy)) % interval); float floatInterval = static_cast (interval); if (moduloValue < floatInterval / 2.0f) { energyRatio = (floatInterval - moduloValue) / floatInterval; } else { energyRatio = moduloValue / floatInterval; } energyRatio = clamp(energyRatio, 0.f, 1.f); } else { energyRatio = clamp(static_cast (p->energy) / static_cast (maxParticleEnergy), 0.f, 1.f); } energyRatio = truncateDecimal(energyRatio, 6); p->lastPos += p->speed; p->lastPos.x = truncateDecimal(p->lastPos.x, 6); p->lastPos.y = truncateDecimal(p->lastPos.y, 6); p->lastPos.z = truncateDecimal(p->lastPos.z, 6); p->pos += p->speed; p->pos.x = truncateDecimal(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(p->pos.z, 6); if (fixed) { p->lastPos += fixedAddition; p->lastPos.x = truncateDecimal(p->lastPos.x, 6); p->lastPos.y = truncateDecimal(p->lastPos.y, 6); p->lastPos.z = truncateDecimal(p->lastPos.z, 6); p->pos += fixedAddition; p->pos.x = truncateDecimal(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(p->pos.z, 6); } p->speed += p->accel; p->speed += p->speedUpConstant; p->speed = p->speed*(1 + p->speedUpRelative); p->speed.x = truncateDecimal(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(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(degToRad(windAngle)))*windSpeed; this->windSpeed.y = 0.0f; this->windSpeed.z = streflop::cosf(static_cast(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(this->windSpeed.x, 6); this->windSpeed.y = truncateDecimal(this->windSpeed.y, 6); this->windSpeed.z = truncateDecimal(this->windSpeed.z, 6); } void UnitParticleSystem::saveGame(XmlNode *rootNode) { std::map 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); // Vec3f meshPos; unitParticleSystemNode->addAttribute("meshPos", meshPos.getString(), mapTagReplacements); // string meshName; unitParticleSystemNode->addAttribute("meshName", meshName, 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; cRotation = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("cRotation")->getValue()); // Vec3f fixedAddition; fixedAddition = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("fixedAddition")->getValue()); // Vec3f oldPosition; oldPosition = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("oldPosition")->getValue()); // Vec3f meshPos; if (unitParticleSystemNode->hasAttribute("meshPos")) { meshPos = Vec3f::strToVec3(unitParticleSystemNode->getAttribute("meshPos")->getValue()); } // Vec3f meshName; if (unitParticleSystemNode->hasAttribute("meshName")) { meshName = unitParticleSystemNode->getAttribute("meshName")->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(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 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(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(degToRad(windAngle)))*windSpeed; this->windSpeed.y = 0.0f; this->windSpeed.z = streflop::cosf(static_cast(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(this->windSpeed.x, 6); this->windSpeed.y = truncateDecimal(this->windSpeed.y, 6); this->windSpeed.z = truncateDecimal(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(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(degToRad(windAngle)))*windSpeed; this->windSpeed.y = 0.0f; this->windSpeed.z = streflop::cosf(static_cast(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(this->windSpeed.x, 6); this->windSpeed.y = truncateDecimal(this->windSpeed.y, 6); this->windSpeed.z = truncateDecimal(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 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(trajectorySpeed, 6); flatPos.x = truncateDecimal(flatPos.x, 6); flatPos.y = truncateDecimal(flatPos.y, 6); flatPos.z = truncateDecimal(flatPos.z, 6); Vec3f targetVector = endPos - startPos; targetVector.x = truncateDecimal(targetVector.x, 6); targetVector.y = truncateDecimal(targetVector.y, 6); targetVector.z = truncateDecimal(targetVector.z, 6); Vec3f currentVector = flatPos - startPos; currentVector.x = truncateDecimal(currentVector.x, 6); currentVector.y = truncateDecimal(currentVector.y, 6); currentVector.z = truncateDecimal(currentVector.z, 6); // ratio float relative = clamp(currentVector.length() / targetVector.length(), 0.0f, 1.0f); relative = truncateDecimal(relative, 6); float absolute = relative; setTween(relative, absolute); // trajectory switch (trajectory) { case tLinear: { pos = flatPos; } break; case tParabolic: { float scaledT = truncateDecimal(2.0f * (relative - 0.5f), 6); float paraboleY = truncateDecimal((1.0f - scaledT * scaledT) * trajectoryScale, 6); pos = flatPos; pos.y += paraboleY; pos.y = truncateDecimal(pos.y, 6); } break; case tSpiral: { pos = flatPos; #ifdef USE_STREFLOP pos += xVector * streflop::cos(static_cast(relative * trajectoryFrequency * targetVector.length())) * trajectoryScale; pos.x = truncateDecimal(pos.x, 6); pos.y = truncateDecimal(pos.y, 6); pos.z = truncateDecimal(pos.z, 6); pos += yVector * streflop::sin(static_cast(relative * trajectoryFrequency * targetVector.length())) * trajectoryScale; #else pos += xVector * cos(relative * trajectoryFrequency * targetVector.length()) * trajectoryScale; pos.x = truncateDecimal(pos.x, 6); pos.y = truncateDecimal(pos.y, 6); pos.z = truncateDecimal(pos.z, 6); pos += yVector * sin(relative * trajectoryFrequency * targetVector.length()) * trajectoryScale; #endif pos.x = truncateDecimal(pos.x, 6); pos.y = truncateDecimal(pos.y, 6); pos.z = truncateDecimal(pos.z, 6); } break; default: assert(false); break; } direction = (pos - lastPos); direction.x = truncateDecimal(direction.x, 6); direction.y = truncateDecimal(direction.y, 6); direction.z = truncateDecimal(direction.z, 6); direction.normalize(); direction.x = truncateDecimal(direction.x, 6); direction.y = truncateDecimal(direction.y, 6); direction.z = truncateDecimal(direction.z, 6); // trigger update of child particles positionChildren(); rotateChildren(); //arrive destination arriveDestinationDistance = truncateDecimal(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(streflop::atan2(static_cast(direction.x), static_cast(direction.z)), 6); #else float rotation = truncateDecimal(atan2(direction.x, direction.z), 6); #endif rotation = truncateDecimal(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 (particleIndex) / emissionRate; t = truncateDecimal(t, 6); p->pos = pos + (lastPos - pos) * t; p->pos.x = truncateDecimal(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); p->accel = Vec3f(0.0f, -gravity, 0.0f); p->accel.x = truncateDecimal(p->accel.x, 6); p->accel.y = truncateDecimal(p->accel.y, 6); p->accel.z = truncateDecimal(p->accel.z, 6); updateParticle(p); } void ProjectileParticleSystem::updateParticle(Particle *p) { float energyRatio = clamp(static_cast (p->energy) / maxParticleEnergy, 0.f, 1.f); energyRatio = truncateDecimal(energyRatio, 6); p->lastPos += p->speed; p->lastPos.x = truncateDecimal(p->lastPos.x, 6); p->lastPos.y = truncateDecimal(p->lastPos.y, 6); p->lastPos.z = truncateDecimal(p->lastPos.z, 6); p->pos += p->speed; p->pos.x = truncateDecimal(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(p->pos.z, 6); p->speed += p->accel; p->speed.x = truncateDecimal(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); p->color = color * energyRatio + colorNoEnergy * (1.0f - energyRatio); p->size = particleSize * energyRatio + sizeNoEnergy * (1.0f - energyRatio); p->size = truncateDecimal(p->size, 6); p->energy--; } void ProjectileParticleSystem::setPath(Vec3f startPos, Vec3f endPos) { startPos.x = truncateDecimal(startPos.x, 6); startPos.y = truncateDecimal(startPos.y, 6); startPos.z = truncateDecimal(startPos.z, 6); endPos.x = truncateDecimal(endPos.x, 6); endPos.y = truncateDecimal(endPos.y, 6); endPos.z = truncateDecimal(endPos.z, 6); //compute axis zVector = endPos - startPos; zVector.x = truncateDecimal(zVector.x, 6); zVector.y = truncateDecimal(zVector.y, 6); zVector.z = truncateDecimal(zVector.z, 6); zVector.normalize(); zVector.x = truncateDecimal(zVector.x, 6); zVector.y = truncateDecimal(zVector.y, 6); zVector.z = truncateDecimal(zVector.z, 6); yVector = Vec3f(0.0f, 1.0f, 0.0f); xVector = zVector.cross(yVector); xVector.x = truncateDecimal(xVector.x, 6); xVector.y = truncateDecimal(xVector.y, 6); xVector.z = truncateDecimal(xVector.z, 6); //apply offset startPos += xVector * offset.x; startPos.x = truncateDecimal(startPos.x, 6); startPos.y = truncateDecimal(startPos.y, 6); startPos.z = truncateDecimal(startPos.z, 6); startPos += yVector * offset.y; startPos.x = truncateDecimal(startPos.x, 6); startPos.y = truncateDecimal(startPos.y, 6); startPos.z = truncateDecimal(startPos.z, 6); startPos += zVector * offset.z; startPos.x = truncateDecimal(startPos.x, 6); startPos.y = truncateDecimal(startPos.y, 6); startPos.z = truncateDecimal(startPos.z, 6); pos = startPos; lastPos = startPos; flatPos = startPos; //recompute axis zVector = endPos - startPos; zVector.x = truncateDecimal(zVector.x, 6); zVector.y = truncateDecimal(zVector.y, 6); zVector.z = truncateDecimal(zVector.z, 6); zVector.normalize(); zVector.x = truncateDecimal(zVector.x, 6); zVector.y = truncateDecimal(zVector.y, 6); zVector.z = truncateDecimal(zVector.z, 6); yVector = Vec3f(0.0f, 1.0f, 0.0f); xVector = zVector.cross(yVector); xVector.x = truncateDecimal(xVector.x, 6); xVector.y = truncateDecimal(xVector.y, 6); xVector.z = truncateDecimal(xVector.z, 6); // set members this->startPos = startPos; this->endPos = endPos; // direction direction = (endPos - lastPos); direction.x = truncateDecimal(direction.x, 6); direction.y = truncateDecimal(direction.y, 6); direction.z = truncateDecimal(direction.z, 6); direction.normalize(); direction.x = truncateDecimal(direction.x, 6); direction.y = truncateDecimal(direction.y, 6); direction.z = truncateDecimal(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 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(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(emissionRate, 6); float t = 1.0f - ((emissionRate + startEmissionRate) / (startEmissionRate * 2.0f)); t = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); p->speed.normalize(); p->speed.x = truncateDecimal(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); p->speed = p->speed * speed; p->speed.x = truncateDecimal(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(p->speed.z, 6); p->accel = Vec3f(0.0f, -gravity, 0.0f); p->accel.x = truncateDecimal(p->accel.x, 6); p->accel.y = truncateDecimal(p->accel.y, 6); p->accel.z = truncateDecimal(p->accel.z, 6); p->speedUpConstant = Vec3f(speedUpConstant)*p->speed; } void SplashParticleSystem::updateParticle(Particle *p) { float energyRatio = clamp(static_cast (p->energy) / maxParticleEnergy, 0.f, 1.f); p->lastPos = p->pos; p->pos = p->pos + p->speed; p->pos.x = truncateDecimal(p->pos.x, 6); p->pos.y = truncateDecimal(p->pos.y, 6); p->pos.z = truncateDecimal(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(p->speed.x, 6); p->speed.y = truncateDecimal(p->speed.y, 6); p->speed.z = truncateDecimal(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(p->size, 6); } void SplashParticleSystem::saveGame(XmlNode *rootNode) { std::map 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 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 (ps) != NULL || dynamic_cast (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 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 (ps) != NULL || dynamic_cast (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 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 &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 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 &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 &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 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