Remove unused particle system code
parent
86f2aab284
commit
5678539e05
|
@ -840,10 +840,6 @@ EXCLUDE = src/packdb.cpp \
|
||||||
src/celmath/plane.h \
|
src/celmath/plane.h \
|
||||||
src/celmath/quaternion.h \
|
src/celmath/quaternion.h \
|
||||||
src/celmath/vecmath.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/celengine/vertexbuf.h \
|
||||||
src/celtxf/*.*
|
src/celtxf/*.*
|
||||||
|
|
||||||
|
|
|
@ -91,10 +91,6 @@ set(CELENGINE_SOURCES
|
||||||
parseobject.h
|
parseobject.h
|
||||||
parser.cpp
|
parser.cpp
|
||||||
parser.h
|
parser.h
|
||||||
# particlesystem.cpp
|
|
||||||
# particlesystemfile.cpp
|
|
||||||
# particlesystemfile.h
|
|
||||||
# particlesystem.h
|
|
||||||
planetgrid.cpp
|
planetgrid.cpp
|
||||||
planetgrid.h
|
planetgrid.h
|
||||||
pointstarrenderer.cpp
|
pointstarrenderer.cpp
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
// as published by the Free Software Foundation; either version 2
|
// as published by the Free Software Foundation; either version 2
|
||||||
// of the License, or (at your option) any later version.
|
// of the License, or (at your option) any later version.
|
||||||
|
|
||||||
// Experimental particle system support
|
|
||||||
#define PARTICLE_SYSTEM 0
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -40,11 +37,6 @@
|
||||||
#include "spheremesh.h"
|
#include "spheremesh.h"
|
||||||
#include "texmanager.h"
|
#include "texmanager.h"
|
||||||
|
|
||||||
#if PARTICLE_SYSTEM
|
|
||||||
#include "particlesystem.h"
|
|
||||||
#include "particlesystemfile.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using celestia::util::GetLogger;
|
using celestia::util::GetLogger;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -509,16 +501,6 @@ GeometryInfo::load(const fs::path& resolvedFilename)
|
||||||
model->transform(center, scale);
|
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
|
// Condition the model for optimal rendering
|
||||||
if (model != nullptr)
|
if (model != nullptr)
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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_
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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_
|
|
|
@ -35,7 +35,6 @@ static const char CelestiaLegacyScriptExt[] = ".cel";
|
||||||
static const char CelestiaScriptExt[] = ".clx";
|
static const char CelestiaScriptExt[] = ".clx";
|
||||||
static const char CelestiaScriptExt2[] = ".celx";
|
static const char CelestiaScriptExt2[] = ".celx";
|
||||||
static const char CelestiaModelExt[] = ".cmod";
|
static const char CelestiaModelExt[] = ".cmod";
|
||||||
static const char CelestiaParticleSystemExt[] = ".cpart";
|
|
||||||
static const char CelestiaXYZTrajectoryExt[] = ".xyz";
|
static const char CelestiaXYZTrajectoryExt[] = ".xyz";
|
||||||
static const char CelestiaXYZVTrajectoryExt[] = ".xyzv";
|
static const char CelestiaXYZVTrajectoryExt[] = ".xyzv";
|
||||||
static const char ContentXYZVBinaryExt[] = ".xyzvbin";
|
static const char ContentXYZVBinaryExt[] = ".xyzvbin";
|
||||||
|
@ -82,8 +81,6 @@ ContentType DetermineFileType(const fs::path& filename)
|
||||||
return Content_CelestiaScript;
|
return Content_CelestiaScript;
|
||||||
if (compareIgnoringCase(CelestiaModelExt, ext) == 0)
|
if (compareIgnoringCase(CelestiaModelExt, ext) == 0)
|
||||||
return Content_CelestiaModel;
|
return Content_CelestiaModel;
|
||||||
if (compareIgnoringCase(CelestiaParticleSystemExt, ext) == 0)
|
|
||||||
return Content_CelestiaParticleSystem;
|
|
||||||
if (compareIgnoringCase(DXT5NormalMapExt, ext) == 0)
|
if (compareIgnoringCase(DXT5NormalMapExt, ext) == 0)
|
||||||
return Content_DXT5NormalMap;
|
return Content_DXT5NormalMap;
|
||||||
if (compareIgnoringCase(CelestiaXYZTrajectoryExt, ext) == 0)
|
if (compareIgnoringCase(CelestiaXYZTrajectoryExt, ext) == 0)
|
||||||
|
|
|
@ -34,7 +34,7 @@ enum ContentType
|
||||||
Content_DXT5NormalMap = 17,
|
Content_DXT5NormalMap = 17,
|
||||||
Content_CelestiaXYZTrajectory = 18,
|
Content_CelestiaXYZTrajectory = 18,
|
||||||
Content_CelestiaXYZVTrajectory = 19,
|
Content_CelestiaXYZVTrajectory = 19,
|
||||||
Content_CelestiaParticleSystem = 20,
|
// Content_CelestiaParticleSystem = 20,
|
||||||
Content_WarpMesh = 21,
|
Content_WarpMesh = 21,
|
||||||
Content_CelestiaXYZVBinary = 22,
|
Content_CelestiaXYZVBinary = 22,
|
||||||
#ifdef USE_LIBAVIF
|
#ifdef USE_LIBAVIF
|
||||||
|
|
Loading…
Reference in New Issue