From 99bcf4716778a4e45bce60b02d1ea210c6928ef8 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 5 Dec 2016 21:31:17 +0100 Subject: [PATCH] Implement NiSphericalCollider (Closes #3644) --- components/nif/controlled.cpp | 9 +++++ components/nif/controlled.hpp | 10 +++++ components/nif/niffile.cpp | 1 + components/nif/record.hpp | 3 +- components/nifosg/nifloader.cpp | 7 ++++ components/nifosg/particle.cpp | 69 +++++++++++++++++++++++++++++++++ components/nifosg/particle.hpp | 18 +++++++++ 7 files changed, 116 insertions(+), 1 deletion(-) diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index 5c63094ce..52e7a7302 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -87,6 +87,15 @@ namespace Nif nif->skip(17); } + void NiSphericalCollider::read(NIFStream* nif) + { + Controlled::read(nif); + + mBounceFactor = nif->getFloat(); + mRadius = nif->getFloat(); + mCenter = nif->getVector3(); + } + diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 4bd7ce1f9..5601474ac 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -111,6 +111,16 @@ public: float mPlaneDistance; }; +class NiSphericalCollider : public Controlled +{ +public: + float mBounceFactor; + float mRadius; + osg::Vec3f mCenter; + + void read(NIFStream *nif); +}; + class NiParticleRotation : public Controlled { public: diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index f6e489527..b4b1caefc 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -89,6 +89,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiStringExtraData", &construct , RC_NiStringExtraData )); newFactory.insert(makeEntry("NiGravity", &construct , RC_NiGravity )); newFactory.insert(makeEntry("NiPlanarCollider", &construct , RC_NiPlanarCollider )); + newFactory.insert(makeEntry("NiSphericalCollider", &construct , RC_NiSphericalCollider )); newFactory.insert(makeEntry("NiParticleGrowFade", &construct , RC_NiParticleGrowFade )); newFactory.insert(makeEntry("NiParticleColorModifier", &construct , RC_NiParticleColorModifier )); newFactory.insert(makeEntry("NiParticleRotation", &construct , RC_NiParticleRotation )); diff --git a/components/nif/record.hpp b/components/nif/record.hpp index bcbdba115..605c4d76e 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -92,7 +92,8 @@ enum RecordType RC_NiSequenceStreamHelper, RC_NiSourceTexture, RC_NiSkinInstance, - RC_RootCollisionNode + RC_RootCollisionNode, + RC_NiSphericalCollider }; /// Base class for all records diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 70ca82875..d4dabd2f9 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -871,6 +871,13 @@ namespace NifOsg const Nif::NiPlanarCollider* planarcollider = static_cast(colliders.getPtr()); program->addOperator(new PlanarCollider(planarcollider)); } + else if (colliders->recType == Nif::RC_NiSphericalCollider) + { + const Nif::NiSphericalCollider* sphericalcollider = static_cast(colliders.getPtr()); + program->addOperator(new SphericalCollider(sphericalcollider)); + } + else + std::cerr << "Unhandled particle collider " << colliders->recName << " in " << mFilename << std::endl; } } diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index f542cf379..0259b27e4 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -376,4 +376,73 @@ void PlanarCollider::operate(osgParticle::Particle *particle, double dt) } } +SphericalCollider::SphericalCollider(const Nif::NiSphericalCollider* collider) + : mBounceFactor(collider->mBounceFactor), + mSphere(collider->mCenter, collider->mRadius) +{ +} + +SphericalCollider::SphericalCollider() + : mBounceFactor(1.0f) +{ + +} + +SphericalCollider::SphericalCollider(const SphericalCollider& copy, const osg::CopyOp& copyop) + : osgParticle::Operator(copy, copyop) + , mBounceFactor(copy.mBounceFactor) + , mSphere(copy.mSphere) + , mSphereInParticleSpace(copy.mSphereInParticleSpace) +{ + +} + +void SphericalCollider::beginOperate(osgParticle::Program* program) +{ + mSphereInParticleSpace = mSphere; + if (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF) + mSphereInParticleSpace.center() = program->transformLocalToWorld(mSphereInParticleSpace.center()); +} + +void SphericalCollider::operate(osgParticle::Particle* particle, double dt) +{ + osg::Vec3f cent = (particle->getPosition() - mSphereInParticleSpace.center()); // vector from sphere center to particle + + bool insideSphere = cent.length2() <= mSphereInParticleSpace.radius2(); + + if (insideSphere + || (cent * particle->getVelocity() < 0.0f)) // if outside, make sure the particle is flying towards the sphere + { + // Collision test (finding point of contact) is performed by solving a quadratic equation: + // ||vec(cent) + vec(vel)*k|| = R /^2 + // k^2 + 2*k*(vec(cent)*vec(vel))/||vec(vel)||^2 + (||vec(cent)||^2 - R^2)/||vec(vel)||^2 = 0 + + float b = -(cent * particle->getVelocity()) / particle->getVelocity().length2(); + + osg::Vec3f u = cent + particle->getVelocity() * b; + + if (insideSphere + || (u.length2() < mSphereInParticleSpace.radius2())) + { + float d = (mSphereInParticleSpace.radius2() - u.length2()) / particle->getVelocity().length2(); + float k = insideSphere ? (std::sqrt(d) + b) : (b - std::sqrt(d)); + + if (k < dt) + { + // collision detected; reflect off the tangent plane + osg::Vec3f contact = particle->getPosition() + particle->getVelocity() * k; + + osg::Vec3 normal = (contact - mSphereInParticleSpace.center()); + normal.normalize(); + + float dotproduct = particle->getVelocity() * normal; + + osg::Vec3 reflectedVelocity = particle->getVelocity() - normal * (2 * dotproduct); + reflectedVelocity *= mBounceFactor; + particle->setVelocity(reflectedVelocity); + } + } + } +} + } diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index 6818aa7e0..f9c4abdac 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -16,6 +16,7 @@ namespace Nif { class NiGravity; class NiPlanarCollider; + class NiSphericalCollider; class NiColorData; } @@ -110,6 +111,23 @@ namespace NifOsg osg::Plane mPlaneInParticleSpace; }; + class SphericalCollider : public osgParticle::Operator + { + public: + SphericalCollider(const Nif::NiSphericalCollider* collider); + SphericalCollider(); + SphericalCollider(const SphericalCollider& copy, const osg::CopyOp& copyop); + + META_Object(NifOsg, SphericalCollider) + + virtual void beginOperate(osgParticle::Program* program); + virtual void operate(osgParticle::Particle* particle, double dt); + private: + float mBounceFactor; + osg::BoundingSphere mSphere; + osg::BoundingSphere mSphereInParticleSpace; + }; + class GrowFadeAffector : public osgParticle::Operator { public: