1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 00:39:43 +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);
}
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)
{
NiTimeController::read(nif);
velocity = nif->getFloat();
velocityRandom = nif->getFloat();
verticalDir = nif->getFloat();
verticalAngle = nif->getFloat();
horizontalDir = nif->getFloat();
horizontalAngle = nif->getFloat();
/*normal?*/ nif->getVector3();
color = nif->getVector4();
size = nif->getFloat();
startTime = nif->getFloat();
stopTime = nif->getFloat();
nif->getChar();
emitRate = nif->getFloat();
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++)
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
nif->read(mSpeed);
nif->read(mSpeedVariation);
nif->read(mDeclination);
nif->read(mDeclinationVariation);
nif->read(mPlanarAngle);
nif->read(mPlanarAngleVariation);
nif->read(mInitialNormal);
nif->read(mInitialColor);
nif->read(mInitialSize);
nif->read(mEmitStartTime);
nif->read(mEmitStopTime);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
{
particles[i].velocity = nif->getVector3();
nif->getVector3(); /* unknown */
particles[i].lifetime = nif->getFloat();
particles[i].lifespan = nif->getFloat();
particles[i].timestamp = nif->getFloat();
nif->getUShort(); /* unknown */
particles[i].vertex = nif->getUShort();
mResetParticleSystem = nif->get<uint8_t>() != 0;
nif->read(mBirthRate);
}
nif->getUInt(); /* -1? */
affectors.read(nif);
colliders.read(nif);
nif->getChar();
nif->read(mLifetime);
nif->read(mLifetimeVariation);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
nif->read(mEmitFlags);
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)
{
NiTimeController::post(nif);
emitter.post(nif);
affectors.post(nif);
colliders.post(nif);
mEmitter.post(nif);
mModifier.post(nif);
mCollider.post(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
{
enum BSPArrayController
@ -102,55 +115,47 @@ namespace Nif
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
{
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
// in the system (numParticles) is never exceeded.
};
int emitFlags;
osg::Vec3f offsetRandom;
NiAVObjectPtr emitter;
int numParticles;
int activeCount;
std::vector<Particle> particles;
NiParticleModifierPtr affectors;
NiParticleModifierPtr colliders;
float mSpeed;
float mSpeedVariation;
float mDeclination;
float mDeclinationVariation;
float mPlanarAngle;
float mPlanarAngleVariation;
osg::Vec3f mInitialNormal;
osg::Vec4f mInitialColor;
float mInitialSize;
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 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; }
};
using NiBSPArrayController = NiParticleSystemController;

View file

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

View file

@ -379,7 +379,7 @@ namespace NifOsg
{
public:
ParticleSystemController(const Nif::NiParticleSystemController* ctrl);
ParticleSystemController();
ParticleSystemController() = default;
ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop);
META_Object(NifOsg, ParticleSystemController)
@ -387,8 +387,8 @@ namespace NifOsg
void operator()(osgParticle::ParticleProcessor* node, osg::NodeVisitor* nv);
private:
float mEmitStart;
float mEmitStop;
float mEmitStart{ 0.f };
float mEmitStop{ 0.f };
};
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,
osgParticle::ParticleProcessor::ReferenceFrame rf)
{
@ -1069,50 +1069,50 @@ namespace NifOsg
attachTo->addChild(program);
program->setParticleSystem(partsys);
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));
}
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));
}
else if (affectors->recType == Nif::RC_NiParticleColorModifier)
else if (modifier->recType == Nif::RC_NiParticleColorModifier)
{
const Nif::NiParticleColorModifier* cl
= static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr());
= static_cast<const Nif::NiParticleColorModifier*>(modifier.getPtr());
if (cl->mData.empty())
continue;
const Nif::NiColorData* clrdata = cl->mData.getPtr();
program->addOperator(new ParticleColorAffector(clrdata));
}
else if (affectors->recType == Nif::RC_NiParticleRotation)
else if (modifier->recType == Nif::RC_NiParticleRotation)
{
// unused
}
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
= static_cast<const Nif::NiPlanarCollider*>(colliders.getPtr());
= static_cast<const Nif::NiPlanarCollider*>(collider.getPtr());
program->addOperator(new PlanarCollider(planarcollider));
}
else if (colliders->recType == Nif::RC_NiSphericalCollider)
else if (collider->recType == Nif::RC_NiSphericalCollider)
{
const Nif::NiSphericalCollider* sphericalcollider
= static_cast<const Nif::NiSphericalCollider*>(colliders.getPtr());
= static_cast<const Nif::NiSphericalCollider*>(collider.getPtr());
program->addOperator(new SphericalCollider(sphericalcollider));
}
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);
if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData)
{
partsys->setQuota(partctrl->numParticles);
partsys->setQuota(partctrl->mParticles.size());
return;
}
@ -1134,35 +1134,35 @@ namespace NifOsg
osg::BoundingBox box;
int i = 0;
for (const auto& particle : partctrl->particles)
for (const auto& particle : partctrl->mParticles)
{
if (i++ >= particledata->mActiveCount)
break;
if (particle.lifespan <= 0)
if (particle.mLifespan <= 0)
continue;
if (particle.vertex >= particledata->mVertices.size())
if (particle.mCode >= particledata->mVertices.size())
continue;
ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime));
ParticleAgeSetter particletemplate(std::max(0.f, particle.mAge));
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,
// 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.
created->setVelocity(particle.velocity);
const osg::Vec3f& position = particledata->mVertices[particle.vertex];
created->setVelocity(particle.mVelocity);
const osg::Vec3f& position = particledata->mVertices[particle.mCode];
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));
float size = partctrl->size;
if (particle.vertex < particledata->mSizes.size())
size *= particledata->mSizes[particle.vertex];
float size = partctrl->mInitialSize;
if (particle.mCode < particledata->mSizes.size())
size *= particledata->mSizes[particle.mCode];
created->setSizeRange(osgParticle::rangef(size, size));
box.expandBy(osg::BoundingSphere(position, size));
@ -1179,39 +1179,39 @@ namespace NifOsg
std::vector<int> targets;
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);
osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter;
if (partctrl->noAutoAdjust())
counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate);
else if (partctrl->lifetime == 0 && partctrl->lifetimeRandom == 0)
counter->setNumberOfParticlesPerSecondToCreate(partctrl->mBirthRate);
else if (partctrl->mLifetime == 0 && partctrl->mLifetimeVariation == 0)
counter->setNumberOfParticlesPerSecondToCreate(0);
else
counter->setNumberOfParticlesPerSecondToCreate(
partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom / 2));
partctrl->mParticles.size() / (partctrl->mLifetime + partctrl->mLifetimeVariation / 2));
emitter->setCounter(counter);
ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom * 0.5f,
partctrl->velocity + partctrl->velocityRandom * 0.5f, partctrl->horizontalDir,
partctrl->horizontalAngle, partctrl->verticalDir, partctrl->verticalAngle, partctrl->lifetime,
partctrl->lifetimeRandom);
ParticleShooter* shooter = new ParticleShooter(partctrl->mSpeed - partctrl->mSpeedVariation * 0.5f,
partctrl->mSpeed + partctrl->mSpeedVariation * 0.5f, partctrl->mPlanarAngle,
partctrl->mPlanarAngleVariation, partctrl->mDeclination, partctrl->mDeclinationVariation,
partctrl->mLifetime, partctrl->mLifetimeVariation);
emitter->setShooter(shooter);
emitter->setFlags(partctrl->mFlags);
if (partctrl->recType == Nif::RC_NiBSPArrayController && partctrl->emitAtVertex())
{
emitter->setGeometryEmitterTarget(partctrl->emitter->recIndex);
emitter->setGeometryEmitterTarget(partctrl->mEmitter->recIndex);
}
else
{
osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer;
placer->setXRange(-partctrl->offsetRandom.x() / 2.f, partctrl->offsetRandom.x() / 2.f);
placer->setYRange(-partctrl->offsetRandom.y() / 2.f, partctrl->offsetRandom.y() / 2.f);
placer->setZRange(-partctrl->offsetRandom.z() / 2.f, partctrl->offsetRandom.z() / 2.f);
placer->setXRange(-partctrl->mEmitterDimensions.x() / 2.f, partctrl->mEmitterDimensions.x() / 2.f);
placer->setYRange(-partctrl->mEmitterDimensions.y() / 2.f, partctrl->mEmitterDimensions.y() / 2.f);
placer->setZRange(-partctrl->mEmitterDimensions.z() / 2.f, partctrl->mEmitterDimensions.z() / 2.f);
emitter->setPlacer(placer);
}
@ -1280,11 +1280,11 @@ namespace NifOsg
handleParticleInitialState(nifNode, partsys, partctrl);
partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size));
partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color));
partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->mInitialSize, partctrl->mInitialSize));
partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(partctrl->mInitialColor, partctrl->mInitialColor));
partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f));
if (!partctrl->emitter.empty())
if (!partctrl->mEmitter.empty())
{
osg::ref_ptr<Emitter> emitter = handleParticleEmitter(partctrl);
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
// 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.
mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter);
mEmitterQueue.emplace_back(partctrl->mEmitter->recIndex, emitter);
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
setupController(partctrl, callback, animflags);
@ -1310,16 +1310,16 @@ namespace NifOsg
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
// 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;
collectDrawableProperties(nifNode, parent, drawableProps);
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
// way
osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;