1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 10:39:42 +00:00

Refactor NiParticleSystemController and update definitions

This commit is contained in:
Alexei Kotov 2023-09-14 04:24:37 +03:00
parent d55ba0cfa2
commit 8856dff3db
5 changed files with 148 additions and 145 deletions

View file

@ -162,67 +162,71 @@ namespace Nif
mInterpolator.post(nif); mInterpolator.post(nif);
} }
void NiParticleInfo::read(NIFStream* nif)
{
nif->read(mVelocity);
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
nif->read(mRotationAxis);
nif->read(mAge);
nif->read(mLifespan);
nif->read(mLastUpdate);
nif->read(mSpawnGeneration);
nif->read(mCode);
}
void NiParticleSystemController::read(NIFStream* nif) void NiParticleSystemController::read(NIFStream* nif)
{ {
NiTimeController::read(nif); NiTimeController::read(nif);
velocity = nif->getFloat(); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
velocityRandom = nif->getFloat(); nif->read(mSpeed);
verticalDir = nif->getFloat(); nif->read(mSpeedVariation);
verticalAngle = nif->getFloat(); nif->read(mDeclination);
horizontalDir = nif->getFloat(); nif->read(mDeclinationVariation);
horizontalAngle = nif->getFloat(); nif->read(mPlanarAngle);
/*normal?*/ nif->getVector3(); nif->read(mPlanarAngleVariation);
color = nif->getVector4(); nif->read(mInitialNormal);
size = nif->getFloat(); nif->read(mInitialColor);
startTime = nif->getFloat(); nif->read(mInitialSize);
stopTime = nif->getFloat(); nif->read(mEmitStartTime);
nif->getChar(); nif->read(mEmitStopTime);
emitRate = nif->getFloat(); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
lifetime = nif->getFloat();
lifetimeRandom = nif->getFloat();
emitFlags = nif->getUShort();
offsetRandom = nif->getVector3();
emitter.read(nif);
/* Unknown Short, 0?
* Unknown Float, 1.0?
* Unknown Int, 1?
* Unknown Int, 0?
* Unknown Short, 0?
*/
nif->skip(16);
numParticles = nif->getUShort();
activeCount = nif->getUShort();
particles.resize(numParticles);
for (size_t i = 0; i < particles.size(); i++)
{ {
particles[i].velocity = nif->getVector3(); mResetParticleSystem = nif->get<uint8_t>() != 0;
nif->getVector3(); /* unknown */ nif->read(mBirthRate);
particles[i].lifetime = nif->getFloat();
particles[i].lifespan = nif->getFloat();
particles[i].timestamp = nif->getFloat();
nif->getUShort(); /* unknown */
particles[i].vertex = nif->getUShort();
} }
nif->read(mLifetime);
nif->getUInt(); /* -1? */ nif->read(mLifetimeVariation);
affectors.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
colliders.read(nif); nif->read(mEmitFlags);
nif->getChar(); nif->read(mEmitterDimensions);
mEmitter.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
{
nif->read(mNumSpawnGenerations);
nif->read(mPercentageSpawned);
nif->read(mSpawnMultiplier);
nif->read(mSpawnSpeedChaos);
nif->read(mSpawnDirChaos);
mParticles.resize(nif->get<uint16_t>());
nif->read(mNumValid);
for (NiParticleInfo& particle : mParticles)
particle.read(nif);
nif->skip(4); // NiEmitterModifier link
}
mModifier.read(nif);
mCollider.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 15))
nif->read(mStaticTargetBound);
} }
void NiParticleSystemController::post(Reader& nif) void NiParticleSystemController::post(Reader& nif)
{ {
NiTimeController::post(nif); NiTimeController::post(nif);
emitter.post(nif); mEmitter.post(nif);
affectors.post(nif); mModifier.post(nif);
colliders.post(nif); mCollider.post(nif);
} }
void NiMaterialColorController::read(NIFStream* nif) void NiMaterialColorController::read(NIFStream* nif)

View file

@ -94,6 +94,19 @@ namespace Nif
{ {
}; };
struct NiParticleInfo
{
osg::Vec3f mVelocity;
osg::Vec3f mRotationAxis;
float mAge;
float mLifespan;
float mLastUpdate;
uint16_t mSpawnGeneration;
uint16_t mCode;
void read(NIFStream* nif);
};
struct NiParticleSystemController : public NiTimeController struct NiParticleSystemController : public NiTimeController
{ {
enum BSPArrayController enum BSPArrayController
@ -102,55 +115,47 @@ namespace Nif
BSPArrayController_AtVertex = 0x10 BSPArrayController_AtVertex = 0x10
}; };
struct Particle
{
osg::Vec3f velocity;
float lifetime;
float lifespan;
float timestamp;
unsigned short vertex;
};
float velocity;
float velocityRandom;
float verticalDir; // 0=up, pi/2=horizontal, pi=down
float verticalAngle;
float horizontalDir;
float horizontalAngle;
osg::Vec4f color;
float size;
float startTime;
float stopTime;
float emitRate;
float lifetime;
float lifetimeRandom;
enum EmitFlags enum EmitFlags
{ {
EmitFlag_NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise, EmitFlag_NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise,
// we calculate an emit rate so that the maximum number of particles // we calculate an emit rate so that the maximum number of particles
// in the system (numParticles) is never exceeded. // in the system (numParticles) is never exceeded.
}; };
int emitFlags;
osg::Vec3f offsetRandom; float mSpeed;
float mSpeedVariation;
NiAVObjectPtr emitter; float mDeclination;
float mDeclinationVariation;
int numParticles; float mPlanarAngle;
int activeCount; float mPlanarAngleVariation;
std::vector<Particle> particles; osg::Vec3f mInitialNormal;
osg::Vec4f mInitialColor;
NiParticleModifierPtr affectors; float mInitialSize;
NiParticleModifierPtr colliders; float mEmitStartTime;
float mEmitStopTime;
bool mResetParticleSystem{ false };
float mBirthRate;
float mLifetime;
float mLifetimeVariation;
uint16_t mEmitFlags{ 0 };
osg::Vec3f mEmitterDimensions;
NiAVObjectPtr mEmitter;
uint16_t mNumSpawnGenerations;
float mPercentageSpawned;
uint16_t mSpawnMultiplier;
float mSpawnSpeedChaos;
float mSpawnDirChaos;
uint16_t mNumParticles;
uint16_t mNumValid;
std::vector<NiParticleInfo> mParticles;
NiParticleModifierPtr mModifier;
NiParticleModifierPtr mCollider;
uint8_t mStaticTargetBound;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
bool noAutoAdjust() const { return emitFlags & EmitFlag_NoAutoAdjust; } bool noAutoAdjust() const { return mEmitFlags & EmitFlag_NoAutoAdjust; }
bool emitAtVertex() const { return mFlags & BSPArrayController_AtVertex; } bool emitAtVertex() const { return mFlags & BSPArrayController_AtVertex; }
}; };
using NiBSPArrayController = NiParticleSystemController; using NiBSPArrayController = NiParticleSystemController;

View file

@ -556,14 +556,8 @@ namespace NifOsg
} }
ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController* ctrl) ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController* ctrl)
: mEmitStart(ctrl->startTime) : mEmitStart(ctrl->mEmitStartTime)
, mEmitStop(ctrl->stopTime) , mEmitStop(ctrl->mEmitStopTime)
{
}
ParticleSystemController::ParticleSystemController()
: mEmitStart(0.f)
, mEmitStop(0.f)
{ {
} }

View file

@ -379,7 +379,7 @@ namespace NifOsg
{ {
public: public:
ParticleSystemController(const Nif::NiParticleSystemController* ctrl); ParticleSystemController(const Nif::NiParticleSystemController* ctrl);
ParticleSystemController(); ParticleSystemController() = default;
ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop); ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop);
META_Object(NifOsg, ParticleSystemController) META_Object(NifOsg, ParticleSystemController)
@ -387,8 +387,8 @@ namespace NifOsg
void operator()(osgParticle::ParticleProcessor* node, osg::NodeVisitor* nv); void operator()(osgParticle::ParticleProcessor* node, osg::NodeVisitor* nv);
private: private:
float mEmitStart; float mEmitStart{ 0.f };
float mEmitStop; float mEmitStop{ 0.f };
}; };
class PathController : public SceneUtil::NodeCallback<PathController, NifOsg::MatrixTransform*>, class PathController : public SceneUtil::NodeCallback<PathController, NifOsg::MatrixTransform*>,

View file

@ -1061,7 +1061,7 @@ namespace NifOsg
} }
} }
void handleParticlePrograms(Nif::NiParticleModifierPtr affectors, Nif::NiParticleModifierPtr colliders, void handleParticlePrograms(Nif::NiParticleModifierPtr modifier, Nif::NiParticleModifierPtr collider,
osg::Group* attachTo, osgParticle::ParticleSystem* partsys, osg::Group* attachTo, osgParticle::ParticleSystem* partsys,
osgParticle::ParticleProcessor::ReferenceFrame rf) osgParticle::ParticleProcessor::ReferenceFrame rf)
{ {
@ -1069,50 +1069,50 @@ namespace NifOsg
attachTo->addChild(program); attachTo->addChild(program);
program->setParticleSystem(partsys); program->setParticleSystem(partsys);
program->setReferenceFrame(rf); program->setReferenceFrame(rf);
for (; !affectors.empty(); affectors = affectors->mNext) for (; !modifier.empty(); modifier = modifier->mNext)
{ {
if (affectors->recType == Nif::RC_NiParticleGrowFade) if (modifier->recType == Nif::RC_NiParticleGrowFade)
{ {
const Nif::NiParticleGrowFade* gf = static_cast<const Nif::NiParticleGrowFade*>(affectors.getPtr()); const Nif::NiParticleGrowFade* gf = static_cast<const Nif::NiParticleGrowFade*>(modifier.getPtr());
program->addOperator(new GrowFadeAffector(gf->mGrowTime, gf->mFadeTime)); program->addOperator(new GrowFadeAffector(gf->mGrowTime, gf->mFadeTime));
} }
else if (affectors->recType == Nif::RC_NiGravity) else if (modifier->recType == Nif::RC_NiGravity)
{ {
const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(affectors.getPtr()); const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(modifier.getPtr());
program->addOperator(new GravityAffector(gr)); program->addOperator(new GravityAffector(gr));
} }
else if (affectors->recType == Nif::RC_NiParticleColorModifier) else if (modifier->recType == Nif::RC_NiParticleColorModifier)
{ {
const Nif::NiParticleColorModifier* cl const Nif::NiParticleColorModifier* cl
= static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr()); = static_cast<const Nif::NiParticleColorModifier*>(modifier.getPtr());
if (cl->mData.empty()) if (cl->mData.empty())
continue; continue;
const Nif::NiColorData* clrdata = cl->mData.getPtr(); const Nif::NiColorData* clrdata = cl->mData.getPtr();
program->addOperator(new ParticleColorAffector(clrdata)); program->addOperator(new ParticleColorAffector(clrdata));
} }
else if (affectors->recType == Nif::RC_NiParticleRotation) else if (modifier->recType == Nif::RC_NiParticleRotation)
{ {
// unused // unused
} }
else else
Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle modifier " << modifier->recName << " in " << mFilename;
} }
for (; !colliders.empty(); colliders = colliders->mNext) for (; !collider.empty(); collider = collider->mNext)
{ {
if (colliders->recType == Nif::RC_NiPlanarCollider) if (collider->recType == Nif::RC_NiPlanarCollider)
{ {
const Nif::NiPlanarCollider* planarcollider const Nif::NiPlanarCollider* planarcollider
= static_cast<const Nif::NiPlanarCollider*>(colliders.getPtr()); = static_cast<const Nif::NiPlanarCollider*>(collider.getPtr());
program->addOperator(new PlanarCollider(planarcollider)); program->addOperator(new PlanarCollider(planarcollider));
} }
else if (colliders->recType == Nif::RC_NiSphericalCollider) else if (collider->recType == Nif::RC_NiSphericalCollider)
{ {
const Nif::NiSphericalCollider* sphericalcollider const Nif::NiSphericalCollider* sphericalcollider
= static_cast<const Nif::NiSphericalCollider*>(colliders.getPtr()); = static_cast<const Nif::NiSphericalCollider*>(collider.getPtr());
program->addOperator(new SphericalCollider(sphericalcollider)); program->addOperator(new SphericalCollider(sphericalcollider));
} }
else else
Log(Debug::Info) << "Unhandled particle collider " << colliders->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle collider " << collider->recName << " in " << mFilename;
} }
} }
@ -1124,7 +1124,7 @@ namespace NifOsg
auto particleNode = static_cast<const Nif::NiParticles*>(nifNode); auto particleNode = static_cast<const Nif::NiParticles*>(nifNode);
if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData) if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData)
{ {
partsys->setQuota(partctrl->numParticles); partsys->setQuota(partctrl->mParticles.size());
return; return;
} }
@ -1134,35 +1134,35 @@ namespace NifOsg
osg::BoundingBox box; osg::BoundingBox box;
int i = 0; int i = 0;
for (const auto& particle : partctrl->particles) for (const auto& particle : partctrl->mParticles)
{ {
if (i++ >= particledata->mActiveCount) if (i++ >= particledata->mActiveCount)
break; break;
if (particle.lifespan <= 0) if (particle.mLifespan <= 0)
continue; continue;
if (particle.vertex >= particledata->mVertices.size()) if (particle.mCode >= particledata->mVertices.size())
continue; continue;
ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); ParticleAgeSetter particletemplate(std::max(0.f, particle.mAge));
osgParticle::Particle* created = partsys->createParticle(&particletemplate); osgParticle::Particle* created = partsys->createParticle(&particletemplate);
created->setLifeTime(particle.lifespan); created->setLifeTime(particle.mLifespan);
// Note this position and velocity is not correct for a particle system with absolute reference frame, // Note this position and velocity is not correct for a particle system with absolute reference frame,
// which can not be done in this loader since we are not attached to the scene yet. Will be fixed up // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up
// post-load in the SceneManager. // post-load in the SceneManager.
created->setVelocity(particle.velocity); created->setVelocity(particle.mVelocity);
const osg::Vec3f& position = particledata->mVertices[particle.vertex]; const osg::Vec3f& position = particledata->mVertices[particle.mCode];
created->setPosition(position); created->setPosition(position);
created->setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color)); created->setColorRange(osgParticle::rangev4(partctrl->mInitialColor, partctrl->mInitialColor));
created->setAlphaRange(osgParticle::rangef(1.f, 1.f)); created->setAlphaRange(osgParticle::rangef(1.f, 1.f));
float size = partctrl->size; float size = partctrl->mInitialSize;
if (particle.vertex < particledata->mSizes.size()) if (particle.mCode < particledata->mSizes.size())
size *= particledata->mSizes[particle.vertex]; size *= particledata->mSizes[particle.mCode];
created->setSizeRange(osgParticle::rangef(size, size)); created->setSizeRange(osgParticle::rangef(size, size));
box.expandBy(osg::BoundingSphere(position, size)); box.expandBy(osg::BoundingSphere(position, size));
@ -1179,39 +1179,39 @@ namespace NifOsg
std::vector<int> targets; std::vector<int> targets;
if (partctrl->recType == Nif::RC_NiBSPArrayController && !partctrl->emitAtVertex()) if (partctrl->recType == Nif::RC_NiBSPArrayController && !partctrl->emitAtVertex())
{ {
getAllNiNodes(partctrl->emitter.getPtr(), targets); getAllNiNodes(partctrl->mEmitter.getPtr(), targets);
} }
osg::ref_ptr<Emitter> emitter = new Emitter(targets); osg::ref_ptr<Emitter> emitter = new Emitter(targets);
osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter; osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter;
if (partctrl->noAutoAdjust()) if (partctrl->noAutoAdjust())
counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate); counter->setNumberOfParticlesPerSecondToCreate(partctrl->mBirthRate);
else if (partctrl->lifetime == 0 && partctrl->lifetimeRandom == 0) else if (partctrl->mLifetime == 0 && partctrl->mLifetimeVariation == 0)
counter->setNumberOfParticlesPerSecondToCreate(0); counter->setNumberOfParticlesPerSecondToCreate(0);
else else
counter->setNumberOfParticlesPerSecondToCreate( counter->setNumberOfParticlesPerSecondToCreate(
partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom / 2)); partctrl->mParticles.size() / (partctrl->mLifetime + partctrl->mLifetimeVariation / 2));
emitter->setCounter(counter); emitter->setCounter(counter);
ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom * 0.5f, ParticleShooter* shooter = new ParticleShooter(partctrl->mSpeed - partctrl->mSpeedVariation * 0.5f,
partctrl->velocity + partctrl->velocityRandom * 0.5f, partctrl->horizontalDir, partctrl->mSpeed + partctrl->mSpeedVariation * 0.5f, partctrl->mPlanarAngle,
partctrl->horizontalAngle, partctrl->verticalDir, partctrl->verticalAngle, partctrl->lifetime, partctrl->mPlanarAngleVariation, partctrl->mDeclination, partctrl->mDeclinationVariation,
partctrl->lifetimeRandom); partctrl->mLifetime, partctrl->mLifetimeVariation);
emitter->setShooter(shooter); emitter->setShooter(shooter);
emitter->setFlags(partctrl->mFlags); emitter->setFlags(partctrl->mFlags);
if (partctrl->recType == Nif::RC_NiBSPArrayController && partctrl->emitAtVertex()) if (partctrl->recType == Nif::RC_NiBSPArrayController && partctrl->emitAtVertex())
{ {
emitter->setGeometryEmitterTarget(partctrl->emitter->recIndex); emitter->setGeometryEmitterTarget(partctrl->mEmitter->recIndex);
} }
else else
{ {
osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer; osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer;
placer->setXRange(-partctrl->offsetRandom.x() / 2.f, partctrl->offsetRandom.x() / 2.f); placer->setXRange(-partctrl->mEmitterDimensions.x() / 2.f, partctrl->mEmitterDimensions.x() / 2.f);
placer->setYRange(-partctrl->offsetRandom.y() / 2.f, partctrl->offsetRandom.y() / 2.f); placer->setYRange(-partctrl->mEmitterDimensions.y() / 2.f, partctrl->mEmitterDimensions.y() / 2.f);
placer->setZRange(-partctrl->offsetRandom.z() / 2.f, partctrl->offsetRandom.z() / 2.f); placer->setZRange(-partctrl->mEmitterDimensions.z() / 2.f, partctrl->mEmitterDimensions.z() / 2.f);
emitter->setPlacer(placer); emitter->setPlacer(placer);
} }
@ -1280,11 +1280,11 @@ namespace NifOsg
handleParticleInitialState(nifNode, partsys, partctrl); handleParticleInitialState(nifNode, partsys, partctrl);
partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size)); partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->mInitialSize, partctrl->mInitialSize));
partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color)); partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(partctrl->mInitialColor, partctrl->mInitialColor));
partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f)); partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f));
if (!partctrl->emitter.empty()) if (!partctrl->mEmitter.empty())
{ {
osg::ref_ptr<Emitter> emitter = handleParticleEmitter(partctrl); osg::ref_ptr<Emitter> emitter = handleParticleEmitter(partctrl);
emitter->setParticleSystem(partsys); emitter->setParticleSystem(partsys);
@ -1293,7 +1293,7 @@ namespace NifOsg
// The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later // The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later
// moment. If the emitter node is placed later than the particle node, it'll have a single frame delay // moment. If the emitter node is placed later than the particle node, it'll have a single frame delay
// in particle processing. But that shouldn't be a game-breaking issue. // in particle processing. But that shouldn't be a game-breaking issue.
mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter); mEmitterQueue.emplace_back(partctrl->mEmitter->recIndex, emitter);
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl)); osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
setupController(partctrl, callback, animflags); setupController(partctrl, callback, animflags);
@ -1310,16 +1310,16 @@ namespace NifOsg
partsys->update(0.0, nv); partsys->update(0.0, nv);
} }
// affectors should be attached *after* the emitter in the scene graph for correct update order // modifiers should be attached *after* the emitter in the scene graph for correct update order
// attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct // attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct
// localToWorldMatrix for transforming to particle space // localToWorldMatrix for transforming to particle space
handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf); handleParticlePrograms(partctrl->mModifier, partctrl->mCollider, parentNode, partsys.get(), rf);
std::vector<const Nif::Property*> drawableProps; std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, parent, drawableProps); collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, true, animflags); applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
// particle system updater (after the emitters and affectors in the scene graph) // particle system updater (after the emitters and modifiers in the scene graph)
// I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other // I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other
// way // way
osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater; osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;