From 6d9deaa38643586b18dd35aa6ebd5096bef2c120 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Mar 2015 04:57:01 +0100 Subject: [PATCH] Implement planar collider for particles (Fixes #2149) --- components/nif/controlled.cpp | 10 ++++++-- components/nif/controlled.hpp | 5 ++++ components/nif/controller.cpp | 7 +++--- components/nif/controller.hpp | 3 ++- components/nifosg/nifloader.cpp | 41 +++++++++++++++++++------------- components/nifosg/particle.cpp | 42 +++++++++++++++++++++++++++++++++ components/nifosg/particle.hpp | 19 +++++++++++++++ 7 files changed, 104 insertions(+), 23 deletions(-) diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index 526b2eca3..9db0c4a6f 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -65,8 +65,14 @@ namespace Nif { Controlled::read(nif); - // (I think) 4 floats + 4 vectors - nif->skip(4*16); + mBounceFactor = nif->getFloat(); + /*unknown*/nif->getFloat(); + + for (int i=0;i<10;++i) + /*unknown*/nif->getFloat(); + + mPlaneNormal = nif->getVector3(); + mPlaneDistance = nif->getFloat(); } void NiParticleRotation::read(NIFStream *nif) diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index ac75c3508..36d90b03d 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -103,6 +103,11 @@ class NiPlanarCollider : public Controlled { public: void read(NIFStream *nif); + + float mBounceFactor; + + osg::Vec3f mPlaneNormal; + float mPlaneDistance; }; class NiParticleRotation : public Controlled diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 376a1fe12..f39132543 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -76,8 +76,8 @@ namespace Nif } nif->getUInt(); /* -1? */ - extra.read(nif); - nif->getUInt(); /* -1? */ + affectors.read(nif); + colliders.read(nif); nif->getChar(); } @@ -85,7 +85,8 @@ namespace Nif { Controller::post(nif); emitter.post(nif); - extra.post(nif); + affectors.post(nif); + colliders.post(nif); } void NiMaterialColorController::read(NIFStream *nif) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 73344e77b..0861dfa6b 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -72,7 +72,8 @@ public: int activeCount; std::vector particles; - ExtraPtr extra; + ExtraPtr affectors; + ExtraPtr colliders; void read(NIFStream *nif); void post(NIFFile *nif); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d05758f2e..a2fd8e39a 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -719,39 +719,44 @@ namespace NifOsg } } - void handleParticleAffectors(Nif::ExtraPtr e, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) + void handleParticlePrograms(Nif::ExtraPtr affectors, Nif::ExtraPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) { osgParticle::ModularProgram* program = new osgParticle::ModularProgram; attachTo->addChild(program); program->setParticleSystem(partsys); program->setReferenceFrame(rf); - for (; !e.empty(); e = e->extra) + for (; !affectors.empty(); affectors = affectors->extra) { - if (e->recType == Nif::RC_NiParticleGrowFade) + if (affectors->recType == Nif::RC_NiParticleGrowFade) { - const Nif::NiParticleGrowFade *gf = static_cast(e.getPtr()); - GrowFadeAffector* affector = new GrowFadeAffector(gf->growTime, gf->fadeTime); - program->addOperator(affector); + const Nif::NiParticleGrowFade *gf = static_cast(affectors.getPtr()); + program->addOperator(new GrowFadeAffector(gf->growTime, gf->fadeTime)); } - else if (e->recType == Nif::RC_NiGravity) + else if (affectors->recType == Nif::RC_NiGravity) { - const Nif::NiGravity* gr = static_cast(e.getPtr()); - GravityAffector* affector = new GravityAffector(gr); - program->addOperator(affector); + const Nif::NiGravity* gr = static_cast(affectors.getPtr()); + program->addOperator(new GravityAffector(gr)); } - else if (e->recType == Nif::RC_NiParticleColorModifier) + else if (affectors->recType == Nif::RC_NiParticleColorModifier) { - const Nif::NiParticleColorModifier *cl = static_cast(e.getPtr()); + const Nif::NiParticleColorModifier *cl = static_cast(affectors.getPtr()); const Nif::NiColorData *clrdata = cl->data.getPtr(); - ParticleColorAffector* affector = new ParticleColorAffector(clrdata); - program->addOperator(affector); + program->addOperator(new ParticleColorAffector(clrdata)); } - else if (e->recType == Nif::RC_NiParticleRotation) + else if (affectors->recType == Nif::RC_NiParticleRotation) { // TODO: Implement? } else - std::cerr << "Unhandled particle modifier " << e->recName << std::endl; + std::cerr << "Unhandled particle modifier " << affectors->recName << std::endl; + } + for (; !colliders.empty(); colliders = colliders->extra) + { + if (colliders->recType == Nif::RC_NiPlanarCollider) + { + const Nif::NiPlanarCollider* planarcollider = static_cast(colliders.getPtr()); + program->addOperator(new PlanarCollider(planarcollider)); + } } } @@ -888,7 +893,9 @@ namespace NifOsg emitter->setUpdateCallback(callback); // affectors must be attached *after* the emitter in the scene graph for correct update order - handleParticleAffectors(partctrl->extra, emitterNode, partsys.get(), rf); + // 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); osg::ref_ptr geode (new osg::Geode); geode->addDrawable(partsys); diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 899ce7537..0afbf5e95 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -303,4 +303,46 @@ void FindRecIndexVisitor::apply(osg::Node &searchNode) traverse(searchNode); } +PlanarCollider::PlanarCollider(const Nif::NiPlanarCollider *collider) + : mBounceFactor(collider->mBounceFactor) + , mPlane(-collider->mPlaneNormal, collider->mPlaneDistance) +{ +} + +PlanarCollider::PlanarCollider() + : mBounceFactor(0.f) +{ +} + +PlanarCollider::PlanarCollider(const PlanarCollider ©, const osg::CopyOp ©op) + : osgParticle::Operator(copy, copyop) + , mBounceFactor(copy.mBounceFactor) + , mPlane(copy.mPlane) + , mPlaneInParticleSpace(copy.mPlaneInParticleSpace) +{ +} + +void PlanarCollider::beginOperate(osgParticle::Program *program) +{ + mPlaneInParticleSpace = mPlane; + if (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF) + mPlaneInParticleSpace.transform(program->getLocalToWorldMatrix()); +} + +void PlanarCollider::operate(osgParticle::Particle *particle, double dt) +{ + float dotproduct = particle->getVelocity() * mPlaneInParticleSpace.getNormal(); + + if (dotproduct > 0) + { + osg::BoundingSphere bs(particle->getPosition(), 0.f); + if (mPlaneInParticleSpace.intersect(bs) == 1) + { + osg::Vec3 reflectedVelocity = particle->getVelocity() - mPlaneInParticleSpace.getNormal() * (2 * dotproduct); + reflectedVelocity *= mBounceFactor; + particle->setVelocity(reflectedVelocity); + } + } +} + } diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index 01e06f471..99db3c83a 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -17,6 +17,7 @@ namespace Nif { class NiGravity; + class NiPlanarCollider; } namespace NifOsg @@ -92,6 +93,24 @@ namespace NifOsg float mLifetimeRandom; }; + class PlanarCollider : public osgParticle::Operator + { + public: + PlanarCollider(const Nif::NiPlanarCollider* collider); + PlanarCollider(); + PlanarCollider(const PlanarCollider& copy, const osg::CopyOp& copyop); + + META_Object(NifOsg, PlanarCollider) + + virtual void beginOperate(osgParticle::Program* program); + virtual void operate(osgParticle::Particle* particle, double dt); + + private: + float mBounceFactor; + osg::Plane mPlane; + osg::Plane mPlaneInParticleSpace; + }; + class GrowFadeAffector : public osgParticle::Operator { public: