From 167ae600c5a87b1d0fcbea6570992026d29a31a1 Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Mon, 20 Apr 2015 00:37:17 +0200
Subject: [PATCH] Implement gravity decay (previously unknown float)

---
 components/nif/controlled.cpp  |  5 +----
 components/nif/controlled.hpp  |  1 +
 components/nifosg/particle.cpp | 38 +++++++++++++++++++++++++---------
 components/nifosg/particle.hpp |  4 +++-
 4 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp
index bf391d388..5c63094ce 100644
--- a/components/nif/controlled.cpp
+++ b/components/nif/controlled.cpp
@@ -54,10 +54,7 @@ namespace Nif
     {
         Controlled::read(nif);
 
-        float decay = nif->getFloat();
-        if (decay != 0.f)
-            nif->file->warn("Unhandled gravity decay factor");
-
+        mDecay = nif->getFloat();
         mForce = nif->getFloat();
         mType = nif->getUInt();
         mPosition = nif->getVector3();
diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp
index 36d90b03d..4bd7ce1f9 100644
--- a/components/nif/controlled.hpp
+++ b/components/nif/controlled.hpp
@@ -92,6 +92,7 @@ public:
      * 1 - Point (fixed origin)
      */
     int mType;
+    float mDecay;
     osg::Vec3f mPosition;
     osg::Vec3f mDirection;
 
diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp
index 59a4a981b..3cfd91f1f 100644
--- a/components/nifosg/particle.cpp
+++ b/components/nifosg/particle.cpp
@@ -155,13 +155,14 @@ void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* d
 GravityAffector::GravityAffector(const Nif::NiGravity *gravity)
     : mForce(gravity->mForce)
     , mType(static_cast<ForceType>(gravity->mType))
+    , mDecay(gravity->mDecay)
     , mPosition(gravity->mPosition)
     , mDirection(gravity->mDirection)
 {
 }
 
 GravityAffector::GravityAffector()
-    : mForce(0), mType(Type_Wind)
+    : mForce(0), mType(Type_Wind), mDecay(0.f)
 {
 
 }
@@ -175,10 +176,12 @@ GravityAffector::GravityAffector(const GravityAffector &copy, const osg::CopyOp
 void GravityAffector::beginOperate(osgParticle::Program* program)
 {
     bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF);
-    if (mType == Type_Wind)
-        mCachedWorldPositionDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;
-    else // Type_Point
-        mCachedWorldPositionDirection = absolute ? program->transformLocalToWorld(mPosition) : mPosition;
+
+    if (mType == Type_Point || mDecay != 0.f) // we don't need the position for Wind gravity, except if decay is being applied
+        mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition;
+
+    mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection;
+    mCachedWorldDirection.normalize();
 }
 
 void GravityAffector::operate(osgParticle::Particle *particle, double dt)
@@ -187,18 +190,33 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt)
     switch (mType)
     {
         case Type_Wind:
-            particle->addVelocity(mCachedWorldPositionDirection * mForce * dt * magic);
+        {
+            float decayFactor = 1.f;
+            if (mDecay != 0.f)
+            {
+                osg::Plane gravityPlane(mCachedWorldDirection, mCachedWorldPosition);
+                float distance = std::abs(gravityPlane.distance(particle->getPosition()));
+                decayFactor = std::exp(-1.f * mDecay * distance);
+            }
+
+            particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor * magic);
+
             break;
+        }
         case Type_Point:
         {
-            osg::Vec3f diff = mCachedWorldPositionDirection - particle->getPosition();
+            osg::Vec3f diff = mCachedWorldPosition - particle->getPosition();
+
+            float decayFactor = 1.f;
+            if (mDecay != 0.f)
+                decayFactor = std::exp(-1.f * mDecay * diff.length());
+
             diff.normalize();
-            particle->addVelocity(diff * mForce * dt * magic);
+
+            particle->addVelocity(diff * mForce * dt * decayFactor * magic);
             break;
         }
     }
-
-    // velocity *= e^-[(dist/decay)^2]
 }
 
 Emitter::Emitter()
diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp
index 99db3c83a..416477cdd 100644
--- a/components/nifosg/particle.hpp
+++ b/components/nifosg/particle.hpp
@@ -169,7 +169,9 @@ namespace NifOsg
         ForceType mType;
         osg::Vec3f mPosition;
         osg::Vec3f mDirection;
-        osg::Vec3f mCachedWorldPositionDirection;
+        float mDecay;
+        osg::Vec3f mCachedWorldPosition;
+        osg::Vec3f mCachedWorldDirection;
     };
 
     // NodeVisitor to find a child node with the given record index, stored in the node's user data container.