From 6eb5601dd4abd85199a467500a36a9d9c47f06a2 Mon Sep 17 00:00:00 2001
From: Tazpn <tazpn@users.sourceforge.net>
Date: Mon, 11 Sep 2006 02:56:26 +0000
Subject: [PATCH]   0.2.4   ----- o Exporter   - Add animation export.

o Importer
  - Add option to ignore UPB buffers.
    * Useful in preventing LOD Bone warnings from the Civ4 Exporter
  - Fixed problem with Animation Note Tracks not being cleared
  - Fixed issue with BSpline animation when too much data was present
    * Specifically the Civ4 Leaderheads now import animation very well
  - Import Animation Priority into user prop buffer

o NifProps Utility
  - Added Animation Priority
  - Removed unused Globals
---
 MaxNifPlugins_Readme.txt      |  19 +-
 NifCommon/AnimKey.cpp         |  84 +++++-
 NifCommon/AnimKey.h           |  25 +-
 NifCommon/NifPlugins.h        |   4 +
 NifCommon/niutils.h           |   8 +
 NifExport/Animation.cpp       | 509 +++++++++++++++++++++++-----------
 NifExport/Config.cpp          |  28 ++
 NifExport/DllEntry.cpp        |   6 +-
 NifExport/Exporter.cpp        |   2 +
 NifExport/Exporter.h          |   9 +-
 NifExport/KfExport.cpp        | 229 ++++++++++++++-
 NifExport/MtlTex.cpp          |  16 +-
 NifExport/NifExport.cpp       | 111 ++++----
 NifExport/NifExport.rc        |  48 +++-
 NifExport/resource.h          |  10 +-
 NifFurniture/NifFurniture.rc  |   8 +-
 NifImport/ImportAnimation.cpp |  58 +++-
 NifImport/ImportSkeleton.cpp  |   3 +
 NifImport/MaxNifImport.cpp    |  20 +-
 NifImport/MaxNifImport.rc     |  33 +--
 NifImport/NIFImport.cpp       |   2 +
 NifImport/NIFImporter.h       |   1 +
 NifImport/NifDialog.cpp       |   3 +
 NifImport/resource.h          |   1 +
 NifProps/NifProps.cpp         |  49 +++-
 NifProps/NifProps.rc          |  40 ++-
 NifProps/resource.h           |  29 +-
 27 files changed, 1041 insertions(+), 314 deletions(-)

diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index 810cd27..355c3fd 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -1,4 +1,4 @@
-                        MaxPlugins 0.2.3
+                        MaxPlugins 0.2.4
                         ================
 
  
@@ -27,6 +27,23 @@
 
     Change log
     ----------
+      0.2.4
+      -----
+    o Exporter
+      - Add animation export.
+      
+    o Importer
+      - Add option to ignore UPB buffers.  
+        * Useful in preventing LOD Bone warnings from the Civ4 Exporter
+      - Fixed problem with Animation Note Tracks not being cleared
+      - Fixed issue with BSpline animation when too much data was present
+        * Specifically the Civ4 Leaderheads now import animation very well
+      - Import Animation Priority into user prop buffer
+        
+    o NifProps Utility
+      - Added Animation Priority
+      - Removed unused Globals
+    
       0.2.3
       -----
     o Exporter
diff --git a/NifCommon/AnimKey.cpp b/NifCommon/AnimKey.cpp
index f5fcfdc..8caa4ac 100644
--- a/NifCommon/AnimKey.cpp
+++ b/NifCommon/AnimKey.cpp
@@ -131,6 +131,30 @@ FloatKey MapKey<FloatKey, ILinFloatKey>(ILinFloatKey& key, float time)
    return InitFromLinKey(rKey, key, time);
 }
 
+template<>
+Vector3Key MapKey<Vector3Key, ILinPoint3Key>(ILinPoint3Key& key, float time)
+{
+   Vector3Key rKey;
+   rKey.data = TOVECTOR3(key.val);
+   return InitFromLinKey(rKey, key, time);
+}
+
+template<>
+QuatKey MapKey<QuatKey, ILinRotKey>(ILinRotKey& key, float time)
+{
+   QuatKey rKey;
+   rKey.data = TOQUAT(key.val, true);
+   return InitFromLinKey(rKey, key, time);
+}
+
+template<>
+FloatKey MapKey<FloatKey, ILinScaleKey>(ILinScaleKey& key, float time)
+{
+   FloatKey rKey;
+   rKey.data = (key.val[0] + key.val[1] + key.val[2]) / 3.0f;
+   return InitFromLinKey(rKey, key, time);
+}
+
 
 // Specialized Bezier/Hybrid mappings
 
@@ -154,7 +178,6 @@ IBezPoint3Key MapKey<IBezPoint3Key, Vector3Key>(Vector3Key& key, float time)
    return InitBezKey(rKey, key, time);
 }
 
-
 template<>
 IBezQuatKey MapKey<IBezQuatKey, QuatKey>(QuatKey& key, float time)
 {
@@ -172,7 +195,7 @@ IBezScaleKey MapKey<IBezScaleKey, FloatKey>(FloatKey& key, float time)
    return InitBezKey(rKey, key, time);
 }
 
-// Specialized Linear Mappings
+// Specialized Bezier/Hybrid Mappings
 
 template<>
 FloatKey MapKey<FloatKey, IBezFloatKey>(IBezFloatKey& key, float time)
@@ -184,6 +207,37 @@ FloatKey MapKey<FloatKey, IBezFloatKey>(IBezFloatKey& key, float time)
    return InitFromBezKey(rKey, key, time);
 }
 
+template<>
+Vector3Key MapKey<Vector3Key, IBezPoint3Key>(IBezPoint3Key& key, float time)
+{
+   Vector3Key rKey;
+   rKey.data = TOVECTOR3(key.val);
+   rKey.forward_tangent = TOVECTOR3(key.intan);
+   rKey.backward_tangent = TOVECTOR3(key.outtan);
+   return InitFromBezKey(rKey, key, time);
+}
+
+template<>
+QuatKey MapKey<QuatKey, IBezQuatKey>(IBezQuatKey& key, float time)
+{
+   QuatKey rKey;
+   rKey.data = TOQUAT(key.val, true);
+   //rKey.forward_tangent = TOQUAT(key.intan, true);
+   //rKey.backward_tangent = TOQUAT(key.outtan, true);
+   return InitFromBezKey(rKey, key, time);
+}
+
+template<>
+FloatKey MapKey<FloatKey, IBezScaleKey>(IBezScaleKey& key, float time)
+{
+   FloatKey rKey;
+   rKey.data = Average(key.val.s);
+   rKey.forward_tangent = Average(key.intan);
+   rKey.backward_tangent = Average(key.outtan);
+   return InitFromBezKey(rKey, key, time);
+}
+
+
 // Specialized TCB Mappings
 
 template<>
@@ -229,6 +283,32 @@ FloatKey MapKey<FloatKey, ITCBFloatKey>(ITCBFloatKey& key, float time)
    return InitFromTCBKey(rKey, key, time);
 }
 
+template<>
+Vector3Key MapKey<Vector3Key, ITCBPoint3Key>(ITCBPoint3Key& key, float time)
+{
+   Vector3Key rKey;
+   rKey.data = TOVECTOR3(key.val);
+   return InitFromTCBKey(rKey, key, time);
+}
+
+template<>
+QuatKey MapKey<QuatKey, ITCBRotKey>(ITCBRotKey& key, float time)
+{
+   QuatKey rKey;
+   rKey.data = TOQUAT(key.val, true);
+   return InitFromTCBKey(rKey, key, time);
+}
+
+template<>
+FloatKey MapKey<FloatKey, ITCBScaleKey>(ITCBScaleKey& key, float time)
+{
+   FloatKey rKey;
+   rKey.data = Average(key.val.s);
+   return InitFromTCBKey(rKey, key, time);
+}
+
+// Merge Keys
+
 template<> void MergeKey<ILinRotKey>(ILinRotKey& lhs, ILinRotKey& rhs) {
    lhs.val *= rhs.val;
 }
diff --git a/NifCommon/AnimKey.h b/NifCommon/AnimKey.h
index 1cb3462..7766ab9 100644
--- a/NifCommon/AnimKey.h
+++ b/NifCommon/AnimKey.h
@@ -120,17 +120,38 @@ inline void SetKeys(Control *subCtrl, vector<U>& keys, float time)
 }
 
 template<typename T, typename U>
-inline void GetKeys(Control *subCtrl, vector<T>& keys, float time)
+inline int GetKeys(Control *subCtrl, vector<T>& keys, float time)
 {
    if (IKeyControl *ikeys = GetKeyControlInterface(subCtrl)){
-      int n = ikeys->GetKeySize();
+      int n = ikeys->GetNumKeys();
       keys.reserve(n);
       for (int i=0; i<n; ++i){
          AnyKey buf; U *key = reinterpret_cast<U*>((IKey*)buf);
          ikeys->GetKey(i, key);
          keys.push_back( MapKey<T>(*key, time) );
       }
+      return keys.size();
    }
+   return 0;
+}
+
+template<typename T, typename U>
+inline int GetKeys(Control *subCtrl, vector<T>& keys, Interval range)
+{
+   if (IKeyControl *ikeys = GetKeyControlInterface(subCtrl)){
+      float timeOffset = -FrameToTime(range.Start());
+      int n = ikeys->GetNumKeys();
+      keys.reserve(n);
+      for (int i=0; i<n; ++i){
+         AnyKey buf; U *key = reinterpret_cast<U*>((IKey*)buf);
+         ikeys->GetKey(i, key);
+         if (range.InInterval(key->time)) {
+            keys.push_back( MapKey<T>(*key, timeOffset) );
+         }
+      }
+      return keys.size();
+   }
+   return 0;
 }
 
 template<typename T, typename U>
diff --git a/NifCommon/NifPlugins.h b/NifCommon/NifPlugins.h
index ca1239c..0090584 100644
--- a/NifCommon/NifPlugins.h
+++ b/NifCommon/NifPlugins.h
@@ -39,6 +39,8 @@ using Niflib::Vector3;
 
 #define NP_FRN_POS							_T("np_frn_pos")
 
+#define NP_ANM_PRI							_T("np_anm_pri")
+
 /* default values */
 #define NP_DEFAULT_HVK_MATERIAL				9
 #define NP_DEFAULT_HVK_LAYER				1
@@ -50,6 +52,8 @@ using Niflib::Vector3;
 #define NP_DEFAULT_HVK_MOTION_SYSTEM		7
 #define NP_DEFAULT_HVK_QUALITY_TYPE			1
 
+#define NP_DEFAULT_ANM_PRI	0.0f
+
 #define NP_DEFAULT_FRN_MARKER_TYPE			NP_FRN_SLEEP_LEFT
 
 /* other constants */
diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h
index 2af7484..2942059 100644
--- a/NifCommon/niutils.h
+++ b/NifCommon/niutils.h
@@ -351,6 +351,14 @@ static inline Point3 GetScale(const Matrix3& mtx){
    return Point3( fabs(mtx.GetRow(0)[0]), fabs(mtx.GetRow(1)[1]), fabs(mtx.GetRow(2)[2]) );
 }
 
+static inline float Average(const Point3& val) {
+   return (val[0] + val[1] + val[2]) / 3.0f;
+}
+
+static inline float Average(const Niflib::Vector3& val) {
+   return (val.x + val.y + val.z) / 3.0f;
+}
+
 template <typename U, typename T>
 inline Niflib::Ref<U> SelectFirstObjectOfType( vector<Niflib::Ref<T> > const & objs ) {
    for (vector<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) {
diff --git a/NifExport/Animation.cpp b/NifExport/Animation.cpp
index 8b79945..70eaba4 100644
--- a/NifExport/Animation.cpp
+++ b/NifExport/Animation.cpp
@@ -11,6 +11,9 @@ HISTORY:
 *>	Copyright (c) 2006, All Rights Reserved.
 **********************************************************************/
 #include "pch.h"
+#include <IFrameTagManager.h>
+#include <notetrck.h>
+
 #include "NifExport.h"
 #include "AnimKey.h"
 
@@ -39,33 +42,135 @@ struct AnimationExport
 {
    AnimationExport(Exporter& parent) : ne(parent) {}
 
-   bool doExport();
-   bool exportController(INode *node, NiControllerSequenceRef seq);
+   INode * findTrackedNode(INode *root);
+
+   bool doExport(NiControllerSequenceRef seq);
+   bool exportController(INode *node);
    Control *GetTMController(INode* node);
 
    Exporter &ne;
+   Interval range;
+   NiControllerSequenceRef seq;
 };
 
-bool Exporter::doAnimExport()
+Exporter::Result Exporter::doAnimExport(NiControllerSequenceRef root)
 {
    AnimationExport animExporter(*this);
-   return animExporter.doExport();
 
-   return true;
+   return animExporter.doExport(root) ? Exporter::Ok : Exporter::Abort ;
 }
 //NiControllerSequenceRef makeSequence(TimeValue start, TimeValue end);
 
-bool AnimationExport::doExport()
+INode * AnimationExport::findTrackedNode(INode *node)
 {
-   INode *root = ne.mI->GetRootNode();
-   NiControllerSequenceRef seq = new NiControllerSequence();
-   seq->SetStartTime(0.0f);
-   seq->SetStopTime(0.0f);
+   // locate START in note track before assuming all is well
+   if (node->HasNoteTracks()) {
+      for (int i=0, n=node->NumNoteTracks(); i<n; ++i) {
+         if ( NoteTrack *nt = node->GetNoteTrack(i) ) {
+            if ( nt->ClassID() == Class_ID(NOTETRACK_CLASS_ID,0) ) {
+               DefNoteTrack *defNT = (DefNoteTrack *)nt;
+               if ( defNT->NumKeys() > 0 ) {
+                  for (int j=0, m=defNT->keys.Count(); j<m; ++j) {
+                     NoteKey* key = defNT->keys[j];
+                     if (wildmatch("start*", key->note)) {
+                        return node;
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+   for (int i=0; i < node->NumberOfChildren(); ++i ){
+      if ( INode *root = findTrackedNode( node->GetChildNode(i) ) ) {
+         return root;
+      }
+   }
+   return NULL;
+}
+
+bool AnimationExport::doExport(NiControllerSequenceRef seq)
+{
+   INode *node = findTrackedNode(ne.mI->GetRootNode());
+   if (node == NULL)
+      throw runtime_error("No Actor Roots have been selected in the Animation Manager. Cannot continue.");
+
+   this->seq = seq;
+
+   vector<StringKey> textKeys;
+
+   this->range.SetInstant(0);
+
+   seq->SetStartTime(FloatINF);
+   seq->SetStopTime(FloatINF);
    seq->SetFrequency(1.0f);
    seq->SetCycleType( CYCLE_CLAMP );
-   seq->SetTargetName("Bip01");
-   
-   return true;
+   seq->SetTargetName( node->GetName() );
+
+   NiTextKeyExtraDataRef textKeyData = new NiTextKeyExtraData();
+   seq->SetTextKey(textKeyData);
+
+   // Populate Text keys and Sequence information from note tracks
+   for (int i=0, n=node->NumNoteTracks(); i<n; ++i) {
+      if ( NoteTrack *nt = node->GetNoteTrack(i) ) {
+         if ( nt->ClassID() == Class_ID(NOTETRACK_CLASS_ID,0) ) {
+            DefNoteTrack *defNT = (DefNoteTrack *)nt;
+            if ( defNT->NumKeys() > 0 ) {
+               bool stop = false;
+               for (int j=0, m=defNT->keys.Count(); j<m && !stop; ++j) {
+                  NoteKey* key = defNT->keys[j];
+
+                  if (wildmatch("start*", key->note)) {
+                     stringlist args = TokenizeCommandLine(key->note, true);
+                     if (args.empty()) continue;
+
+                     seq->SetStartTime(0.0f);
+                     range.SetStart( key->time );
+                     for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) {
+                        if (strmatch("-name", *itr)) {
+                           if (++itr == args.end()) break;
+                           seq->SetName(*itr);
+                        } else if (strmatch("-loop", *itr)) {
+                           seq->SetCycleType(CYCLE_LOOP);
+                        }
+                     }
+                  } else if ( wildmatch("end*", key->note) ) {
+                     range.SetEnd( key->time );
+                     seq->SetStopTime( FrameToTime( range.Duration()-1 ) );
+                     stop = true;
+                  }
+                  StringKey strkey;
+                  strkey.time = FrameToTime( Interval(range.Start(), key->time).Duration()-1 );
+                  strkey.data = key->note;
+                  textKeys.push_back(strkey);
+               }
+            }
+         }
+      }
+   }
+   textKeyData->SetKeys(textKeys);
+
+   // Now let the fun begin.
+
+   bool ok = exportController(node);
+
+   // Handle NonAccum
+   if (ok)
+   {
+      NiNodeRef ninode = new NiNode();
+      ninode->SetName(FormatString("%s NonAccum", node->GetName()));
+
+      NiTransformControllerRef control = new NiTransformController();
+      NiTransformInterpolatorRef interp = new NiTransformInterpolator();
+      ninode->AddController(StaticCast<NiTimeController>(control));
+      control->SetInterpolator(StaticCast<NiInterpolator>(interp));
+
+      interp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) );
+      interp->SetScale( 1.0f );
+      interp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) );
+      seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), Exporter::mDefaultPriority);
+   }
+   return ok;
 }
 
 
@@ -87,175 +192,255 @@ Control *AnimationExport::GetTMController(INode *n)
 }
 
 
-bool AnimationExport::exportController(INode *node, NiControllerSequenceRef seq)
+bool AnimationExport::exportController(INode *node)
 {
    bool ok = true;
-   int nKeys = node->NumKeys();
-   if (nKeys >= 0)
+   bool skip = false;
+
+   // Primary recursive decent routine
+
+   ObjectState os = node->EvalWorldState(range.Start()); 
+
+   if (!Exporter::mExportCameras && os.obj && os.obj->SuperClassID()==CAMERA_CLASS_ID)
+   {
+      skip = true;
+   }
+   else if (!Exporter::mExportLights && os.obj && os.obj->SuperClassID()==LIGHT_CLASS_ID)
+   {
+      skip = true;
+   }
+
+   if (!skip && Exporter::mExportTransforms)
    {
-      float timeOffset = 0.0f;
-      NiKeyframeDataRef data = new NiKeyframeData();
+      float timeOffset = -FrameToTime(range.Start());
       if (Control *tmCont = GetTMController(node))
       {
-         if (Control *c = tmCont->GetPositionController()) 
+         Interval validity; validity.SetEmpty();
+         Matrix3 tm = node->GetObjTMAfterWSM(range.Start());
+         Matrix3 pm = Inverse(node->GetParentTM(range.Start()));
+         tm *= pm;
+
+         NiNodeRef ninode = new NiNode();
+         ninode->SetName(node->GetName());
+
+         //Vector3 trans(FloatNegINF, FloatNegINF, FloatNegINF);
+         //Quaternion rot(FloatNegINF, FloatNegINF, FloatNegINF, FloatNegINF);
+         Vector3 trans = TOVECTOR3(tm.GetTrans());
+         Quaternion rot = TOQUAT( Quat(tm), true );
+         float scale = FloatNegINF;
+         bool keepData = false;
+
+         NiTransformControllerRef control = new NiTransformController();
+         NiTransformInterpolatorRef interp = new NiTransformInterpolator();
+         ninode->AddController(StaticCast<NiTimeController>(control));
+         control->SetInterpolator(StaticCast<NiInterpolator>(interp));
+
+         //if (validity.InInterval(range))
+         //{
+         //   // Valid for entire interval.  i.e. no changes
+         //   interp->SetTranslation( TOVECTOR3(tm.GetTrans()) );
+         //   interp->SetScale( Average(GetScale(tm)) );
+         //   interp->SetRotation( TOQUAT( Quat(tm) ) );
+         //   seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control));
+         //}
+         //else
          {
-            // separate xyz
-            if (c->ClassID() == IPOS_CONTROL_CLASS_ID) 
-            { 
-               KeyType kType = QUADRATIC_KEY;
-               vector<FloatKey> xkeys, ykeys, zkeys;
-
-               if (Control *x = c->GetXController()){
-                  if (x->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-                     kType = LINEAR_KEY;
-                     GetKeys<FloatKey, ILinFloatKey>(x, xkeys, timeOffset);
-                  } else if (x->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-                     kType = QUADRATIC_KEY;
-                     GetKeys<FloatKey, IBezFloatKey>(x, xkeys, timeOffset);
-                  } else if (x->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-                     kType = TBC_KEY;
-                     GetKeys<FloatKey, ITCBFloatKey>(x, xkeys, timeOffset);
-                  } else {
-                     kType = QUADRATIC_KEY;
-                     GetKeys<FloatKey, IBezFloatKey>(x, xkeys, timeOffset);
+            NiTransformDataRef data = new NiTransformData();
+
+            if (Control *c = tmCont->GetPositionController()) 
+            {
+               int nkeys = 0;
+               // separate xyz
+               if (c->ClassID() == IPOS_CONTROL_CLASS_ID) 
+               { 
+                  KeyType kType = QUADRATIC_KEY;
+                  vector<FloatKey> xkeys, ykeys, zkeys;
+                  if (Control *x = c->GetXController()){
+                     if (x->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                        kType = LINEAR_KEY;
+                        nkeys += GetKeys<FloatKey, ILinFloatKey>(x, xkeys, range);
+                     } else if (x->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                        kType = QUADRATIC_KEY;
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(x, xkeys, range);
+                     } else if (x->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                        kType = TBC_KEY;
+                        nkeys += GetKeys<FloatKey, ITCBFloatKey>(x, xkeys, range);
+                     } else {
+                        kType = QUADRATIC_KEY;
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(x, xkeys, range);
+                     }
                   }
-               }
-               if (Control *y = c->GetYController()){
-                  if (y->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-                     GetKeys<FloatKey, ILinFloatKey>(y, ykeys, timeOffset);
-                  } else if (y->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-                     GetKeys<FloatKey, IBezFloatKey>(y, ykeys, timeOffset);
-                  } else if (y->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-                     GetKeys<FloatKey, ITCBFloatKey>(y, ykeys, timeOffset);
+                  if (Control *y = c->GetYController()){
+                     if (y->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ILinFloatKey>(y, ykeys, range);
+                     } else if (y->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(y, ykeys, range);
+                     } else if (y->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ITCBFloatKey>(y, ykeys, range);
+                     } else {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(y, ykeys, range);
+                     }
+                  }
+                  if (Control *z = c->GetZController()){
+                     if (z->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ILinFloatKey>(z, zkeys, range);
+                     } else if (z->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(z, zkeys, range);
+                     } else if (z->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ITCBFloatKey>(z, zkeys, range);
+                     } else {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(z, zkeys, range);
+                     }
+                  }
+                  vector<Vector3Key> keys;
+                  JoinKeys(keys, xkeys, ykeys, zkeys);
+                  data->SetTranslateType(kType);
+                  data->SetTranslateKeys(keys);
+               } else {
+                  vector<Vector3Key> keys;
+                  if (c->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                     data->SetTranslateType(LINEAR_KEY);
+                     nkeys += GetKeys<Vector3Key, ILinPoint3Key>(c, keys, range);
+                  } else if (c->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                     data->SetTranslateType(QUADRATIC_KEY);
+                     nkeys += GetKeys<Vector3Key, IBezPoint3Key>(c, keys, range);
+                  } else if (c->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                     data->SetTranslateType(TBC_KEY);
+                     nkeys += GetKeys<Vector3Key, ITCBPoint3Key>(c, keys, range);
                   } else {
-                     GetKeys<FloatKey, IBezFloatKey>(y, ykeys, timeOffset);
+                     data->SetTranslateType(QUADRATIC_KEY);
+                     nkeys += GetKeys<Vector3Key, IBezPoint3Key>(c, keys, range);
                   }
+                  data->SetTranslateKeys(keys);
                }
-               if (Control *z = c->GetZController()){
-                  if (z->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-                     GetKeys<FloatKey, ILinFloatKey>(z, zkeys, timeOffset);
-                  } else if (z->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-                     GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-                  } else if (z->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-                     GetKeys<FloatKey, ITCBFloatKey>(z, zkeys, timeOffset);
-                  } else {
-                     GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
+               if (nkeys != 0) { // if no changes set the base transform
+                  keepData = true;
+                  //trans = TOVECTOR3(tm.GetTrans());
+               }
+            }
+
+            // Rotations
+            if (Control *c = tmCont->GetRotationController()) 
+            {
+               int nkeys = 0;
+               if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID,0)) {
+                  vector<QuatKey> keys;
+                  data->SetRotateType(LINEAR_KEY);
+                  nkeys += GetKeys<QuatKey, ILinRotKey>(c, keys, range);
+                  data->SetQuatRotateKeys(keys);
+               } else if (c->ClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0)) {
+                  vector<QuatKey> keys;
+                  data->SetRotateType(QUADRATIC_KEY);
+                  nkeys += GetKeys<QuatKey, IBezQuatKey>(c, keys, range);
+                  data->SetQuatRotateKeys(keys);
+               } else if (c->ClassID() == Class_ID(EULER_CONTROL_CLASS_ID,0)) {
+                  if (Control *x = c->GetXController()){
+                     vector<FloatKey> keys;
+                     if (x->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ILinFloatKey>(x, keys, range);
+                        data->SetXRotateType(LINEAR_KEY);
+                     } else if (x->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(x, keys, range);
+                        data->SetXRotateType(QUADRATIC_KEY);
+                     } else if (x->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ITCBFloatKey>(x, keys, range);
+                        data->SetXRotateType(TBC_KEY);
+                     } else {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(x, keys, range);
+                        data->SetXRotateType(QUADRATIC_KEY);
+                     }
+                     data->SetXRotateKeys(keys);
+                  }
+                  if (Control *y = c->GetXController()) {
+                     vector<FloatKey> keys;
+                     if (y->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ILinFloatKey>(y, keys, range);
+                        data->SetYRotateType(LINEAR_KEY);
+                     } else if (y->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(y, keys, range);
+                        data->SetYRotateType(QUADRATIC_KEY);
+                     } else if (y->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ITCBFloatKey>(y, keys, range);
+                        data->SetYRotateType(TBC_KEY);
+                     } else {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(y, keys, range);
+                        data->SetYRotateType(QUADRATIC_KEY);
+                     }
+                     data->SetYRotateKeys(keys);
+                  }
+                  if (Control *z = c->GetZController()) {
+                     vector<FloatKey> keys;
+                     if (z->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ILinFloatKey>(z, keys, range);
+                        data->SetZRotateType(LINEAR_KEY);
+                     } else if (z->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(z, keys, range);
+                        data->SetZRotateType(QUADRATIC_KEY);
+                     } else if (z->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
+                        nkeys += GetKeys<FloatKey, ITCBFloatKey>(z, keys, range);
+                        data->SetZRotateType(TBC_KEY);
+                     } else {
+                        nkeys += GetKeys<FloatKey, IBezFloatKey>(z, keys, range);
+                        data->SetZRotateType(QUADRATIC_KEY);
+                     }
+                     data->SetZRotateKeys(keys);
                   }
+               } else if (c->ClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID,0)) {
+                  vector<QuatKey> keys;
+                  data->SetRotateType(TBC_KEY);
+                  nkeys += GetKeys<QuatKey, ITCBRotKey>(c, keys, range);
+                  data->SetQuatRotateKeys(keys);
+               }
+               if (nkeys != 0) { // if no changes set the base transform
+                  keepData = true;
+                  //rot = TOQUAT( Quat(tm) );
                }
-               vector<Vector3Key> keys;
-               JoinKeys(keys, xkeys, ykeys, zkeys);
-               data->SetTranslateType(kType);
-               data->SetTranslateKeys(keys);
-
-            } else {
-               //if (c->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-               //   data->SetRotateType(LINEAR_KEY);
-               //   GetKeys<FloatKey, ILinFloatKey>(z, zkeys, timeOffset);
-               //} else if (c->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-               //   data->SetZRotateType(QUADRATIC_KEY);
-               //   GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-               //} else if (c->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-               //   data->SetZRotateType(TBC_KEY);
-               //   GetKeys<FloatKey, ITCBFloatKey>(z, zkeys, timeOffset);
-               //} else {
-               //   data->SetZRotateType(QUADRATIC_KEY);
-               //   GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-               //}
             }
-         }
 
-         //// rotations
-         //if (Control *c = tmCont->GetPositionController()) 
-         //{
-         //   // separate xyz
-         //   if (c->ClassID() == IPOS_CONTROL_CLASS_ID) 
-         //   { 
-         //      if (Control *x = c->GetXController()){
-         //         if (x->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetXRotateType(LINEAR_KEY);
-         //            GetKeys<FloatKey, ILinFloatKey>(x, xkeys, timeOffset);
-         //         } else if (x->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetXRotateType(QUADRATIC_KEY);
-         //            GetKeys<FloatKey, IBezFloatKey>(x, xkeys, timeOffset);
-         //         } else if (x->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetXRotateType(TBC_KEY);
-         //            GetKeys<FloatKey, ITCBFloatKey>(x, xkeys, timeOffset);
-         //         } else {
-         //            data->SetXRotateType(QUADRATIC_KEY);
-         //            GetKeys<FloatKey, IBezFloatKey>(x, xkeys, timeOffset);
-         //         }
-         //         data->SetXRotateKeys(xkeys);
-         //      }
-         //      if (Control *y = c->GetYController()){
-         //         KeyType kType = LINEAR_KEY;
-         //         vector<FloatKey> ykeys;
-         //         if (y->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetYRotateType(LINEAR_KEY);
-         //            GetKeys<FloatKey, ILinFloatKey>(y, ykeys, timeOffset);
-         //         } else if (y->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetYRotateType(QUADRATIC_KEY);
-         //            GetKeys<FloatKey, IBezFloatKey>(y, ykeys, timeOffset);
-         //         } else if (y->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetYRotateType(TBC_KEY);
-         //            GetKeys<FloatKey, ITCBFloatKey>(y, ykeys, timeOffset);
-         //         } else {
-         //            data->SetYRotateType(QUADRATIC_KEY);
-         //            GetKeys<FloatKey, IBezFloatKey>(y, ykeys, timeOffset);
-         //         }
-         //         data->SetYRotateKeys(ykeys);
-         //      }
-         //      if (Control *z = c->GetZController()){
-         //         KeyType kType = LINEAR_KEY;
-         //         vector<FloatKey> zkeys;
-         //         if (z->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetZRotateType(LINEAR_KEY);
-         //            GetKeys<FloatKey, ILinFloatKey>(z, zkeys, timeOffset);
-         //         } else if (z->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetZRotateType(QUADRATIC_KEY);
-         //            GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-         //         } else if (z->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-         //            data->SetZRotateType(TBC_KEY);
-         //            GetKeys<FloatKey, ITCBFloatKey>(z, zkeys, timeOffset);
-         //         } else {
-         //            data->SetZRotateType(QUADRATIC_KEY);
-         //            GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-         //         }
-         //         data->SetZRotateKeys(zkeys);
-         //      }
-         //   } else {
-         //      if (c->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID,0)) {
-         //         data->SetRotateType(LINEAR_KEY);
-         //         GetKeys<FloatKey, ILinFloatKey>(z, zkeys, timeOffset);
-         //      } else if (c->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0)) {
-         //         data->SetZRotateType(QUADRATIC_KEY);
-         //         GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-         //      } else if (c->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0)) {
-         //         data->SetZRotateType(TBC_KEY);
-         //         GetKeys<FloatKey, ITCBFloatKey>(z, zkeys, timeOffset);
-         //      } else {
-         //         data->SetZRotateType(QUADRATIC_KEY);
-         //         GetKeys<FloatKey, IBezFloatKey>(z, zkeys, timeOffset);
-         //      }
-         //   }
-         //}
+            // Scale
+            if (Control *c = tmCont->GetScaleController()) 
+            {
+               int nkeys = 0;
+               if (c->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID,0)) {
+                  vector<FloatKey> keys;
+                  data->SetScaleType(LINEAR_KEY);
+                  nkeys += GetKeys<FloatKey, ILinFloatKey>(c, keys, range);
+                  data->SetScaleKeys(keys);
+               } else if (c->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0)) {
+                  vector<FloatKey> keys;
+                  data->SetScaleType(QUADRATIC_KEY);
+                  nkeys += GetKeys<FloatKey, IBezFloatKey>(c, keys, range);
+                  data->SetScaleKeys(keys);
+               } else if (c->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID,0)) {
+                  vector<FloatKey> keys;
+                  data->SetScaleType(TBC_KEY);
+                  nkeys += GetKeys<FloatKey, ITCBFloatKey>(c, keys, range);
+                  data->SetScaleKeys(keys);
+               }
+               if (nkeys != 0) { // if no changes set the base transform
+                  keepData = true;
+                  //scale = Average(GetScale(tm));
+               }           
+            }
+            // only add transform data object if data actually is present
+            if (keepData) {
+               interp->SetData(data);
+            }
+            interp->SetTranslation(trans);
+            interp->SetScale(scale);
+            interp->SetRotation(rot);
 
+            // Get Priority from node
+            float priority;
+            npGetProp(node, NP_ANM_PRI, priority, Exporter::mDefaultPriority);
+            seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), priority);
+         }
       }
-      //Class_ID c->ClassID()
-      //if (Control *subCtrl = MakePositionXYZ(c, Class_ID(LININTERP_FLOAT_CLASS_ID,0))) {
-      
-      //NiKeyframeDataRef data = new NiKeyframeData();
-      //NiKeyframeDataRef data = CreateBlock("NiKeyframeData");
-      //data->SetRotateType(QUADRATIC_KEY);
-      //data->SetTranslateType(QUADRATIC_KEY);
-      //data->SetScaleType(QUADRATIC_KEY);
-      //data->SetTranslateKeys( interp->SampleTranslateKeys(npoints, 4) );
-      //data->SetQuatRotateKeys( interp->SampleQuatRotateKeys(npoints, 4) );
-      //data->SetScaleKeys( interp->SampleScaleKeys(npoints, 4) );
    }
    for (int i=0, n=node->NumberOfChildren(); ok && i<n; ++i)
    {
       INode *child = node->GetChildNode(i);
-      ok |= exportController(child, seq);
+      ok |= exportController(child);
    }
    return ok;
 }
\ No newline at end of file
diff --git a/NifExport/Config.cpp b/NifExport/Config.cpp
index ea3262b..1ff4721 100755
--- a/NifExport/Config.cpp
+++ b/NifExport/Config.cpp
@@ -5,6 +5,7 @@
 #define REGPATH "Software\\NifTools\\MaxPlugins"
 
 static LPCTSTR NifExportSection = TEXT("MaxNifExport");
+static LPCTSTR KfExportSection = TEXT("KfExport");
 
 void regSet(HKEY hKey, const char *value, float f);
 void regSet(HKEY hKey, const char *value, bool b);
@@ -127,6 +128,33 @@ void Exporter::readConfig(Interface *i)
 }
 
 
+void Exporter::readKfConfig(Interface *i)
+{
+   TCHAR iniName[MAX_PATH];
+   LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
+   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+
+   mExportHidden = GetIniValue(KfExportSection, "IncludeHidden", false, iniName);
+   mExportLights = GetIniValue(KfExportSection, "Lights", false, iniName);
+   mExportCameras = GetIniValue(KfExportSection, "Cameras", false, iniName);
+   mExportTransforms = GetIniValue(KfExportSection, "Transforms", true, iniName);
+   mDefaultPriority = GetIniValue<float>(KfExportSection, "Priority", 0.0f, iniName);
+}
+
+void Exporter::writeKfConfig(Interface *i)
+{
+   TCHAR iniName[MAX_PATH];
+   LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
+   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+
+   SetIniValue(KfExportSection, "IncludeHidden", mExportHidden, iniName);
+   SetIniValue(KfExportSection, "Lights", mExportLights, iniName);
+   SetIniValue(KfExportSection, "Cameras", mExportCameras, iniName);
+   SetIniValue(KfExportSection, "Transforms", mExportTransforms, iniName);
+   SetIniValue<float>(KfExportSection, "Priority", mDefaultPriority, iniName);
+}
+
+
 void regSet(HKEY hKey, const char *value, float f)
 {
 	DWORD dw = *((DWORD*)&f);
diff --git a/NifExport/DllEntry.cpp b/NifExport/DllEntry.cpp
index 0e7e3aa..8907fa3 100755
--- a/NifExport/DllEntry.cpp
+++ b/NifExport/DllEntry.cpp
@@ -60,11 +60,7 @@ __declspec( dllexport ) const TCHAR* LibDescription()
 //TODO: Must change this number when adding a new class
 __declspec( dllexport ) int LibNumberClasses()
 {
-#if _DEBUG
-	return 2;
-#else
-   return 1;
-#endif
+   return 2;
 }
 
 // This function returns the number of plug-in classes this DLL
diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp
index a13f1df..8bdf74b 100755
--- a/NifExport/Exporter.cpp
+++ b/NifExport/Exporter.cpp
@@ -29,6 +29,8 @@ int Exporter::mNifUserVersion = 0;
 bool Exporter::mSkeletonOnly=false;
 bool Exporter::mExportCameras=false;
 bool Exporter::mGenerateBoneCollision=false;
+bool Exporter::mExportTransforms=true;
+float Exporter::mDefaultPriority=0.0f;
 
 Exporter::Exporter(Interface *i, AppSettings *appSettings)
    : mI(i), mAppSettings(appSettings)
diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h
index 44dd7b7..8d5b2f0 100755
--- a/NifExport/Exporter.h
+++ b/NifExport/Exporter.h
@@ -57,6 +57,9 @@ public:
    static bool				mExportCameras;
    static bool          mGenerateBoneCollision;
 
+   static bool          mExportTransforms;
+   static float         mDefaultPriority;
+
 	Exporter(Interface *i, AppSettings *appSettings);
 
 	Result					doExport(NiNodeRef &root, INode *node);
@@ -70,11 +73,15 @@ public:
 	static void				writeConfig(INode *node);
 	// writes config to registry
 	static void				writeConfig(Interface *i);
+   // writes config to registry
+   static void				writeKfConfig(Interface *i);
 
 	// reads config from root node
 	static void				readConfig(INode *node);
 	// reads config from registry
 	static void				readConfig(Interface *i);
+   // reads config from registry
+   static void				readKfConfig(Interface *i);
 
 public:
 	typedef vector<unsigned short> TriStrip;
@@ -176,7 +183,7 @@ public:
    bool exportSkin();
 
    /* animation export */
-   bool doAnimExport();
+   Result doAnimExport(Ref<NiControllerSequence> root);
 
    /* misc export */
    bool exportUPB(NiNodeRef &root, INode *node);
diff --git a/NifExport/KfExport.cpp b/NifExport/KfExport.cpp
index c865cc3..4102b0c 100644
--- a/NifExport/KfExport.cpp
+++ b/NifExport/KfExport.cpp
@@ -2,12 +2,12 @@
 #include "AppSettings.h"
 #include "niutils.h"
 #include  <io.h>
-
+#include "obj/NiControllerSequence.h"
 using namespace Niflib;
 
 #define KFEXPORT_CLASS_ID	Class_ID(0xa57ff0a4, 0xa0374ffc)
 
-static LPCTSTR KfExportSection = TEXT("MaxKfExport");
+static LPCTSTR KfExportSection = TEXT("KfExport");
 
 class KfExport : public SceneExport 
 {
@@ -43,12 +43,12 @@ class KfExportClassDesc : public ClassDesc2
 public:
    int 			IsPublic() { return TRUE; }
    void			*Create(BOOL loading = FALSE) { return new KfExport(); }
-   const TCHAR		*ClassName() { return GetString(IDS_CLASS_NAME); }
+   const TCHAR		*ClassName() { return GetString(IDS_KF_CLASS_NAME); }
    SClass_ID		SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
    Class_ID		ClassID() { return KFEXPORT_CLASS_ID; }
    const TCHAR		*Category() { return GetString(IDS_CATEGORY); }
 
-   const TCHAR		*InternalName() { return _T("KfExport"); }	// returns fixed parsable name (scripter-visible name)
+   const TCHAR		*InternalName() { return _T("_KfExport"); }	// returns fixed parsable name (scripter-visible name)
    HINSTANCE		HInstance() { return hInstance; }					// returns owning module handle
 
 
@@ -58,6 +58,128 @@ static KfExportClassDesc KfExportDesc;
 ClassDesc2* GetKfExportDesc() { return &KfExportDesc; }
 
 
+static BOOL CALLBACK KfExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
+   static KfExport *imp = NULL;
+
+   switch (message) 
+   {
+   case WM_INITDIALOG:
+      {
+         // Append file version to dialog
+         TSTR fileVersion = GetFileVersion(NULL);
+         if (!fileVersion.isNull()) {
+            char buffer[256];
+            GetWindowText(hWnd, buffer, _countof(buffer));
+            _tcscat(buffer, TEXT(" "));
+            _tcscat(buffer, fileVersion);
+            SetWindowText(hWnd, buffer);
+         }
+
+         imp = (KfExport *)lParam;
+         CenterWindow(hWnd,GetParent(hWnd));
+
+         CheckDlgButton(hWnd, IDC_CHK_HIDDEN, Exporter::mExportHidden);
+         CheckDlgButton(hWnd, IDC_CHK_LIGHTS, Exporter::mExportLights);
+         CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras);
+         CheckDlgButton(hWnd, IDC_CHK_TRANSFORMS, Exporter::mExportTransforms);
+         SetDlgItemText(hWnd, IDC_ED_PRIORITY, FormatText("%.4f", Exporter::mDefaultPriority));
+
+         string selection = Exporter::mGameName;
+         string version = Exporter::mNifVersion;
+         string userVer = FormatString("%d", Exporter::mNifUserVersion);
+         for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr)
+            SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_ADDSTRING, 0, LPARAM(itr->Name.c_str()));
+         SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_SELECTSTRING, WPARAM(-1), LPARAM(selection.c_str()));
+         SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str()));
+         SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str()));
+
+         imp->mDlgResult = IDCANCEL;
+      }
+      return TRUE;
+
+   case WM_CLOSE:
+      EndDialog(hWnd, imp->mDlgResult);
+      return TRUE;
+
+   case WM_COMMAND:
+      if (HIWORD(wParam) == BN_CLICKED)
+      {
+         char tmp[MAX_PATH], *end;
+         bool close = false;
+         switch (LOWORD(wParam))
+         {
+         case IDOK:
+            // Validity Check
+            GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
+            if (tmp[0] != 0)
+            {
+               int nifVersion = GetVersion(tmp);
+               if (!IsVersionSupported(nifVersion))
+               {
+                  MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP);
+                  return FALSE;
+               }
+            }
+
+            Exporter::mExportHidden = IsDlgButtonChecked(hWnd, IDC_CHK_HIDDEN);
+            Exporter::mExportLights = IsDlgButtonChecked(hWnd, IDC_CHK_LIGHTS);
+            Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA);
+            Exporter::mExportTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_TRANSFORMS);
+
+            GetDlgItemText(hWnd, IDC_ED_PRIORITY, tmp, MAX_PATH);
+            Exporter::mDefaultPriority = atof(tmp);
+
+            GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
+            if (AppSettings *appSettings = FindAppSetting(tmp))
+            {
+               Exporter::mGameName = appSettings->Name;
+               GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
+               Exporter::mNifVersion = tmp;
+               GetDlgItemText(hWnd, IDC_CB_USER_VERSION, tmp, MAX_PATH);
+               Exporter::mNifUserVersion = strtol(tmp, &end, 0);
+            }
+
+            EndDialog(hWnd, imp->mDlgResult=IDOK);
+            close = true;
+            break;
+
+         case IDCANCEL:
+            EndDialog(hWnd, imp->mDlgResult=IDCANCEL);
+            close = true;
+            break;
+         }
+
+         if (close)
+            SendMessage(hWnd, WM_CLOSE, 0, 0);
+      }
+      else if (HIWORD(wParam) == STN_CLICKED)
+      {
+         if (LOWORD(wParam) == IDC_LBL_LINK)
+         {
+            ShellExecute(hWnd, "open", "http://www.niftools.org",
+               NULL, NULL, SW_SHOWDEFAULT);
+         }
+      }
+      else if (HIWORD(wParam) == CBN_SELCHANGE)
+      {
+         if (LOWORD(wParam) == IDC_CB_GAME)
+         {
+            char tmp[MAX_PATH];
+            GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
+            if (AppSettings *appSettings = FindAppSetting(tmp))
+            {
+               string version = appSettings->NiVersion;
+               string userVer = FormatString("%d", appSettings->NiUserVersion);
+               SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str()));
+               SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str()));
+            }
+         }
+      }
+      break;
+   }
+   return FALSE;
+}
+
 //--- KfExport -------------------------------------------------------
 KfExport::KfExport()
 {
@@ -141,8 +263,103 @@ BOOL KfExport::SupportsOptions(int ext, DWORD options)
 
 int	KfExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
 {
-   // read application settings
-   AppSettings::Initialize(i);
+   try
+   {
+      // read application settings
+      AppSettings::Initialize(i);
+
+      TCHAR iniName[MAX_PATH];
+      LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
+      PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+      bool iniNameIsValid = (-1 != _taccess(iniName, 0));
+
+      // read config from registry
+      Exporter::readKfConfig(i);
+
+      AppSettings *appSettings = NULL;
+      if (iniNameIsValid)
+      {
+         string fname = name;
+         // Locate which application to use. If Auto, find first app where this file appears in the root path list
+         string curapp = GetIniValue<string>(KfExportSection, "CurrentApp", "AUTO", iniName);
+         if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
+            // Scan Root paths
+            for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
+               if ((*itr).IsFileInRootPaths(fname)) {
+                  appSettings = &(*itr);
+                  break;
+               }
+            }
+         } else {
+            appSettings = FindAppSetting(curapp);
+         }
+      }
+      if (appSettings == NULL && !TheAppSettings.empty())
+         appSettings = &TheAppSettings.front();
+
+      Exporter::mGameName = appSettings->Name;
+      Exporter::mNifVersion = appSettings->NiVersion;
+      Exporter::mNifUserVersion = appSettings->NiUserVersion;
+
+      if(!suppressPrompts)
+      {
+         if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_KF_PANEL), GetActiveWindow(), KfExportOptionsDlgProc, (LPARAM)this) != IDOK)
+            return true;
+
+         // write config to registry
+         Exporter::writeKfConfig(i);
+
+         // write config to root node
+         Exporter::writeConfig(i->GetRootNode());
+
+         // Update the current app version
+         appSettings = FindAppSetting(Exporter::mGameName);
+         if (appSettings == NULL && !TheAppSettings.empty())
+            appSettings = &TheAppSettings.front();
+         appSettings->NiVersion = Exporter::mNifVersion;
+         appSettings->NiUserVersion = Exporter::mNifUserVersion;
+         appSettings->WriteSettings(i);
+      }
+
+      int nifVersion = VER_20_0_0_5;
+      int nifUserVer = Exporter::mNifUserVersion;
+
+      if (!Exporter::mNifVersion.empty())
+      {
+         nifVersion = GetVersion(Exporter::mNifVersion);
+         if (!IsVersionSupported(nifVersion))
+            throw exception(FormatString("Version '%s' is not a supported version.").c_str());
+      }
+
+      Exporter::mSelectedOnly = (options&SCENE_EXPORT_SELECTED) != 0;
+      Exporter exp(i, appSettings);
+
+      Ref<NiControllerSequence> root = new NiControllerSequence();
+
+      TCHAR fname[MAX_PATH];
+      _tcscpy(fname, PathFindFileName(name));
+      PathRemoveExtension(fname);
+      root->SetName(fname);
+
+      Exporter::Result result = exp.doAnimExport(root);
+
+      if (result!=Exporter::Ok)
+         throw exception("Unknown error.");
+
+      WriteNifTree(name, StaticCast<NiObject>(root), nifVersion, nifUserVer);
+   }
+
+   catch (exception &e)
+   {
+      MessageBox(NULL, e.what(), "Export Error", MB_OK);
+      return true;
+   }
+
+   catch (...)
+   {
+      MessageBox(NULL, "Unknown error.", "Export Error", MB_OK);
+      return true;
+   }
 
    return TRUE;
 }
\ No newline at end of file
diff --git a/NifExport/MtlTex.cpp b/NifExport/MtlTex.cpp
index 75b4089..9465d63 100755
--- a/NifExport/MtlTex.cpp
+++ b/NifExport/MtlTex.cpp
@@ -136,16 +136,16 @@ void Exporter::makeMaterial(NiAVObjectRef &parent, Mtl *mtl)
          if (smtl->SupportsShaders()) {
             if (Shader *s = smtl->GetShader()) {
                if (smtl->GetWire()){
-                  NiWireframePropertyRef wireProp = CreateNiObject<NiWireframeProperty>();
+                  NiWireframePropertyRef wireProp = new NiWireframeProperty();
                   wireProp->SetFlags(1);
                   parent->AddProperty(wireProp);
                }
                if (smtl->GetTwoSided()){
-                  NiStencilPropertyRef stencil = CreateNiObject<NiStencilProperty>();
-                  stencil->SetStencilFunction(4);
+                  NiStencilPropertyRef stencil = new NiStencilProperty();
+                  stencil->SetStencilFunction(TEST_GREATER);
                   stencil->SetStencilEnabled(false);
-                  stencil->SetPassAction(3);
-                  stencil->SetDrawMode(3);
+                  stencil->SetPassAction(ACTION_INCREMENT);
+                  stencil->SetDrawMode(DRAW_BOTH);
                   parent->AddProperty(stencil);
                }
                if (smtl->IsFaceted()) {
@@ -343,10 +343,10 @@ bool Exporter::exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl)
                }
                if (smtl->GetTwoSided()){
                   NiStencilPropertyRef stencil = CreateNiObject<NiStencilProperty>();
-                  stencil->SetStencilFunction(4);
+                  stencil->SetStencilFunction(TEST_GREATER);
                   stencil->SetStencilEnabled(false);
-                  stencil->SetPassAction(3);
-                  stencil->SetDrawMode(3);
+                  stencil->SetPassAction(ACTION_INCREMENT);
+                  stencil->SetDrawMode(DRAW_BOTH);
                   parent->AddProperty(stencil);
                }
                if (smtl->IsFaceted()) {
diff --git a/NifExport/NifExport.cpp b/NifExport/NifExport.cpp
index c434a04..4a7be3b 100755
--- a/NifExport/NifExport.cpp
+++ b/NifExport/NifExport.cpp
@@ -7,7 +7,7 @@ using namespace Niflib;
 
 #define NifExport_CLASS_ID	Class_ID(0xa57ff0a4, 0xa0374ffb)
 
-static LPCTSTR NifExportSection = TEXT("MaxNifExport");
+LPCTSTR NifExportSection = TEXT("MaxNifExport");
 
 class NifExport : public SceneExport 
 {
@@ -41,7 +41,7 @@ class NifExportClassDesc : public ClassDesc2
 	public:
 	int 			IsPublic() { return TRUE; }
 	void			*Create(BOOL loading = FALSE) { return new NifExport(); }
-	const TCHAR		*ClassName() { return GetString(IDS_CLASS_NAME); }
+	const TCHAR		*ClassName() { return GetString(IDS_NIF_CLASS_NAME); }
 	SClass_ID		SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
 	Class_ID		ClassID() { return NifExport_CLASS_ID; }
 	const TCHAR		*Category() { return GetString(IDS_CATEGORY); }
@@ -283,68 +283,69 @@ BOOL NifExport::SupportsOptions(int ext, DWORD options)
 
 int	NifExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
 {
-   // read application settings
-   AppSettings::Initialize(i);
-
-   TCHAR iniName[MAX_PATH];
-   LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
-   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
-   bool iniNameIsValid = (-1 != _taccess(iniName, 0));
-
-   // Set whether Config should use registry or not
-   Exporter::mUseRegistry = !iniNameIsValid || GetIniValue<bool>(NifExportSection, "UseRegistry", false, iniName);
-	// read config from registry
-	Exporter::readConfig(i);
-	// read config from root node
-	Exporter::readConfig(i->GetRootNode());
-
-   // locate the "default" app setting
-   AppSettings *appSettings = NULL;
-   if (iniNameIsValid)
-   {
-      string fname = name;
-      // Locate which application to use. If Auto, find first app where this file appears in the root path list
-      string curapp = GetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName);
-      if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
-         // Scan Root paths
-         for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
-            if ((*itr).IsFileInRootPaths(fname)) {
-               appSettings = &(*itr);
-               break;
+	try
+	{
+      // read application settings
+      AppSettings::Initialize(i);
+
+      TCHAR iniName[MAX_PATH];
+      LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
+      PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+      bool iniNameIsValid = (-1 != _taccess(iniName, 0));
+
+      // Set whether Config should use registry or not
+      Exporter::mUseRegistry = !iniNameIsValid || GetIniValue<bool>(NifExportSection, "UseRegistry", false, iniName);
+      // read config from registry
+      Exporter::readConfig(i);
+      // read config from root node
+      Exporter::readConfig(i->GetRootNode());
+
+      // locate the "default" app setting
+      AppSettings *appSettings = NULL;
+      if (iniNameIsValid)
+      {
+         string fname = name;
+         // Locate which application to use. If Auto, find first app where this file appears in the root path list
+         string curapp = GetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName);
+         if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
+            // Scan Root paths
+            for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
+               if ((*itr).IsFileInRootPaths(fname)) {
+                  appSettings = &(*itr);
+                  break;
+               }
             }
+         } else {
+            appSettings = FindAppSetting(curapp);
          }
-      } else {
-         appSettings = FindAppSetting(curapp);
       }
-   }
-   if (appSettings == NULL && !TheAppSettings.empty())
-      appSettings = &TheAppSettings.front();
+      if (appSettings == NULL && !TheAppSettings.empty())
+         appSettings = &TheAppSettings.front();
 
-	if(!suppressPrompts)
-	{
       Exporter::mGameName = appSettings->Name;
       Exporter::mNifVersion = appSettings->NiVersion;
       Exporter::mNifUserVersion = appSettings->NiUserVersion;
 
-		if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PANEL), GetActiveWindow(), NifExportOptionsDlgProc, (LPARAM)this) != IDOK)
-			return true;
-
-		// write config to registry
-		Exporter::writeConfig(i);
-		// write config to root node
-		Exporter::writeConfig(i->GetRootNode());
-
-      // Update the current app version
-      appSettings = FindAppSetting(Exporter::mGameName);
-      if (appSettings == NULL && !TheAppSettings.empty())
-         appSettings = &TheAppSettings.front();
-      appSettings->NiVersion = Exporter::mNifVersion;
-      appSettings->NiUserVersion = Exporter::mNifUserVersion;
-      appSettings->WriteSettings(i);
-	}
+      if(!suppressPrompts)
+      {
+         if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_NIF_PANEL), GetActiveWindow(), NifExportOptionsDlgProc, (LPARAM)this) != IDOK)
+            return true;
+
+         // write config to registry
+         Exporter::writeConfig(i);
+         // write config to root node
+         Exporter::writeConfig(i->GetRootNode());
+
+         // Update the current app version
+         appSettings = FindAppSetting(Exporter::mGameName);
+         if (appSettings == NULL && !TheAppSettings.empty())
+            appSettings = &TheAppSettings.front();
+         appSettings->NiVersion = Exporter::mNifVersion;
+         appSettings->NiUserVersion = Exporter::mNifUserVersion;
+         appSettings->WriteSettings(i);
+      }
+
 
-	try
-	{
       int nifVersion = VER_20_0_0_5;
       int nifUserVer = Exporter::mNifUserVersion;
 
diff --git a/NifExport/NifExport.rc b/NifExport/NifExport.rc
index 5c6fc3a..e9f8667 100755
--- a/NifExport/NifExport.rc
+++ b/NifExport/NifExport.rc
@@ -73,7 +73,32 @@ END
 // Dialog
 //
 
-IDD_PANEL DIALOGEX 0, 0, 205, 203
+IDD_KF_PANEL DIALOGEX 0, 0, 205, 130
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Export KF"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    GROUPBOX        "Export:",IDC_STATIC,7,7,81,66
+    CONTROL         "&Hidden Nodes",IDC_CHK_HIDDEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,17,67,10
+    CONTROL         "Transforms",IDC_CHK_TRANSFORMS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,31,67,10
+    CONTROL         "&Lights",IDC_CHK_LIGHTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,44,67,10
+    CONTROL         "Cameras",IDC_CHK_CAMERA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,58,67,10
+    GROUPBOX        "Behaviors:",IDC_STATIC,94,7,104,66
+    LTEXT           "Priority:",IDC_LBL_PRIORITY,104,24,37,8
+    EDITTEXT        IDC_ED_PRIORITY,145,22,39,12,ES_AUTOHSCROLL
+    LTEXT           "Game",IDC_STATIC,7,78,66,8
+    COMBOBOX        IDC_CB_GAME,7,89,105,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Version",IDC_STATIC,117,78,39,8
+    EDITTEXT        IDC_CB_VERSION,117,89,45,12,ES_AUTOHSCROLL
+    LTEXT           "User",IDC_STATIC,167,78,25,8
+    EDITTEXT        IDC_CB_USER_VERSION,167,89,30,12,ES_AUTOHSCROLL
+    DEFPUSHBUTTON   "&Export",IDOK,5,109,34,14
+    PUSHBUTTON      "&Cancel",IDCANCEL,45,109,33,14
+    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,103,109,95,14,SS_NOTIFY | SS_CENTERIMAGE
+END
+
+IDD_NIF_PANEL DIALOGEX 0, 0, 205, 203
 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 EXSTYLE WS_EX_TOOLWINDOW
 CAPTION "Export Nif"
@@ -122,7 +147,15 @@ END
 #ifdef APSTUDIO_INVOKED
 GUIDELINES DESIGNINFO 
 BEGIN
-    IDD_PANEL, DIALOG
+    IDD_KF_PANEL, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 198
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 123
+    END
+
+    IDD_NIF_PANEL, DIALOG
     BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 198
@@ -139,8 +172,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,2,3,0
- PRODUCTVERSION 0,2,3,0
+ FILEVERSION 0,2,4,0
+ PRODUCTVERSION 0,2,4,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -156,12 +189,12 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Exporter"
-            VALUE "FileVersion", "0, 2, 3, 0"
+            VALUE "FileVersion", "0, 2, 4, 0"
             VALUE "InternalName", "NifExport.dle"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "NifExport.dle"
             VALUE "ProductName", "3ds Max Nif Exporter"
-            VALUE "ProductVersion", "0, 2, 3, 0"
+            VALUE "ProductVersion", "0, 2, 4, 0"
         END
     END
     BLOCK "VarFileInfo"
@@ -178,9 +211,10 @@ END
 
 STRINGTABLE 
 BEGIN
-    IDS_CLASS_NAME          "Nif Exporter"
+    IDS_NIF_CLASS_NAME      "Niftools NIF Exporter"
     IDS_PARAMS              "Parameters"
     IDS_SPIN                "Spin"
+    IDS_KF_CLASS_NAME       "Niftools KF Exporter"
 END
 
 #endif    // English (U.S.) resources
diff --git a/NifExport/resource.h b/NifExport/resource.h
index 2aeda38..3d67a7f 100755
--- a/NifExport/resource.h
+++ b/NifExport/resource.h
@@ -5,9 +5,13 @@
 #define IDS_LIBDESCRIPTION              1
 #define IDS_CATEGORY                    2
 #define IDS_CLASS_NAME                  3
+#define IDS_NIF_CLASS_NAME              3
 #define IDS_PARAMS                      4
 #define IDS_SPIN                        5
+#define IDS_KF_CLASS_NAME               6
 #define IDD_PANEL                       101
+#define IDD_NIF_PANEL                   101
+#define IDD_KF_PANEL                    102
 #define IDC_CLOSEBUTTON                 1000
 #define IDC_DOSTUFF                     1000
 #define IDC_CHK_HIDDEN                  1003
@@ -18,8 +22,10 @@
 #define IDC_ED_WELDTHRESH               1008
 #define IDC_LBL_WELDTHRESH              1009
 #define IDC_CHK_COLL                    1010
+#define IDC_ED_PRIORITY                 1010
 #define IDC_LBL_LINK                    1011
 #define IDC_CHK_VCOLORS                 1012
+#define IDC_LBL_PRIORITY                1012
 #define IDC_CHK_REMAP                   1013
 #define IDC_CB_GAME                     1014
 #define IDC_CB_VERSION                  1015
@@ -36,6 +42,8 @@
 #define IDC_CHK_CAMERA                  1025
 #define IDC_CHK_SKEL_ONLY2              1026
 #define IDC_CHK_BONE_COLL               1026
+#define IDC_CHK_TRANSFORMS              1026
+#define IDC_EDIT1                       1027
 #define IDC_COLOR                       1456
 #define IDC_EDIT                        1490
 #define IDC_SPIN                        1496
@@ -46,7 +54,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        102
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1025
+#define _APS_NEXT_CONTROL_VALUE         1028
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
diff --git a/NifFurniture/NifFurniture.rc b/NifFurniture/NifFurniture.rc
index ce1e6e6..3c75f27 100755
--- a/NifFurniture/NifFurniture.rc
+++ b/NifFurniture/NifFurniture.rc
@@ -107,8 +107,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,2,3,0
- PRODUCTVERSION 0,2,3,0
+ FILEVERSION 0,2,4,0
+ PRODUCTVERSION 0,2,4,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -124,12 +124,12 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Furniture Plugin"
-            VALUE "FileVersion", "0, 2, 3, 0"
+            VALUE "FileVersion", "0, 2, 4, 0"
             VALUE "InternalName", "NifFurniture.dlu"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "NifFurniture.dlu"
             VALUE "ProductName", "3ds Max Nif Furniture Plugin"
-            VALUE "ProductVersion", "0, 2, 3, 0"
+            VALUE "ProductVersion", "0, 2, 4, 0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
index 72ee9f4..5849460 100644
--- a/NifImport/ImportAnimation.cpp
+++ b/NifImport/ImportAnimation.cpp
@@ -18,6 +18,7 @@ HISTORY:
 #include "KFMImporter.h"
 #include "KFImporter.h"
 #include "AnimKey.h"
+#include "NifPlugins.h"
 #include <obj/NiInterpolator.h>
 #include <obj/NiTransformInterpolator.h>
 #include <obj/NiTransformData.h>
@@ -136,11 +137,20 @@ void NifImporter::ClearAnimation(INode *node)
 {
    if (node != NULL)
    {
+      if (node->HasNoteTracks()){
+         for (int i = node->NumNoteTracks()-1; i>=0; --i ){
+            if (NoteTrack *nt = node->GetNoteTrack(i))
+               node->DeleteNoteTrack(nt, TRUE);
+         }
+      }
       node->DeleteKeys(TRACK_DOALL);
       ::ClearAnimation(node->GetTMController());
       for (int i=0, n=node->NumberOfChildren(); i<n; ++i){
          ClearAnimation(node->GetChildNode(i));
       }
+
+      // Clear animation priority
+      node->SetUserPropFloat(NP_ANM_PRI, 0.0f);
    }
 }
 void NifImporter::ClearAnimation()
@@ -304,7 +314,48 @@ bool KFMImporter::ImportAnimation()
          if (addNoteTracks) {
             string target = cntr->GetTargetName();
             if ( INode *n = gi->GetINodeByName(target.c_str()) ) {
+#if 1
+               DefNoteTrack* nt = (DefNoteTrack*)NewDefaultNoteTrack();
+               n->AddNoteTrack(nt);
+
+               for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) {
+                  TimeValue t = TimeToFrame(time + (*itr).time);
+
+                  if (wildmatch("start*", (*itr).data)){
+                     stringlist args = TokenizeCommandLine((*itr).data.c_str(), true);
+                     if (args.empty()) continue;
+                     bool hasName = false;
+                     bool hasLoop = false;
+                     CycleType ct = cntr->GetCycleType();
+                     for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) {
+                        if (strmatch("-name", *itr)) {
+                           if (++itr == args.end()) break;                       
+                           hasName = true;
+                        } else if (strmatch("-loop", *itr)) {
+                           hasLoop = true;
+                        }
+                     }
+                     if (!hasName) {
+                        string name = cntr->GetName();
+                        if (name.empty())
+                           name = FormatString("EMPTY_SEQUENCE_AT_%df", int(t * FramesPerSecond / TicksPerFrame) );
+                        args.push_back("-name");
+                        args.push_back(name);
+                     }
+                     if (!hasLoop && ct == CYCLE_LOOP) {
+                        args.push_back("-loop");
+                     }
 
+                     string line = JoinCommandLine(args);
+                     NoteKey *key = new NoteKey(t, line.c_str(), 0);
+                     nt->keys.Append(1, &key);
+                  } else {
+                     NoteKey *key = new NoteKey(t, (*itr).data.c_str(), 0);
+                     nt->keys.Append(1, &key);
+                  }
+               }
+
+#else
                TSTR script;
                script += 
                "fn getActorManager obj = (\n"
@@ -326,7 +377,7 @@ bool KFMImporter::ImportAnimation()
                script += FormatText("nt = getActorManager $'%s'\n", target.c_str());
                
                for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) {
-                  TimeValue t = TimeToFrame(time + (*itr).time) + 1;
+                  TimeValue t = TimeToFrame(time + (*itr).time);
 
                   if (wildmatch("start*", (*itr).data)){
                      stringlist args = TokenizeCommandLine((*itr).data.c_str(), true);
@@ -363,6 +414,7 @@ bool KFMImporter::ImportAnimation()
                   //nt->keys.Append(1, &key);
                }
                ExecuteMAXScriptScript(script, TRUE, NULL);
+#endif
             }
          }
 
@@ -396,6 +448,10 @@ bool KFMImporter::ImportAnimation()
 
          INode *n = gi->GetINodeByName(name.c_str());
 
+         if ((*lnk).priority_ != 0.0f) {
+            npSetProp(n, NP_ANM_PRI, (*lnk).priority_);
+         }
+
          NiKeyframeDataRef data;
          Point3 p; Quat q; float s;
          if (ai.GetTransformData(*lnk, name, data, p, q, s)) {
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
index 0649cb4..1d8cfc5 100644
--- a/NifImport/ImportSkeleton.cpp
+++ b/NifImport/ImportSkeleton.cpp
@@ -686,6 +686,9 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse)
 
 bool NifImporter::ImportUPB(INode *node, Niflib::NiNodeRef block)
 {
+   if (!importUPB)
+      return false;
+
    bool ok = false;
    if (node && block)
    {
diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp
index dcb5407..acd3970 100644
--- a/NifImport/MaxNifImport.cpp
+++ b/NifImport/MaxNifImport.cpp
@@ -203,20 +203,16 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi,
             return FALSE;
          ok = importer.DoImport();
       }
+   }
+   catch (exception &e)
+   {
+      MessageBox(NULL, e.what(), "Import Error", MB_OK);
+      return TRUE;
    }
-   catch( exception & e ) 
+   catch (...)
    {
-      e=e;
-      ok = false;
-   }
-   catch( exception * e ) 
-   {
-      e=e;
-      ok = false;
-   }
-   catch( ... ) 
-   {
-      ok = false;
+      MessageBox(NULL, "Unknown error.", "Import Error", MB_OK);
+      return TRUE;
    }
    return ok ? TRUE : FALSE;
 }
diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc
index 158f7c5..6ca1950 100644
--- a/NifImport/MaxNifImport.rc
+++ b/NifImport/MaxNifImport.rc
@@ -36,27 +36,28 @@ BEGIN
     CONTROL         "&Skeleton",IDC_CHK_BONES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,16,67,10
     CONTROL         "S&kin Modifier",IDC_CHK_SKIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,27,67,10
     CONTROL         "Vertex &Colors",IDC_CHK_VCOLORS,"Button",BS_AUTO3STATE | WS_TABSTOP,14,38,67,10
-    CONTROL         "Co&llision",IDC_CHK_COLL,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,86,67,10
     CONTROL         "&Animation",IDC_CHK_ANIMATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,50,67,10
+    CONTROL         "&Lights",IDC_CHK_LIGHTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,62,67,10
+    CONTROL         "Cameras",IDC_CHK_CAMERA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,74,67,10
+    CONTROL         "Co&llision",IDC_CHK_COLL,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,86,67,10
     CONTROL         "Furniture &Markers",IDC_CHK_FURN,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,97,67,10
-    GROUPBOX        "Behaviors:",IDC_STATIC,94,6,101,92
     CONTROL         "Flip U&V",IDC_CHK_FLIP_UV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,16,88,10
     CONTROL         "&Render Textures in View",IDC_CHK_SHOW_TEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,27,88,10
     CONTROL         "Auto Sm&ooth Mesh",IDC_CHK_AUTOSMOOTH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,38,88,11
     CONTROL         "Remove &Illegal Faces",IDC_CHK_ILLEGAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,50,88,11
     CONTROL         "Remove &Unused Bones",IDC_CHK_REM_BONES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,62,88,10
-    CONTROL         "Use &Biped",IDC_CHK_BIPED,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,102,84,87,10
+    CONTROL         "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,73,88,10
+    CONTROL         "Ignore User Prop Buffers",IDC_CHK_UPB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,86,89,10
+    GROUPBOX        "Behaviors:",IDC_STATIC,94,6,101,105
+    CONTROL         "Use &Biped",IDC_CHK_BIPED,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,102,98,87,10
+    LTEXT           "Game:",IDC_STATIC,7,129,31,8
+    COMBOBOX        IDC_CB_GAME,47,127,131,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
     LTEXT           "Skeleton:",IDC_STC_SKELETON,7,143,31,8
     EDITTEXT        IDC_ED_SKELETON,7,155,171,12,ES_AUTOHSCROLL
     PUSHBUTTON      "...",IDC_BTN_BROWSE,192,155,19,13
-    LTEXT           "Game:",IDC_STATIC,7,129,31,8
-    COMBOBOX        IDC_CB_GAME,47,127,131,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
     DEFPUSHBUTTON   "&Import",IDOK,5,172,34,14
     PUSHBUTTON      "&Cancel",IDCANCEL,45,172,33,14
     LTEXT           "http://www.niftools.org",IDC_LBL_LINK,93,172,95,14,SS_NOTIFY | SS_CENTERIMAGE
-    CONTROL         "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,73,88,10
-    CONTROL         "&Lights",IDC_CHK_LIGHTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,62,67,10
-    CONTROL         "Cameras",IDC_CHK_CAMERA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,74,67,10
 END
 
 IDD_KF_PANEL DIALOGEX 0, 0, 118, 99
@@ -66,12 +67,12 @@ CAPTION "Import KF"
 FONT 8, "MS Sans Serif", 0, 0, 0x1
 BEGIN
     GROUPBOX        "Import:",-1,7,6,104,51
-    DEFPUSHBUTTON   "&Import",IDOK,5,78,34,14
-    PUSHBUTTON      "&Cancel",IDCANCEL,45,78,33,14
-    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,7,61,95,14,SS_NOTIFY | SS_CENTERIMAGE
     CONTROL         "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,72,10
     CONTROL         "Add Key Notes",IDC_CHK_KEYNOTES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,31,72,10
     CONTROL         "Add Time Tags",IDC_CHK_TIMETAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,44,72,10
+    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,7,61,95,14,SS_NOTIFY | SS_CENTERIMAGE
+    DEFPUSHBUTTON   "&Import",IDOK,5,78,34,14
+    PUSHBUTTON      "&Cancel",IDCANCEL,45,78,33,14
 END
 
 
@@ -134,8 +135,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,2,3,0
- PRODUCTVERSION 0,2,3,0
+ FILEVERSION 0,2,4,0
+ PRODUCTVERSION 0,2,4,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -151,12 +152,12 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Importer"
-            VALUE "FileVersion", "0, 2, 3, 0"
+            VALUE "FileVersion", "0, 2, 4, 0"
             VALUE "InternalName", "MaxNifImport.dli"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "MaxNifImport.dli"
             VALUE "ProductName", "3ds Max Nif Importer"
-            VALUE "ProductVersion", "0, 2, 3, 0"
+            VALUE "ProductVersion", "0, 2, 4, 0"
         END
     END
     BLOCK "VarFileInfo"
@@ -175,7 +176,7 @@ STRINGTABLE
 BEGIN
     IDS_LIBDESCRIPTION      "Importer for NIF files"
     IDS_CATEGORY            "NifTools"
-    IDS_CLASS_NAME          "MaxNifImport"
+    IDS_CLASS_NAME          "Niftools_Nif_Importer"
     IDS_PARAMS              "Parameters"
     IDS_SPIN                "Spin"
 END
diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp
index 001a36d..cb52375 100644
--- a/NifImport/NIFImport.cpp
+++ b/NifImport/NIFImport.cpp
@@ -147,6 +147,7 @@ void NifImporter::LoadIniSettings()
    vertexColorMode = GetIniValue<int>(NifImportSection, "VertexColorMode", 1);
    useCiv4Shader = GetIniValue(NifImportSection, "UseCiv4Shader", true);
    mergeNonAccum = GetIniValue(NifImportSection, "MergeNonAccum", true);
+   importUPB = GetIniValue(NifImportSection, "ImportUPB", true);
 
    // Biped
    importBones = GetIniValue(BipedImportSection, "ImportBones", true);
@@ -210,6 +211,7 @@ void NifImporter::SaveIniSettings()
    SetIniValue(NifImportSection, "EnableAutoSmooth", enableAutoSmooth);
    SetIniValue(NifImportSection, "RemoveIllegalFaces", removeIllegalFaces);
    SetIniValue(NifImportSection, "RemoveDegenerateFaces", removeDegenerateFaces);
+   SetIniValue(NifImportSection, "ImportUPB", importUPB);
 
    SetIniValue(BipedImportSection, "ImportBones", importBones);
    SetIniValue(BipedImportSection, "RemoveUnusedImportedBones", removeUnusedImportedBones);  
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
index 087328b..6d47202 100644
--- a/NifImport/NIFImporter.h
+++ b/NifImport/NIFImporter.h
@@ -36,6 +36,7 @@ public:
    bool mergeNonAccum;
    bool enableLights;
    bool enableCameras;
+   bool importUPB;
 
    // Biped/Bones related settings
    bool importBones;
diff --git a/NifImport/NifDialog.cpp b/NifImport/NifDialog.cpp
index 2bf1152..4a36f87 100644
--- a/NifImport/NifDialog.cpp
+++ b/NifImport/NifDialog.cpp
@@ -57,6 +57,8 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
             CheckDlgButton(hWnd, IDC_CHK_CLEARANIM, imp->clearAnimation);
             
             CheckDlgButton(hWnd, IDC_CHK_BIPED, imp->useBiped);
+            CheckDlgButton(hWnd, IDC_CHK_UPB, !imp->importUPB);
+            
             
             string selection = (imp->appSettings) ? imp->appSettings->Name : "User";
             for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr)
@@ -108,6 +110,7 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
                   imp->removeUnusedImportedBones = IsDlgButtonChecked(hWnd, IDC_CHK_REM_BONES) ? true : false;
                   imp->clearAnimation = IsDlgButtonChecked(hWnd, IDC_CHK_CLEARANIM) ? true : false;
                   imp->useBiped = IsDlgButtonChecked(hWnd, IDC_CHK_BIPED) ? true : false;
+                  imp->importUPB = IsDlgButtonChecked(hWnd, IDC_CHK_UPB) ? false : true;
 
                   GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
                   if (AppSettings *appSettings = FindAppSetting(tmp)) {
diff --git a/NifImport/resource.h b/NifImport/resource.h
index a608a76..22c9238 100644
--- a/NifImport/resource.h
+++ b/NifImport/resource.h
@@ -21,6 +21,7 @@
 #define IDC_ED_SKELETON                 1006
 #define IDC_CHK_BIPED                   1007
 #define IDC_CHK_LIGHTS                  1008
+#define IDC_CHK_UPB                     1009
 #define IDC_CHK_COLL                    1010
 #define IDC_LBL_LINK                    1011
 #define IDC_CHK_VCOLORS                 1012
diff --git a/NifProps/NifProps.cpp b/NifProps/NifProps.cpp
index 62b11ae..af0afb2 100755
--- a/NifProps/NifProps.cpp
+++ b/NifProps/NifProps.cpp
@@ -27,11 +27,13 @@ public:
 
 	void			selectionChanged();
 
-	BOOL			dlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+   BOOL			dlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+   BOOL			dlgAnimProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
 private:
 		
-	HWND			mPanel;
+   HWND			mPanel;
+   HWND			mAnimPanel;
 	IUtil			*mIU;
 	Interface		*mIP;
 	INode			*mNode;
@@ -44,7 +46,7 @@ private:
 	NpComboBox		mCbMotionSystem;
 	NpComboBox		mCbQualityType;
 
-	void enableGUI(BOOL object, BOOL hvk);		
+	void enableGUI(BOOL object, BOOL hvk, BOOL anim);		
 };
 
 static NifProps plugin;
@@ -74,7 +76,12 @@ ClassDesc2* GetNifPropsDesc() { return &NifPropsDesc; }
 
 static BOOL CALLBACK NifPropsDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
-	return plugin.dlgProc(hWnd, msg, wParam, lParam);
+   return plugin.dlgProc(hWnd, msg, wParam, lParam);
+}
+
+static BOOL CALLBACK NifAnimPropsDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+   return plugin.dlgAnimProc(hWnd, msg, wParam, lParam);
 }
 
 
@@ -86,6 +93,7 @@ NifProps::NifProps()
 	mIU = NULL;
 	mIP = NULL;	
 	mPanel = NULL;
+   mAnimPanel = NULL;
 }
 
 NifProps::~NifProps()
@@ -163,6 +171,17 @@ void NifProps::BeginEditParams(Interface *ip, IUtil *iu)
 		str++;
 	}
 
+   
+   //mAnimPanel = ip->AddRollupPage(
+   //   hInstance,
+   //   MAKEINTRESOURCE(IDD_ANIM_PANEL),
+   //   NifAnimPropsDlgProc,
+   //   GetString(IDS_ANIM_PARAMS),
+   //   0);
+
+   mSpins[IDC_SP_ANIM_PRIORITY] = 
+      SetupFloatSpinner(mPanel, IDC_SP_ANIM_PRIORITY, IDC_ED_ANIM_PRIORITY, 0, 255, 25, .1f);
+
 	selectionChanged();
 }
 	
@@ -179,7 +198,7 @@ void NifProps::EndEditParams(Interface *ip,IUtil *iu)
 	mNode = NULL;
 }
 
-void NifProps::enableGUI(BOOL obj, BOOL hvk)
+void NifProps::enableGUI(BOOL obj, BOOL hvk, BOOL anim)
 {
 	EnableWindow(GetDlgItem(mPanel, IDC_GRP_OBJECT), obj);
 	EnableWindow(GetDlgItem(mPanel, IDC_CHK_ISCOLL), obj);
@@ -190,8 +209,11 @@ void NifProps::enableGUI(BOOL obj, BOOL hvk)
 	EnableWindow(GetDlgItem(mPanel, IDC_LBL_LAYER), hvk);
 	EnableWindow(GetDlgItem(mPanel, IDC_CB_LAYER), hvk);
 */
-	for (int i=IDC_HVK_BEGIN; i<=IDC_HVK_END; i++)
-		EnableWindow(GetDlgItem(mPanel, i), hvk);
+   for (int i=IDC_HVK_BEGIN; i<=IDC_HVK_END; i++)
+      EnableWindow(GetDlgItem(mPanel, i), hvk);
+
+   for (int i=IDC_ANIM_BEGIN; i<=IDC_ANIM_END; i++)
+      EnableWindow(GetDlgItem(mPanel, i), anim);
 }
 
 void NifProps::SelectionSetChanged(Interface *ip, IUtil *iu)
@@ -205,7 +227,7 @@ void NifProps::selectionChanged()
 	if (numSel <= 0)
 	{
 		mNode = NULL;
-		enableGUI(false, false);
+		enableGUI(false, false, false);
 		return;
 	}
 
@@ -213,7 +235,7 @@ void NifProps::selectionChanged()
 	INode *nodeSel = mIP->GetSelNode(0);
 	bool isColl = npIsCollision(nodeSel);
 
-	enableGUI(singleSel, singleSel && isColl);
+	enableGUI(singleSel, singleSel && isColl, true);
 	mNode = singleSel?nodeSel:NULL;		
 	
 	if (!singleSel)
@@ -262,6 +284,9 @@ void NifProps::selectionChanged()
 	mSpins[IDC_SP_MAX_LINEAR_VELOCITY]->SetValue(maxlinvel, TRUE);
 	mSpins[IDC_SP_MAX_ANGULAR_VELOCITY]->SetValue(maxangvel, TRUE);
 
+   float priority;
+   npGetProp(nodeSel, NP_ANM_PRI, priority, NP_DEFAULT_ANM_PRI);
+   mSpins[IDC_SP_ANIM_PRIORITY]->SetValue(priority, TRUE);
 
 }
 
@@ -383,6 +408,12 @@ BOOL NifProps::dlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 				case IDC_SP_PENETRATION_DEPTH:
 					npSetProp(mNode, NP_HVK_PENETRATION_DEPTH, mSpins[IDC_SP_PENETRATION_DEPTH]->GetFVal());
 					break;
+
+            case IDC_SP_ANIM_PRIORITY:
+               for (int i=0, n=mIP->GetSelNodeCount(); i<n; ++i) {
+                  npSetProp(mIP->GetSelNode(i), NP_ANM_PRI, mSpins[IDC_SP_ANIM_PRIORITY]->GetFVal());
+               }
+               break;
 			}
 			break;
 
diff --git a/NifProps/NifProps.rc b/NifProps/NifProps.rc
index a070ca4..68b5abc 100755
--- a/NifProps/NifProps.rc
+++ b/NifProps/NifProps.rc
@@ -73,7 +73,7 @@ END
 // Dialog
 //
 
-IDD_PANEL DIALOGEX 0, 0, 107, 401
+IDD_PANEL DIALOGEX 0, 0, 107, 360
 STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE
 FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
@@ -84,11 +84,6 @@ BEGIN
     GROUPBOX        "Havok",IDC_GRP_HAVOK,5,39,95,285,WS_DISABLED
     GROUPBOX        "Object",IDC_GRP_OBJECT,5,5,95,32
     CONTROL         "Is Collision Mesh",IDC_CHK_ISCOLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,80,10
-    GROUPBOX        "Global",IDC_STATIC,5,329,95,65,WS_DISABLED
-    EDITTEXT        IDC_ED_BSXFLAGS,13,352,80,12,ES_AUTOHSCROLL | WS_DISABLED
-    CONTROL         "BSXFlags",IDC_LBL_BSXFLAGS,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,13,340,80,8
-    EDITTEXT        IDC_ED_STRINGSEXTRA,13,374,80,12,ES_AUTOHSCROLL | WS_DISABLED
-    CONTROL         "Strings Extra Data",IDC_LBL_STRINGSEXTRA,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,13,365,80,8
     CONTROL         "Center",IDC_LBL_CENTER,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_DISABLED | WS_GROUP,12,98,31,8
     CONTROL         "Mass",IDC_LBL_MASS,"Static",SS_LEFTNOWORDWRAP | WS_DISABLED | WS_GROUP,12,124,28,8
     CONTROL         "Friction",IDC_LBL_FRICTION,"Static",SS_LEFTNOWORDWRAP | WS_DISABLED | WS_GROUP,42,124,28,8
@@ -124,6 +119,20 @@ BEGIN
     LTEXT           "Penetration\r\nDepth",IDC_LBL_PENETRATION_DEPTH,12,229,40,16,WS_DISABLED
     CONTROL         "",IDC_ED_PENETRATION_DEPTH,"CustEdit",WS_DISABLED | WS_TABSTOP,12,249,35,10
     CONTROL         "",IDC_SP_PENETRATION_DEPTH,"SpinnerControl",WS_DISABLED,45,249,7,10
+    GROUPBOX        "Animation",IDC_GRP_ANIMATION,7,325,95,28
+    LTEXT           "Priority:",IDC_STATIC,10,338,24,8
+    CONTROL         "",IDC_ED_ANIM_PRIORITY,"CustEdit",WS_DISABLED | WS_TABSTOP,39,337,20,10
+    CONTROL         "",IDC_SP_ANIM_PRIORITY,"SpinnerControl",WS_DISABLED,57,337,7,10
+END
+
+IDD_ANIM_PANEL DIALOGEX 0, 0, 107, 41
+STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+    GROUPBOX        "Animation",IDC_GRP_ANIMATION,5,5,95,28
+    LTEXT           "Priority:",IDC_STATIC,8,18,24,8
+    CONTROL         "",IDC_ED_ANIM_PRIORITY,"CustEdit",WS_DISABLED | WS_TABSTOP,37,17,20,10
+    CONTROL         "",IDC_SP_ANIM_PRIORITY,"SpinnerControl",WS_DISABLED,55,17,7,10
 END
 
 
@@ -140,7 +149,15 @@ BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 100
         TOPMARGIN, 7
-        BOTTOMMARGIN, 394
+        BOTTOMMARGIN, 353
+    END
+
+    IDD_ANIM_PANEL, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 100
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 34
     END
 END
 #endif    // APSTUDIO_INVOKED
@@ -342,8 +359,8 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,2,3,0
- PRODUCTVERSION 0,2,3,0
+ FILEVERSION 0,2,4,0
+ PRODUCTVERSION 0,2,4,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -359,12 +376,12 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Reactor Properites Plugin"
-            VALUE "FileVersion", "0, 2, 3, 0"
+            VALUE "FileVersion", "0, 2, 4, 0"
             VALUE "InternalName", "NifProps.dlu"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "NifProps.dlu"
             VALUE "ProductName", "3ds Max Nif Reactor Properites Plugin"
-            VALUE "ProductVersion", "0, 2, 3, 0"
+            VALUE "ProductVersion", "0, 2, 4, 0"
         END
     END
     BLOCK "VarFileInfo"
@@ -386,6 +403,7 @@ BEGIN
     IDS_CLASS_NAME          "NifProps"
     IDS_PARAMS              "Parameters"
     IDS_SPIN                "Spin"
+    IDS_ANIM_PARAMS         "Animation"
 END
 
 #endif    // English (U.S.) resources
diff --git a/NifProps/resource.h b/NifProps/resource.h
index d9cfbe8..fc3cc8a 100755
--- a/NifProps/resource.h
+++ b/NifProps/resource.h
@@ -7,15 +7,17 @@
 #define IDS_CLASS_NAME                  3
 #define IDS_PARAMS                      4
 #define IDS_SPIN                        5
+#define IDS_ANIM_PARAMS                 6
 #define IDD_PANEL                       101
-#define IDC_LBL_BSXFLAGS				1001
-#define IDC_ED_BSXFLAGS					1002
-#define IDC_ED_STRINGSEXTRA				1003
-#define IDC_LBL_STRINGSEXTRA			1004
+#define IDD_ANIM_PANEL                  102
+#define IDC_LBL_BSXFLAGS                1001
+#define IDC_ED_BSXFLAGS                 1002
+#define IDC_ED_STRINGSEXTRA             1003
+#define IDC_LBL_STRINGSEXTRA            1004
 #define IDC_GRP_OBJECT                  1005
 #define IDC_CHK_ISCOLL                  1006
 #define IDC_GRP_HAVOK                   1007
-#define IDC_HVK_BEGIN					IDC_GRP_HAVOK
+#define IDC_HVK_BEGIN					    1007
 #define IDC_LBL_MATERIAL                1008
 #define IDC_CB_MATERIAL                 1009
 #define IDC_LBL_LAYER                   1010
@@ -51,11 +53,16 @@
 #define IDC_LBL_ANGULAR_DAMPING         1517
 #define IDC_ED_ANGULAR_DAMPING          1518
 #define IDC_SP_ANGULAR_DAMPING          1519
-#define IDC_LBL_MOTION_SYSTEM			1520
-#define IDC_CB_MOTION_SYSTEM			1521
-#define IDC_LBL_QUALITY_TYPE			1522
-#define IDC_CB_QUALITY_TYPE				1523
-#define IDC_HVK_END						IDC_CB_QUALITY_TYPE
+#define IDC_LBL_MOTION_SYSTEM           1520
+#define IDC_CB_MOTION_SYSTEM            1521
+#define IDC_LBL_QUALITY_TYPE            1522
+#define IDC_CB_QUALITY_TYPE             1523
+#define IDC_HVK_END						    1523
+#define IDC_ANIM_BEGIN                  1600
+#define IDC_GRP_ANIMATION               1600
+#define IDC_ED_ANIM_PRIORITY            1601
+#define IDC_SP_ANIM_PRIORITY            1602
+#define IDC_ANIM_END                    1602
 
 // Next default values for new objects
 // 
@@ -63,7 +70,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        103
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1035
+#define _APS_NEXT_CONTROL_VALUE         1700
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
-- 
GitLab