// particlesystem.cpp // // Particle system file loader. // // Copyright (C) 2008, Chris Laurel // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. #include "particlesystemfile.h" #include "particlesystem.h" #include "texmanager.h" #include using namespace Eigen; using namespace std; /* !!! IMPORTANT !!! * The particle system code is still under development; the complete * set of particle system features has not been decided and the cpart * format is not even close time final. There are most certainly bugs. * DO NOT enable this code and invest a lot of time in creating your * own particle system files until development is further along. */ ParticleSystemLoader::ParticleSystemLoader(istream& in) : m_tokenizer(&in), m_parser(&m_tokenizer) { } ParticleSystem* ParticleSystemLoader::load() { auto* particleSystem = new ParticleSystem(); while (m_tokenizer.nextToken() != Tokenizer::TokenEnd) { string objType; if (m_tokenizer.getTokenType() != Tokenizer::TokenName) { raiseError("Error parsing particle system"); delete particleSystem; return nullptr; } objType = m_tokenizer.getNameValue(); if (objType != "Emitter") { ostringstream stream; stream << "Unexpected object '" << objType << "' in particle system file"; raiseError(stream.str()); delete particleSystem; return nullptr; } Value* objParamsValue = m_parser.readValue(); if (objParamsValue == nullptr || objParamsValue->getType() != Value::HashType) { raiseError("Error parsing particle system"); delete particleSystem; return nullptr; } Hash* objParams = objParamsValue->getHash(); if (objType == "Emitter") { ParticleEmitter* emitter = parseEmitter(objParams); if (emitter == nullptr) { delete particleSystem; return nullptr; } particleSystem->addEmitter(emitter); } } return particleSystem; } VectorGenerator* ParticleSystemLoader::parseGenerator(Hash* params) { Vector3f constantValue(Vector3f::Zero()); if (params->getVector("Constant", constantValue)) { return new ConstantGenerator(constantValue); } Value* generatorValue = nullptr; generatorValue = params->getValue("Box"); if (generatorValue != nullptr) { if (generatorValue->getType() != Value::HashType) { raiseError("Error in Box syntax"); return nullptr; } Hash* boxParams = generatorValue->getHash(); Vector3f center(Vector3f::Zero()); Vector3f size(Vector3f::Zero()); boxParams->getVector("Center", center); boxParams->getVector("Size", size); return new BoxGenerator(center, size); } generatorValue = params->getValue("Line"); if (generatorValue != nullptr) { if (generatorValue->getType() != Value::HashType) { raiseError("Error in Line syntax"); return nullptr; } Hash* lineParams = generatorValue->getHash(); Vector3f p0(Vector3f::Zero()); Vector3f p1(Vector3f::Zero()); lineParams->getVector("Point1", p0); lineParams->getVector("Point2", p1); return new LineGenerator(p0, p1); } generatorValue = params->getValue("EllipsoidSurface"); if (generatorValue != nullptr) { if (generatorValue->getType() != Value::HashType) { raiseError("Error in EllipsoidSurface syntax"); return nullptr; } Hash* ellipsoidSurfaceParams = generatorValue->getHash(); Vector3f center(Vector3f::Zero()); Vector3f size(2.0f, 2.0f, 2.0f); ellipsoidSurfaceParams->getVector("Center", center); ellipsoidSurfaceParams->getVector("Size", size); return new EllipsoidSurfaceGenerator(center, size * 0.5f); } generatorValue = params->getValue("Cone"); if (generatorValue != nullptr) { if (generatorValue->getType() != Value::HashType) { raiseError("Error in Cone syntax"); return nullptr; } Hash* coneParams = generatorValue->getHash(); double minAngle = 0.0; double maxAngle = 0.0; double minSpeed = 0.0; double maxSpeed = 1.0; coneParams->getNumber("MinAngle", minAngle); coneParams->getNumber("MaxAngle", maxAngle); coneParams->getNumber("MinSpeed", minSpeed); coneParams->getNumber("MaxSpeed", maxSpeed); return new ConeGenerator((float) degToRad(minAngle), (float) degToRad(maxAngle), (float) minSpeed, (float) maxSpeed); } generatorValue = params->getValue("GaussianDisc"); if (generatorValue != nullptr) { if (generatorValue->getType() != Value::HashType) { raiseError("Error in GaussianDisc syntax"); return nullptr; } Hash* gaussianDiscParams = generatorValue->getHash(); double sigma = 1.0; gaussianDiscParams->getNumber("Sigma", sigma); return new GaussianDiscGenerator((float) sigma); } raiseError("Missing generator for emitter"); return nullptr; } ParticleEmitter* ParticleSystemLoader::parseEmitter(Hash* params) { string textureFileName; ResourceHandle textureHandle = InvalidResource; if (params->getString("Texture", textureFileName)) { textureHandle = GetTextureManager()->getHandle(TextureInfo(textureFileName, getTexturePath(), TextureInfo::BorderClamp)); } double rate = 1.0; double lifetime = 1.0; params->getNumber("Rate", rate); params->getNumber("Lifetime", lifetime); double startSize = 1.0; double endSize = 1.0; params->getNumber("StartSize", startSize); params->getNumber("EndSize", endSize); Color startColor(Color::White); Color endColor(Color::Black); float startOpacity = 0.0f; float endOpacity = 0.0f; params->getColor("StartColor", startColor); params->getNumber("StartOpacity", startOpacity); params->getColor("EndColor", endColor); params->getNumber("EndOpacity", endOpacity); VectorGenerator* initialPositionGenerator = nullptr; Value* positionValue = params->getValue("InitialPosition"); if (positionValue == nullptr) { initialPositionGenerator = new ConstantGenerator(Vector3f::Zero()); } else { if (positionValue->getType() != Value::HashType) { raiseError("Error in InitialPosition syntax"); } else { initialPositionGenerator = parseGenerator(positionValue->getHash()); } } if (initialPositionGenerator == nullptr) { return nullptr; } VectorGenerator* initialVelocityGenerator = nullptr; Value* velocityValue = params->getValue("InitialVelocity"); if (velocityValue == nullptr) { initialVelocityGenerator = new ConstantGenerator(Vector3f::Zero()); } else { if (velocityValue->getType() != Value::HashType) { raiseError("Error in InitialVelocity syntax"); } else { initialVelocityGenerator = parseGenerator(velocityValue->getHash()); } } if (initialVelocityGenerator == nullptr) { delete initialPositionGenerator; return nullptr; } Vector3f acceleration; params->getVector("Acceleration", acceleration); double startTime = -numeric_limits::infinity(); double endTime = numeric_limits::infinity(); params->getNumber("Beginning", startTime); params->getNumber("Ending", endTime); double minRotationRate = 0.0; double maxRotationRate = 0.0; params->getNumber("MinRotationRate", minRotationRate); params->getNumber("MaxRotationRate", maxRotationRate); auto* emitter = new ParticleEmitter(); emitter->m_texture = textureHandle; emitter->m_rate = (float) rate; emitter->m_lifetime = (float) lifetime; emitter->m_startColor = Color(startColor, startOpacity); emitter->m_endColor = Color(endColor, endOpacity); emitter->m_startSize = (float) startSize; emitter->m_endSize = (float) endSize; emitter->m_positionGenerator = initialPositionGenerator; emitter->m_velocityGenerator = initialVelocityGenerator; emitter->createMaterial(); emitter->setAcceleration(acceleration); emitter->setLifespan(startTime, endTime); emitter->setRotationRateRange((float) degToRad(minRotationRate), (float) degToRad(maxRotationRate)); return emitter; } void ParticleSystemLoader::raiseError(const string& errorMessage) { m_errorMessage = errorMessage; } const string& ParticleSystemLoader::getErrorMessage() const { return m_errorMessage; } void ParticleSystemLoader::setTexturePath(const string& texPath) { m_texPath = texPath; } const string& ParticleSystemLoader::getTexturePath() const { return m_texPath; } ParticleSystem* LoadParticleSystem(istream& in, const string& texPath) { auto* loader = new ParticleSystemLoader(in); if (loader == nullptr) return nullptr; loader->setTexturePath(texPath); ParticleSystem* particleSystem = loader->load(); if (particleSystem == nullptr) cerr << "Error in particle system file: " << loader->getErrorMessage() << '\n'; delete loader; return particleSystem; }