Remove unused particle system code

pull/1285/head
Andrew Tribick 2021-12-29 13:36:37 +01:00 committed by ajtribick
parent 86f2aab284
commit 5678539e05
9 changed files with 1 additions and 1196 deletions

View File

@ -840,10 +840,6 @@ EXCLUDE = src/packdb.cpp \
src/celmath/plane.h \
src/celmath/quaternion.h \
src/celmath/vecmath.h \
src/celengine/particlesystem.cpp \
src/celengine/particlesystem.h \
src/celengine/particlesystemfile.cpp \
src/celengine/particlesystemfile.h \
src/celengine/vertexbuf.h \
src/celtxf/*.*

View File

@ -91,10 +91,6 @@ set(CELENGINE_SOURCES
parseobject.h
parser.cpp
parser.h
# particlesystem.cpp
# particlesystemfile.cpp
# particlesystemfile.h
# particlesystem.h
planetgrid.cpp
planetgrid.h
pointstarrenderer.cpp

View File

@ -8,9 +8,6 @@
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// Experimental particle system support
#define PARTICLE_SYSTEM 0
#include <cmath>
#include <cstdint>
#include <cstring>
@ -40,11 +37,6 @@
#include "spheremesh.h"
#include "texmanager.h"
#if PARTICLE_SYSTEM
#include "particlesystem.h"
#include "particlesystemfile.h"
#endif
using celestia::util::GetLogger;
namespace
@ -509,16 +501,6 @@ GeometryInfo::load(const fs::path& resolvedFilename)
model->transform(center, scale);
}
}
#if PARTICLE_SYSTEM
else if (fileType == Content_CelestiaParticleSystem)
{
ifstream in(filename);
if (in.good())
{
return LoadParticleSystem(in, path);
}
}
#endif
// Condition the model for optimal rendering
if (model != nullptr)

View File

@ -1,509 +0,0 @@
// particlesystem.cpp
//
// Stateless particle system renderer.
//
// Copyright (C) 2008, Chris Laurel <claurel@gmail.com>
//
// 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 <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <limits>
#include "modelgeometry.h"
#include "particlesystem.h"
#include "glsupport.h"
#include "vecgl.h"
#include "rendcontext.h"
#include "texmanager.h"
using namespace cmod;
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.
*/
/* STATELESS PARTICLE SYSTEMS
*
* THEORY
* In a typical particle system, initial particle states are generated
* and stored in array. At each time step, particles have their states
* updated and are then drawn. A typical sequence is:
* - Compute the forces acting on the particles
* - Update particle positions and velocities
* - Age the particles (updating state such as color and size)
* - Render the particles
*
* This process is well-suited to a simulation where the time steps are
* relatively uniform. But, we cannot rely on a uniform time step in Celestia.
* The user may skip ahead instantly to times in the distance past or future,
* change the time rate to over a billion times normal, or reverse time.
* Numerical integration of particle positions is completely impractical.
*
* Instead, Celestia uses 'stateless' particle systems. From the particle
* system description, the particle positions and appearances can be generated
* for any time. Initial states are generated from a pseudorandom sequence
* and state at the current time is computed analytically from those initial
* values. The fact that motions must be calculated analytically means that
* only very simply force models may be used; still, a large variety of
* effects are still practical.
*
* A particle system is just a list of particle emitters. Each emitter has
* a fixed emission rate (particles per second), start time, and end time.
* There are few properties that apply to all particles produced by an emitter:
* texture, lifetime, start/end color, and start/end size
* Color and size are linearly interpolated between start and end values over
* the lifetime of a particle.
* An emitter has two different 'generators': one produces initial particle
* velocities, the other initial positions.
*
* Emitter generators are fed with values from a linear congruential
* generator. Other pseudorandom number generators can produce sequences with
* better distributions and can have better performance at generating values.
* However, seeding these other generators is very slow, and seeding must
* be every time a particle is to be drawn. The well-known defects in
* pseudorandom sequences produced by an LCG are not visible in a particle
* system (and lack of apparent visual artifacts is the *only* requirement here.)
*/
// Same values as rand48()
static const uint64_t A = ((uint64_t) 0x5deece66ul << 4) | 0xd;
static const uint64_t C = 0xb;
static const uint64_t M = ((uint64_t) 1 << 48) - 1;
/*! Linear congruential random number generator that emulates
* rand48()
*/
class LCGRandomGenerator
{
public:
LCGRandomGenerator() : previous(0) = default;
LCGRandomGenerator(uint64_t seed) :
previous(seed)
{
}
uint64_t randUint64()
{
previous = (A * previous + C) & M;
return previous;
}
/*! Return a random integer between -2^31 and 2^31 - 1
*/
int32_t randInt32()
{
return (int32_t) (randUint64() >> 16);
}
/*! Return a random integer between 0 and 2^32 - 1
*/
uint32_t randUint32()
{
return (uint32_t) (randUint64() >> 16);
}
/*! Generate a random floating point value in [ 0, 1 )
* This function directly manipulates the bits of a floating
* point number, and will not work properly on a system that
* doesn't use IEEE754 floats.
*/
float randFloat()
{
uint32_t randBits = randInt32();
randBits = (randBits & 0x007fffff) | 0x3f800000;
return *reinterpret_cast<float*>(&randBits) - 1.0f;
}
/*! Generate a random floating point value in [ -1, 1 )
* This function directly manipulates the bits of a floating
* point number, and will not work properly on a system that
* doesn't use IEEE754 floats.
*/
float randSfloat()
{
uint32_t randBits = (uint32_t) (randUint64() >> 16);
randBits = (randBits & 0x007fffff) | 0x40000000;
return *reinterpret_cast<float*>(&randBits) - 3.0f;
}
private:
uint64_t previous;
};
/**** Generator implementations ****/
Vector3f
ConstantGenerator::generate(LCGRandomGenerator& /* gen */) const
{
return m_value;
}
Vector3f
BoxGenerator::generate(LCGRandomGenerator& gen) const
{
return Vector3f(gen.randSfloat() * m_semiAxes.x(),
gen.randSfloat() * m_semiAxes.y(),
gen.randSfloat() * m_semiAxes.z()) + m_center;
}
Vector3f
LineGenerator::generate(LCGRandomGenerator& gen) const
{
return m_origin + m_direction * gen.randFloat();
}
Vector3f
EllipsoidSurfaceGenerator::generate(LCGRandomGenerator& gen) const
{
float theta = (float) PI * gen.randSfloat();
float cosPhi = gen.randSfloat();
float sinPhi = std::sqrt(1.0f - cosPhi * cosPhi);
if (cosPhi < 0.0f)
sinPhi = -sinPhi;
float s = std::sin(theta);
float c = std::cos(theta);
return Vector3f(sinPhi * c * m_semiAxes.x(), sinPhi * s * m_semiAxes.y(), cosPhi * m_semiAxes.z()) + m_center;
}
Vector3f
ConeGenerator::generate(LCGRandomGenerator& gen) const
{
float theta = (float) PI * gen.randSfloat();
float cosPhi = 1.0f - m_cosMinAngle - gen.randFloat() * m_cosAngleVariance;
float sinPhi = std::sqrt(1.0f - cosPhi * cosPhi);
if (cosPhi < 0.0f)
sinPhi = -sinPhi;
float s = std::sin(theta);
float c = std::cos(theta);
return Vector3f(sinPhi * c, sinPhi * s, cosPhi) * (m_minLength + gen.randFloat() * m_lengthVariance);
}
Vector3f
GaussianDiscGenerator::generate(LCGRandomGenerator& gen) const
{
float r1 = 0.0f;
float r2 = 0.0f;
float s = 0.0f;
do
{
r1 = gen.randSfloat();
r2 = gen.randSfloat();
s = r1 * r1 + r2 * r2;
} while (s > 1.0f);
// Choose angle uniformly distributed in [ 0, 2*PI ), radius
// with a Gaussian distribution. Use the polar form of the
// Box-Muller transform to produce a normally distributed
// random number.
float r = r1 * std::sqrt(-2.0f * std::log(s) / s) * m_sigma;
float theta = r2 * 2.0f * (float) PI;
return Vector3f(r * std::cos(theta), r * std::sin(theta), 0.0f);
}
ParticleEmitter::ParticleEmitter() :
m_startTime(-numeric_limits<double>::infinity()),
m_endTime(-numeric_limits<double>::infinity()),
m_texture(InvalidResource),
m_rate(1.0f),
m_lifetime(1.0f),
m_startColor(1.0f, 1.0f, 1.0f, 0.0f),
m_startSize(1.0f),
m_endColor(1.0f, 1.0f, 1.0f, 0.0f),
m_endSize(1.0f),
m_positionGenerator(nullptr),
m_velocityGenerator(nullptr),
m_acceleration(Vector3f::Zero()),
m_nonZeroAcceleration(false),
m_minRotationRate(0.0f),
m_rotationRateVariance(0.0f),
m_rotationEnabled(false),
m_blendMode(cmod::Material::PremultipliedAlphaBlend)
{
}
ParticleEmitter::~ParticleEmitter()
{
delete m_positionGenerator;
delete m_velocityGenerator;
}
void
ParticleEmitter::setLifespan(double startTime, double endTime)
{
m_startTime = startTime;
m_endTime = endTime;
}
void
ParticleEmitter::setRotationRateRange(float minRate, float maxRate)
{
m_rotationEnabled = minRate != 0.0f || maxRate != 0.0f;
m_minRotationRate = minRate;
m_rotationRateVariance = maxRate - minRate;
}
void
ParticleEmitter::setAcceleration(const Vector3f& acceleration)
{
m_acceleration = acceleration;
m_nonZeroAcceleration = m_acceleration != Vector3f::Zero();
}
void
ParticleEmitter::setBlendMode(cmod::Material::BlendMode blendMode)
{
m_blendMode = blendMode;
}
static const uint64_t scrambleMask = (uint64_t(0xcccccccc) << 32) | 0xcccccccc;
void
ParticleEmitter::render(double tsec,
RenderContext& rc,
ParticleVertex* particleBuffer,
unsigned int particleBufferCapacity) const
{
double t = tsec;
bool startBounded = m_startTime > -numeric_limits<double>::infinity();
bool endBounded = m_endTime < numeric_limits<double>::infinity();
// Return immediately if we're far enough past the end time that no
// particles remain.
if (endBounded)
{
if (t > m_endTime + m_lifetime)
return;
}
// If a start time is specified, set t to be relative to the start time.
// Return immediately if we haven't reached the start time yet.
if (startBounded)
{
t -= m_startTime;
if (t < 0.0)
return;
}
Matrix3f modelViewMatrix = rc.getCameraOrientation().conjugate().toRotationMatrix();
rc.setMaterial(&m_material);
Vector3f v0 = modelViewMatrix * Vector3f(-1.0f, -1.0f, 0.0f);
Vector3f v1 = modelViewMatrix * Vector3f( 1.0f, -1.0f, 0.0f);
Vector3f v2 = modelViewMatrix * Vector3f( 1.0f, 1.0f, 0.0f);
Vector3f v3 = modelViewMatrix * Vector3f(-1.0f, 1.0f, 0.0f);
Texture* texture = nullptr;
if (m_texture != InvalidResource)
{
texture = GetTextureManager()->find(m_texture);
}
if (texture != nullptr)
texture->bind();
// Use premultiplied alpha
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
double emissionInterval = 1.0 / m_rate;
double dserial = std::fmod(t * m_rate, (double) (1 << 31));
auto serial = (int) (dserial);
double age = (dserial - serial) * emissionInterval;
auto invLifetime = (float) (1.0 / m_lifetime);
double maxAge = m_lifetime;
if (startBounded)
{
maxAge = std::min((double) m_lifetime, t);
}
if (endBounded && tsec > m_endTime)
{
auto skipParticles = (int) ((tsec - m_endTime) * m_rate);
serial -= skipParticles;
age += skipParticles * emissionInterval;
}
Vec4f startColor(m_startColor.red(), m_startColor.green(), m_startColor.blue(), m_startColor.alpha());
Vec4f endColor(m_endColor.red(), m_endColor.green(), m_endColor.blue(), m_endColor.alpha());
unsigned int particleCount = 0;
while (age < maxAge)
{
// When the particle buffer is full, render the particles and flush it
if (particleCount == particleBufferCapacity)
{
glDrawArrays(GL_TRIANGLE_FAN, 0, particleCount * 4);
particleCount = 0;
}
float alpha = (float) age * invLifetime;
float beta = 1.0f - alpha;
float size = alpha * m_endSize + beta * m_startSize;
// Scramble the random number generator seed so that we don't end up with
// artifacts from using regularly incrementing values.
//
// TODO: consider whether the generator could be seeded just once before
// the first particle is drawn. This would entail further restrictions,
// such as no 'branching' (variable number of calls to LCG::generate()) in
// particle state calculation.
LCGRandomGenerator gen(uint64_t(serial) * uint64_t(0x128ef719) ^ scrambleMask);
// Calculate the color of the particle
// TODO: switch to using a lookup table for color and opacity
unsigned char color[4];
color[Color::Red] = (unsigned char) ((alpha * endColor.x + beta * startColor.x) * 255.99f);
color[Color::Green] = (unsigned char) ((alpha * endColor.y + beta * startColor.y) * 255.99f);
color[Color::Blue] = (unsigned char) ((alpha * endColor.z + beta * startColor.z) * 255.99f);
color[Color::Alpha] = (unsigned char) ((alpha * endColor.w + beta * startColor.w) * 255.99f);
Vector3f v = m_velocityGenerator->generate(gen);
Vector3f center = m_positionGenerator->generate(gen) + v * (float) age;
if (m_nonZeroAcceleration)
center += m_acceleration * (float) (age * age);
if (!m_rotationEnabled)
{
particleBuffer[particleCount * 4 + 0].set(center + v0 * size, Vector2f(0.0f, 1.0f), color);
particleBuffer[particleCount * 4 + 1].set(center + v1 * size, Vector2f(1.0f, 1.0f), color);
particleBuffer[particleCount * 4 + 2].set(center + v2 * size, Vector2f(1.0f, 0.0f), color);
particleBuffer[particleCount * 4 + 3].set(center + v3 * size, Vector2f(0.0f, 0.0f), color);
}
else
{
float rotationRate = m_minRotationRate + m_rotationRateVariance * gen.randFloat();
float rotation = rotationRate * (float) age;
float c = std::cos(rotation);
float s = std::sin(rotation);
particleBuffer[particleCount * 4 + 0].set(center + (modelViewMatrix * Vector3f(-c + s, -s - c, 0.0f)) * size, Vector2f(0.0f, 1.0f), color);
particleBuffer[particleCount * 4 + 1].set(center + (modelViewMatrix * Vector3f( c + s, s - c, 0.0f)) * size, Vector2f(1.0f, 1.0f), color);
particleBuffer[particleCount * 4 + 2].set(center + (modelViewMatrix * Vector3f( c - s, s + c, 0.0f)) * size, Vector2f(1.0f, 0.0f), color);
particleBuffer[particleCount * 4 + 3].set(center + (modelViewMatrix * Vector3f(-c - s, -s + c, 0.0f)) * size, Vector2f(0.0f, 0.0f), color);
}
++particleCount;
age += emissionInterval;
serial--;
}
// Render any remaining particles in the buffer
if (particleCount > 0)
{
glDrawArrays(GL_TRIANGLE_FAN, 0, particleCount * 4);
}
}
void
ParticleEmitter::createMaterial()
{
m_material.diffuse = cmod::Material::Color(0.0f, 0.0f, 0.0f);
m_material.emissive = cmod::Material::Color(1.0f, 1.0f, 1.0f);
m_material.blend = m_blendMode;
m_material.opacity = 0.99f;
m_material.maps[0] = new CelestiaTextureResource(m_texture);
}
#define STRUCT_OFFSET(s, memberName) ((uint32_t) (reinterpret_cast<char*>(&(s).memberName) - reinterpret_cast<char*>(&(s))))
ParticleSystem::ParticleSystem() :
m_vertexDesc(nullptr),
m_vertexData(nullptr),
m_particleCapacity(0),
m_particleCount(0)
{
m_particleCapacity = 1000;
m_vertexData = new ParticleVertex[m_particleCapacity * 4];
// Create the vertex description; currently, it is the same for all
// particle systems.
ParticleVertex temp;
Mesh::VertexAttribute attributes[3];
attributes[0] = cmod::Mesh::VertexAttribute(Mesh::Position, Mesh::Float3, STRUCT_OFFSET(temp, position));
attributes[1] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, STRUCT_OFFSET(temp, texCoord));
attributes[2] = Mesh::VertexAttribute(Mesh::Color0, Mesh::UByte4, STRUCT_OFFSET(temp, color));
m_vertexDesc = new Mesh::VertexDescription(sizeof(ParticleVertex), 3, attributes);
}
ParticleSystem::~ParticleSystem()
{
for (const auto emitter : m_emitterList)
delete emitter;
delete[] m_vertexData;
delete m_vertexDesc;
}
void
ParticleSystem::render(RenderContext& rc, double tsec)
{
rc.setVertexArrays(*m_vertexDesc, m_vertexData);
for (const auto emitter : m_emitterList)
{
emitter->render(tsec, rc, m_vertexData, m_particleCapacity);
}
}
bool
ParticleSystem::pick(const Eigen::ParametrizedLine<double, 3>& /* r */, double& /* distance */) const
{
// Pick selection for particle systems not supported (because it's
// not typically desirable.)
return false;
}
bool
ParticleSystem::isOpaque() const
{
return false;
}
void
ParticleSystem::addEmitter(ParticleEmitter* emitter)
{
m_emitterList.push_back(emitter);
}

View File

@ -1,245 +0,0 @@
// particlesystem.h
//
// Copyright (C) 2008, Chris Laurel <claurel@gmail.com>
//
// 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.
#ifndef _CELENGINE_PARTICLESYSTEM_H_
#define _CELENGINE_PARTICLESYSTEM_H_
#include "celmodel/model.h"
#include "celmodel/mesh.h"
#include "celutil/color.h"
#include "rendcontext.h"
#include "geometry.h"
#include <Eigen/Core>
#include <string>
#include <list>
class VectorGenerator;
struct ParticleVertex
{
void set(const Eigen::Vector3f& _position, const Eigen::Vector2f& _texCoord, const unsigned char* _color)
{
position = _position;
texCoord = _texCoord;
color[Color::Red] = _color[Color::Red];
color[Color::Green] = _color[Color::Green];
color[Color::Blue] = _color[Color::Blue];
color[Color::Alpha] = _color[Color::Alpha];
}
Eigen::Vector3f position;
Eigen::Vector2f texCoord;
unsigned char color[4];
};
class ParticleEmitter
{
public:
ParticleEmitter();
~ParticleEmitter();
void render(double tsec, RenderContext& rc, ParticleVertex* particleBuffer, unsigned int particleBufferCapacity) const;
void setAcceleration(const Eigen::Vector3f& acceleration);
void createMaterial();
void setLifespan(double startTime, double endTime);
void setRotationRateRange(float minRate, float maxRate);
void setBlendMode(cmod::Material::BlendMode blendMode);
private:
double m_startTime;
double m_endTime;
public:
ResourceHandle m_texture;
float m_rate;
float m_lifetime;
Color m_startColor;
float m_startSize;
Color m_endColor;
float m_endSize;
VectorGenerator* m_positionGenerator;
VectorGenerator* m_velocityGenerator;
private:
Eigen::Vector3f m_acceleration;
bool m_nonZeroAcceleration;
float m_minRotationRate;
float m_rotationRateVariance;
bool m_rotationEnabled;
cmod::Material::BlendMode m_blendMode;
cmod::Material m_material;
};
class ParticleSystem : public Geometry
{
public:
ParticleSystem();
virtual ~ParticleSystem();
virtual void render(RenderContext& rc, double tsec = 0.0);
virtual bool pick(const Eigen::ParametrizedLine<double, 3>& r, double& distance) const;
virtual bool isOpaque() const;
void addEmitter(ParticleEmitter* emitter);
public:
std::list<ParticleEmitter*> m_emitterList;
cmod::Mesh::VertexDescription* m_vertexDesc;
ParticleVertex* m_vertexData;
unsigned int m_particleCapacity;
unsigned int m_particleCount;
};
class LCGRandomGenerator;
/*! Generator abstract base class.
* Subclasses must implement generate() method.
*/
class VectorGenerator
{
public:
VectorGenerator() = default;
virtual ~VectorGenerator() = default;
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const = 0;
};
/*! Simplest generator; produces the exact same value on each call
* to generate().
*/
class ConstantGenerator : public VectorGenerator
{
public:
ConstantGenerator(const Eigen::Vector3f& value) : m_value(value) {}
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const;
private:
Eigen::Vector3f m_value;
};
/*! Generates values uniformly distributed within an axis-aligned box.
*/
class BoxGenerator : public VectorGenerator
{
public:
BoxGenerator(const Eigen::Vector3f& center, const Eigen::Vector3f& axes) :
m_center(center),
m_semiAxes(axes * 0.5f)
{
}
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const;
private:
Eigen::Vector3f m_center;
Eigen::Vector3f m_semiAxes;
};
/*! Generates values uniformly distributed on a line between
* two points.
*/
class LineGenerator : public VectorGenerator
{
public:
LineGenerator(const Eigen::Vector3f& p0, const Eigen::Vector3f& p1) :
m_origin(p0),
m_direction(p1 - p0)
{
}
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const;
private:
Eigen::Vector3f m_origin;
Eigen::Vector3f m_direction;
};
/*! Generates values uniformly distributed on the surface
* of an ellipsoid.
*/
class EllipsoidSurfaceGenerator : public VectorGenerator
{
public:
EllipsoidSurfaceGenerator(const Eigen::Vector3f& center, const Eigen::Vector3f& semiAxes) :
m_center(center),
m_semiAxes(semiAxes)
{
}
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const;
private:
Eigen::Vector3f m_center;
Eigen::Vector3f m_semiAxes;
};
/*! Generates values uniformly distributed within a spherical
* section. The section is centered on the z-axis.
*/
class ConeGenerator : public VectorGenerator
{
public:
ConeGenerator(float minAngle, float maxAngle, float minLength, const float maxLength) :
m_cosMinAngle(1.0f - std::cos(minAngle)),
m_cosAngleVariance(std::cos(minAngle) - std::cos(maxAngle)),
m_minLength(minLength),
m_lengthVariance(maxLength - minLength)
{
}
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const;
private:
float m_cosMinAngle;
float m_cosAngleVariance;
float m_minLength;
float m_lengthVariance;
};
/*! Generates points in a 2D gaussian distribution in
* the xy-plane and centered on the origin.
*/
class GaussianDiscGenerator : public VectorGenerator
{
public:
GaussianDiscGenerator(float sigma) :
m_sigma(sigma)
{
}
virtual Eigen::Vector3f generate(LCGRandomGenerator& gen) const;
private:
float m_sigma;
};
//ParticleSystem* LoadParticleSystem(const std::string& filename, const std::string& resourcePath);
#endif // _CELENGINE_PARTICLESYSTEM_H_

View File

@ -1,359 +0,0 @@
// particlesystem.cpp
//
// Particle system file loader.
//
// Copyright (C) 2008, Chris Laurel <claurel@gmail.com>
//
// 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 <sstream>
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<double>::infinity();
double endTime = numeric_limits<double>::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;
}

View File

@ -1,53 +0,0 @@
// particlesystem.h
//
// Particle system file loader.
//
// Copyright (C) 2008, Chris Laurel <claurel@gmail.com>
//
// 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.
#ifndef _CELENGINE_PARTICLESYSTEMFILE_H_
#define _CELENGINE_PARTICLESYSTEMFILE_H_
#include <iostream>
#include <string>
#include <celutil/tokenizer.h>
#include "parser.h"
class ParticleSystem;
class VectorGenerator;
class ParticleEmitter;
class ParticleSystemLoader
{
public:
ParticleSystemLoader(std::istream&);
~ParticleSystemLoader() = default;
ParticleSystem* load();
VectorGenerator* parseGenerator(Hash* params);
ParticleEmitter* parseEmitter(Hash* params);
const std::string& getErrorMessage() const;
void setTexturePath(const std::string&);
const std::string& getTexturePath() const;
static ParticleSystemLoader* OpenParticleSystemFile(std::istream& in);
private:
virtual void raiseError(const std::string&);
private:
Tokenizer m_tokenizer;
Parser m_parser;
std::string m_errorMessage;
std::string m_texPath;
};
ParticleSystem* LoadParticleSystem(std::istream& in, const std::string& texPath);
#endif // _CELENGINE_PARTICLESYSTEMFILE_H_

View File

@ -35,7 +35,6 @@ static const char CelestiaLegacyScriptExt[] = ".cel";
static const char CelestiaScriptExt[] = ".clx";
static const char CelestiaScriptExt2[] = ".celx";
static const char CelestiaModelExt[] = ".cmod";
static const char CelestiaParticleSystemExt[] = ".cpart";
static const char CelestiaXYZTrajectoryExt[] = ".xyz";
static const char CelestiaXYZVTrajectoryExt[] = ".xyzv";
static const char ContentXYZVBinaryExt[] = ".xyzvbin";
@ -82,8 +81,6 @@ ContentType DetermineFileType(const fs::path& filename)
return Content_CelestiaScript;
if (compareIgnoringCase(CelestiaModelExt, ext) == 0)
return Content_CelestiaModel;
if (compareIgnoringCase(CelestiaParticleSystemExt, ext) == 0)
return Content_CelestiaParticleSystem;
if (compareIgnoringCase(DXT5NormalMapExt, ext) == 0)
return Content_DXT5NormalMap;
if (compareIgnoringCase(CelestiaXYZTrajectoryExt, ext) == 0)

View File

@ -34,7 +34,7 @@ enum ContentType
Content_DXT5NormalMap = 17,
Content_CelestiaXYZTrajectory = 18,
Content_CelestiaXYZVTrajectory = 19,
Content_CelestiaParticleSystem = 20,
// Content_CelestiaParticleSystem = 20,
Content_WarpMesh = 21,
Content_CelestiaXYZVBinary = 22,
#ifdef USE_LIBAVIF