diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index aa672ebf02c74fb1a398c965b75c878050e72cf9..a3f959e56e3b957cf7779f6b53b792fe15d1fc82 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -1,4 +1,4 @@
-                        MaxPlugins 0.2.7
+                        MaxPlugins 0.2.8
                         ================
 
  
@@ -32,18 +32,30 @@
 
     Change log
     ----------
-0.2.7
------
-o All
-- Add options to disable certain parts of plug via INI
-o Added so people who only want importer can disable exporter.
-- Enhancements from NifLib including support for Freedom Force
-- Max 5 build is available but untested.
+      0.2.8
+      -----   
+    o Exporter
+      - Fix Collapse Transforms on dialog so that it actually triggers collapse
+      - Change defaults for textures so Morrowind does not crash in CS
+      - Fix export of animation for Morrowind
+      - Introduce mechanism in Collapse Transforms so that poorly scaled 
+        models export better
 
-o Exporter
-- Fix bug with NiTriShape export which created corrupted files.
-- Add option to Update Tangent Space (for Oblivion)
-- Add option to Collapse Transforms on meshes (mostly a debug aide)
+    o Importer
+      - Fix import of animation for Morrowind
+    
+      0.2.7
+      -----
+    o All
+      - Add options to disable certain parts of plug via INI
+        o Added so people who only want importer can disable exporter.
+      - Enhancements from NifLib including support for Freedom Force
+      - Max 5 build is available but untested.
+    
+    o Exporter
+      - Fix bug with NiTriShape export which created corrupted files.
+      - Add option to Update Tangent Space (for Oblivion)
+      - Add option to Collapse Transforms on meshes (mostly a debug aide)
       
         
       0.2.6
diff --git a/NifCommon/NifVersion.h b/NifCommon/NifVersion.h
index d03d748b7a31f38d65acfc5878954c981250063c..2248b4e99f84e175c0333b8fb5e4304c1693c6e4 100644
--- a/NifCommon/NifVersion.h
+++ b/NifCommon/NifVersion.h
@@ -18,10 +18,10 @@ HISTORY:
 */
 #define VERSION_MAJOR_INT  0
 #define VERSION_MINOR_INT  2
-#define VERSION_BUILD_INT  7
+#define VERSION_BUILD_INT  8
 #define VERSION_PATCH_INT  0
 
-#define VERSION_STRING "0, 2, 7, 0"
+#define VERSION_STRING "0, 2, 8, 0"
 
 //#define DEF_VERSION_STRING(a,b,c,d) #a ", " #b ", " #c ", " #d
 //#define VERSION_STRING DEF_VERSION_STRING(a,b,c,d)
diff --git a/NifExport/Animation.cpp b/NifExport/Animation.cpp
index c8c5c94a6e0049dcb5970db9843de5cd21241567..44ad58372c45b2104a68e566ccfd670ddd678156 100644
--- a/NifExport/Animation.cpp
+++ b/NifExport/Animation.cpp
@@ -208,7 +208,8 @@ bool Exporter::isNodeTracked(INode *node)
                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)) {
+                     // Versions less than 20.0.0.4 will always export
+                     if (Exporter::mNifVersionInt < VER_20_0_0_4 || wildmatch("start*", key->note) ) {
                         return true;
                      }
                   }
@@ -373,6 +374,13 @@ bool AnimationExport::doExport(NiControllerSequenceRef seq)
          }
       }
    }
+   // keys without explicit start/stop will 
+   if (!textKeys.empty() && seq->GetStartTime() == FloatINF)
+   {
+      seq->SetStartTime(0.0f);
+      seq->SetStopTime(textKeys.back().time);
+   }
+
    textKeyData->SetKeys(textKeys);
 
    // Now let the fun begin.
diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp
index d65cecd7f6c693649ea4576b197127f4f3e7baaa..370d08f53be11f260e482bff55fa9ab737698d86 100755
--- a/NifExport/Exporter.cpp
+++ b/NifExport/Exporter.cpp
@@ -175,6 +175,17 @@ Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
       Result result = exportNodes(root, node);
       if (result != Ok)
          return result;
+
+      // Fix Used Nodes that where never properly initialized.  Happens normally during select export
+      for (NodeMap::iterator itr = mNodeMap.begin(); itr != mNodeMap.end(); ++itr) {
+         NiNodeRef bone = (*itr).second;
+         if (bone->GetParent() == NULL) {
+            if (INode* boneNode = mI->GetINodeByName((*itr).first.c_str())) {
+               makeNode(root, boneNode, false);
+            }
+         }
+      }
+
       if (mExportCollision) {
          result = exportCollision(root, node);
          if (result != Ok)
diff --git a/NifExport/Mesh.cpp b/NifExport/Mesh.cpp
index 9faaab52a7dd88bb50efe631f556b4a81f067429..2539ea63c446ac1e622163da80854a3bc69140ab 100755
--- a/NifExport/Mesh.cpp
+++ b/NifExport/Mesh.cpp
@@ -7,9 +7,6 @@
 #ifdef USE_BIPED
 #  include <cs/BipedApi.h>
 #endif
-#include "obj/NiSkinInstance.h"
-#include "obj/NiSkinData.h"
-#include "obj/NiSkinPartition.h"
 #include "obj/NiBSBoneLODController.h"
 #include "obj/NiTransformController.h"
 #include "obj/bhkBlendController.h"
@@ -18,7 +15,7 @@
 #include "obj/bhkCapsuleShape.h"
 
 Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue t)
-{	
+{
 	ObjectState os = node->EvalWorldState(t);
 
    bool local = !mFlattenHierarchy;
@@ -27,8 +24,37 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 	if (!tri)
 		return Error;
 
+   Mesh *copymesh = NULL;
 	Mesh *mesh = &tri->GetMesh();
 
+   Matrix3 mtx(true);
+   if (Exporter::mCollapseTransforms)
+   {
+      mtx = GetNodeLocalTM(node, t);
+      //if ( fabs(mtx.GetRow(0)[0]) != fabs(mtx.GetRow(1)[1])
+      //   ||fabs(mtx.GetRow(0)[0]) != fabs(mtx.GetRow(2)[2])
+      //   )
+      {
+         mtx.NoTrans();    
+         mesh = copymesh = new Mesh(*mesh);     
+         int n = mesh->getNumVerts();
+         for ( uint i = 0; i < n; ++i ) {
+            Point3& vert = mesh->getVert(i);
+            vert = mtx * vert;
+         }
+         mesh->checkNormals(TRUE);
+#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 6+
+         MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
+         if (NULL != specNorms) {
+            specNorms->CheckNormals();
+            for ( uint i = 0; i < specNorms->GetNumNormals(); ++i ) {
+               Point3& norm = specNorms->Normal(i);
+               norm = (mtx * norm).Normalize();
+            }
+         }
+#endif
+      }
+   }
    // Note that calling setVCDisplayData will clear things like normals so we set this up first
    vector<Color4> vertColors;
    if (mVertexColors)
@@ -97,6 +123,7 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
          nodeTransform(rot, trans, node, t, local);
          tm = Matrix44(trans, rot, 1.0f);
       }
+      tm = TOMATRIX4(Inverse(mtx)) * tm;
 
       TSTR basename = node->NodeName();
       TSTR format = (!basename.isNull() && grps.size() > 1) ? "%s:%d" : "%s";
@@ -132,6 +159,9 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 	if (tri != os.obj)
 		tri->DeleteMe();
 
+   if (copymesh)
+      delete copymesh;
+
 	return result;
 }
 
@@ -397,10 +427,13 @@ Exporter::Result SkinInstance::execute()
    for (BoneWeightList::iterator bitr = boneWeights.begin(); bitr != boneWeights.end(); ++bitr, ++bone) {
       shape->SetBoneWeights(bone, (*bitr));
    }
-   if (Exporter::mMultiplePartitions)
-      shape->GenHardwareSkinInfo(Exporter::mBonesPerPartition, Exporter::mBonesPerVertex);
-   else
-      shape->GenHardwareSkinInfo(0, 0);
+   if (Exporter::mNifVersionInt > VER_4_0_0_2)
+   {
+      if (Exporter::mMultiplePartitions)
+         shape->GenHardwareSkinInfo(Exporter::mBonesPerPartition, Exporter::mBonesPerVertex);
+      else
+         shape->GenHardwareSkinInfo(0, 0);
+   }
    return Exporter::Ok;
 }
 
diff --git a/NifExport/MtlTex.cpp b/NifExport/MtlTex.cpp
index 9465d638f0ccbc8947b0a359f3ffb5dbfd208226..8624b178a0f0e6ba7700b9f4325353cc20716160 100755
--- a/NifExport/MtlTex.cpp
+++ b/NifExport/MtlTex.cpp
@@ -30,7 +30,7 @@ void Exporter::makeTexture(NiAVObjectRef &parent, Mtl *mtl)
 
 bool Exporter::makeTextureDesc(BitmapTex *bmTex, TexDesc& td)
 {
-   td.source = CreateNiObject<NiSourceTexture>();
+   td.source = new NiSourceTexture();
 
    // Filtering
    switch (bmTex->GetFilterType())
@@ -378,7 +378,6 @@ bool Exporter::exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl)
       if (alphaMode != 0 || AlphaTestEnable) {
          // always add alpha ???
          NiAlphaPropertyRef alphaProp = CreateNiObject<NiAlphaProperty>();
-         parent->AddProperty(alphaProp);
          alphaProp->SetAlphaBlend(true);
          if (alphaMode == 0) { // automatic
             alphaProp->SetSourceBlendMode(NiAlphaProperty::BlendMode(srcBlend));
@@ -404,6 +403,7 @@ bool Exporter::exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl)
          alphaProp->SetAlphaSort(!NoSorter);
          alphaProp->SetAlphaTestThreshold(TestRef);
          alphaProp->SetAlphaTest(AlphaTestEnable);
+         parent->AddProperty(alphaProp);
       }
 
       int ntex = mtl->NumSubTexmaps();
@@ -411,15 +411,18 @@ bool Exporter::exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl)
       {
          ntex = min(ntex, 7);
          TexType texmap[] = {BASE_MAP, DARK_MAP, DETAIL_MAP, DECAL_0_MAP, BUMP_MAP, GLOSS_MAP, GLOW_MAP, DECAL_1_MAP};
-         NiTexturingPropertyRef texProp = CreateNiObject<NiTexturingProperty>();       
-         texProp->SetApplyMode(Niflib::ApplyMode(ApplyMode));
-         texProp->SetTextureCount(7);
+         NiTexturingPropertyRef texProp;
          for (int i = 0; i < ntex; ++i) {
-
             BitmapTex *bmTex = getTexture(mtl, i);
             if (!bmTex)
                continue;
 
+            if (texProp == NULL)
+            {
+               texProp = new NiTexturingProperty();       
+               texProp->SetApplyMode(Niflib::ApplyMode(ApplyMode));
+               texProp->SetTextureCount(7);
+            }
             TexDesc td;
             if (makeTextureDesc(bmTex, td)) {
                TexType textype = texmap[i];
@@ -437,7 +440,10 @@ bool Exporter::exportCiv4Shader(NiAVObjectRef parent, Mtl* mtl)
             }
          }
 
-         parent->AddProperty(texProp);
+         if (texProp != NULL)
+         {
+            parent->AddProperty(texProp);
+         }
       }
       return true;
    }
diff --git a/NifExport/NifExport.cpp b/NifExport/NifExport.cpp
index 47a0bd4e328f1baffc9cfd58f93f2af0396cafa6..22a8ad5ae72112d5996a999190e880ee8b3a0b9e 100755
--- a/NifExport/NifExport.cpp
+++ b/NifExport/NifExport.cpp
@@ -104,6 +104,8 @@ BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARA
          CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras);
          CheckDlgButton(hWnd, IDC_CHK_BONE_COLL, Exporter::mGenerateBoneCollision);            
          CheckDlgButton(hWnd, IDC_CHK_TANGENTS, Exporter::mTangentAndBinormalExtraData);
+         CheckDlgButton(hWnd, IDC_CHK_COLLAPSE_TRANS, Exporter::mCollapseTransforms);
+         
 
          string selection = Exporter::mGameName;
          string version = Exporter::mNifVersion;
@@ -190,6 +192,7 @@ BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARA
             Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA);
             Exporter::mGenerateBoneCollision = IsDlgButtonChecked(hWnd, IDC_CHK_BONE_COLL);
             Exporter::mTangentAndBinormalExtraData = IsDlgButtonChecked(hWnd, IDC_CHK_TANGENTS);
+            Exporter::mCollapseTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_COLLAPSE_TRANS);
 
             Exporter::mExportTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_TRANSFORMS2);
             //Exporter::mUseTimeTags = IsDlgButtonChecked(hWnd, IDC_CHK_USE_TIME_TAGS);           
diff --git a/NifExport/Util.cpp b/NifExport/Util.cpp
index 446731c8a631a8b51eb41aaaecffe1204a0af73f..610fd0fbce6a80fce80798d056b53d8f6916fc06 100755
--- a/NifExport/Util.cpp
+++ b/NifExport/Util.cpp
@@ -427,12 +427,50 @@ void Exporter::getChildNodes(INode *node, vector<NiNodeRef>& list)
    for (int i=0; i<node->NumberOfChildren(); i++) 
    {
       INode * child = node->GetChildNode(i);
-      list.push_back( getNode(child->GetName()) );
+      ObjectState os = node->EvalWorldState(0); 
+      bool addBone = false;
+      bool local = !mFlattenHierarchy;
+
+      if (node->IsBoneShowing()) 
+      {
+         addBone = true;
+      }
+      else if (os.obj && os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
+      {
+         if (  os.obj 
+            && (  os.obj->ClassID() == BONE_OBJ_CLASSID 
+            || os.obj->ClassID() == Class_ID(BONE_CLASS_ID,0)
+            || os.obj->ClassID() == Class_ID(0x00009125,0) /* Biped Twist Helpers */
+            )
+            ) 
+         {
+            addBone = true;
+         } 
+         else if (!mSkeletonOnly)
+         {
+            if (mExportType != NIF_WO_ANIM && isNodeTracked(node)) {
+               addBone = true;
+            } else if ( mExportExtraNodes || (mExportType != NIF_WO_ANIM && isNodeKeyed(node) ) ) {
+               addBone = true;
+            }
+         }
+         else if (mExportCameras && os.obj && os.obj->SuperClassID()==CAMERA_CLASS_ID)
+         {
+            addBone = true;
+         }
+         else if (isMeshGroup(node) && local) // only create node if local
+         {
+            addBone = true;
+         } 
+      }
+      if (addBone)
+      {
+         list.push_back( getNode(child->GetName()) );
+      }
       getChildNodes(child, list);
    }
 }
 
-
 // Special case of a single branch being exported
 
 bool Exporter::exportPrn(NiNodeRef &obj, INode *node) {
diff --git a/NifExport/resource.h b/NifExport/resource.h
index b52595ed11706100c62a83f169da8f944d067965..3bc59f19ee1a07a12817b8d40004c89a5f0ecfb8 100755
--- a/NifExport/resource.h
+++ b/NifExport/resource.h
@@ -64,6 +64,8 @@
 #define IDC_SPIN                        13496
 #define IDC_LBL_WIKI                    13497
 #define IDC_CHK_TANGENTS                13498
+#define IDC_CHECK2                      13499
+#define IDC_CHK_COLLAPSE_TRANS          13499
 
 // Next default values for new objects
 // 
@@ -71,7 +73,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        103
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         13499
+#define _APS_NEXT_CONTROL_VALUE         13500
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
index 816c04d112bd87853490cbbe14e8e57b1c472537..a7a6386419f83bb7ac8c20b952395642cc344954 100644
--- a/NifImport/ImportAnimation.cpp
+++ b/NifImport/ImportAnimation.cpp
@@ -47,6 +47,25 @@ void* operator new(size_t size, NoteKey* stub )
 
 void operator delete(void* memblock, NoteKey* stub )
 { return MAX_delete(memblock); }
+#else
+void* operator new(size_t size, NoteKey* stub )
+{ 
+   void * (__cdecl *pfmalloc)(__in size_t _Size) = 0;
+   if (HMODULE hMod = GetModuleHandle("msvcrt.dll")) 
+      *(FARPROC*)pfmalloc = GetProcAddress(hMod, "malloc");
+   if (pfmalloc == 0) pfmalloc = malloc;
+   return pfmalloc(size); 
+}
+
+void operator delete(void* memblock, NoteKey* stub )
+{ 
+   void (__cdecl *pffree)(void *memblock) = 0;
+   if (HMODULE hMod = GetModuleHandle("msvcrt.dll")) 
+      *(FARPROC*)pffree = GetProcAddress(hMod, "free");
+   if (pffree == 0) pffree = free;
+   return pffree(memblock); 
+}
+
 #endif
 
 struct AnimationImport
@@ -309,8 +328,9 @@ bool NifImporter::AddNoteTracks(float time, string name, string target, NiTextKe
       }
       gi->SetAnimRange(range);
 
-      if (addNoteTracks && (wildmatch("start*", textKeys.front().data)) ) {
+      if (addNoteTracks /*&& (wildmatch("start*", textKeys.front().data))*/ ) {
          if ( INode *n = gi->GetINodeByName(target.c_str()) ) {
+//#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 6+
 #if 1
             DefNoteTrack* nt = (DefNoteTrack*)NewDefaultNoteTrack();
             n->AddNoteTrack(nt);
@@ -350,7 +370,7 @@ bool NifImporter::AddNoteTracks(float time, string name, string target, NiTextKe
                }
             }
 
-#else
+#else // Version 5
             TSTR script;
             script += 
                "fn getActorManager obj = (\n"
@@ -378,8 +398,7 @@ bool NifImporter::AddNoteTracks(float time, string name, string target, NiTextKe
                   stringlist args = TokenizeCommandLine((*itr).data.c_str(), true);
                   if (args.empty()) continue;
                   bool hasName = false;
-                  bool hasLoop = false;
-                  CycleType ct = cntr->GetCycleType();
+                  bool hasLoop = loop;
                   for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) {
                      if (strmatch("-name", *itr)) {
                         if (++itr == args.end()) break;                       
@@ -389,13 +408,11 @@ bool NifImporter::AddNoteTracks(float time, string name, string target, NiTextKe
                      }
                   }
                   if (!hasName) {
-                     string name = cntr->GetName();
-                     if (name.empty())
-                        name = FormatString("EMPTY_SEQUENCE_AT_%df", int(t * FramesPerSecond / TicksPerFrame) );
+                     string name = FormatString("EMPTY_SEQUENCE_AT_%df", int(t * FramesPerSecond / TicksPerFrame) );
                      args.push_back("-name");
                      args.push_back(name);
                   }
-                  if (!hasLoop && ct == CYCLE_LOOP) {
+                  if (!hasLoop) {
                      args.push_back("-loop");
                   }
 
@@ -408,7 +425,7 @@ bool NifImporter::AddNoteTracks(float time, string name, string target, NiTextKe
                //NoteKey *key = new NoteKey(TimeToFrame(time + (*itr).time), (*itr).data.c_str(), 0);
                //nt->keys.Append(1, &key);
             }
-            ExecuteMAXScriptScript(script, TRUE, NULL);
+            //ExecuteMAXScriptScript(script, TRUE, NULL);
 #endif
          }
       }
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
index dc81a62d7f39a11eeeb59aed12b6937aba414688..e5f5b6013701b8d302197b154fe4a8018b03b23c 100644
--- a/NifImport/ImportSkeleton.cpp
+++ b/NifImport/ImportSkeleton.cpp
@@ -574,7 +574,7 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse)
       vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
 
       NiAVObject::CollisionType cType = node->GetCollision();
-      if (cType == NiAVObject::CT_BOUNDINGBOX && children.empty() && name=="Bounding Box")
+      if (children.empty() && name=="Bounding Box")
          return;
 
       // Do all node manipulations here
diff --git a/NifPlugins/DllEntry.cpp b/NifPlugins/DllEntry.cpp
index fea12c602a45f0b382b18a17644aa02e3b25a4ae..4df1a75977bb1fbbde5de1b1887ba22cc69f81f1 100644
--- a/NifPlugins/DllEntry.cpp
+++ b/NifPlugins/DllEntry.cpp
@@ -16,6 +16,8 @@ extern ClassDesc2* GetNifExportDesc();
 extern ClassDesc2* GetNifPropsDesc();
 extern ClassDesc2* GetNifFurnitureDesc();
 extern ClassDesc2* GetKfExportDesc();
+extern ClassDesc2* GetbhkSphereDesc();
+extern ClassDesc2* GetbhkCapsuleDesc();
 
 enum ClassDescType
 {
@@ -34,7 +36,7 @@ static int controlsInit = FALSE;
 static int libVersion = VERSION_3DSMAX;
 static int foundOlderReleaseConflict = -1;
 static int nClasses = 0;
-static ClassDesc2* classDescriptions[CD_Count];
+static ClassDesc2* classDescriptions[20];
 static bool classDescEnabled[CD_Count];
 
 
@@ -91,6 +93,10 @@ void InitializeLibSettings()
    if ( GetIniValue<bool>("NifProps", "Enable", true, iniName) ) {
       classDescEnabled[CD_Props] = true;
       classDescriptions[nClasses++] = GetNifPropsDesc();
+#ifdef USE_UNSUPPORTED_CODE
+      classDescriptions[nClasses++] = GetbhkSphereDesc();
+      classDescriptions[nClasses++] = GetbhkCapsuleDesc();
+#endif
    }
    if ( GetIniValue<bool>("NifFurniture", "Enable", true, iniName) ) {
       classDescEnabled[CD_Furniture] = true;
diff --git a/NifProps/NifProps.rc b/NifProps/NifProps.rc
index 7138fc9865897c2eb1beae5dca25259473dcbf9e..d01af50e8e27a952d550c82495516d5bd50bcece 100755
--- a/NifProps/NifProps.rc
+++ b/NifProps/NifProps.rc
@@ -145,6 +145,84 @@ BEGIN
     CONTROL         "",IDC_SP_ANIM_PRIORITY,"SpinnerControl",WS_DISABLED,55,17,7,10
 END
 
+IDD_SPHEREPARAM2 DIALOGEX 0, 0, 108, 51
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+    CONTROL         "",IDC_RADIUS,"CustEdit",WS_TABSTOP,49,8,36,10
+    CONTROL         "",IDC_SEGMENTS,"CustEdit",WS_TABSTOP,49,21,36,10
+    CONTROL         "Smooth",IDC_OBSMOOTH,"Button",BS_AUTOCHECKBOX,49,36,36,10
+    RTEXT           "Radius:",-1,22,9,25,8
+    RTEXT           "Segments:",-1,11,21,36,8
+    CONTROL         "",IDC_SEGSPINNER,"SpinnerControl",0x0,87,21,7,10
+    CONTROL         "",IDC_RADSPINNER,"SpinnerControl",0x0,87,8,7,10
+END
+
+IDD_RigidBody DIALOGEX 0, 0, 107, 238
+STYLE DS_SETFONT | DS_3DLOOK | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+    COMBOBOX        IDC_CB_MATERIAL,9,22,83,157,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Material",IDC_LBL_MATERIAL,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,9,14,83,8
+    COMBOBOX        IDC_CB_LAYER,9,46,83,171,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Layer",IDC_LBL_LAYER,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,9,37,83,8
+    GROUPBOX        "Rigid Body",IDC_GRP_HAVOK,4,3,98,230
+    CONTROL         "Mass",IDC_LBL_MASS,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,9,63,56,8
+    CONTROL         "Friction",IDC_LBL_FRICTION,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,9,76,56,8
+    LTEXT           "Restitution",IDC_LBL_RESTITUTION,9,87,56,11
+    CONTROL         "Motion System",IDC_LBL_MOTION_SYSTEM,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,9,183,67,8
+    COMBOBOX        IDC_CB_MOTION_SYSTEM,9,192,83,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Quality Type",IDC_LBL_QUALITY_TYPE,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,9,208,43,8
+    COMBOBOX        IDC_CB_QUALITY_TYPE,9,217,83,63,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "",IDC_ED_MASS,"CustEdit",WS_TABSTOP,67,63,22,10
+    CONTROL         "",IDC_SP_MASS,"SpinnerControl",0x0,90,63,7,10
+    CONTROL         "",IDC_ED_FRICTION,"CustEdit",WS_TABSTOP,67,75,22,10
+    CONTROL         "",IDC_SP_FRICTION,"SpinnerControl",0x0,90,75,7,10
+    CONTROL         "",IDC_ED_RESTITUTION,"CustEdit",WS_TABSTOP,67,87,22,10
+    CONTROL         "",IDC_SP_RESTITUTION,"SpinnerControl",0x0,90,87,7,10
+    LTEXT           "Linear Damping",IDC_LBL_LINEAR_DAMPING,9,100,56,11
+    CONTROL         "",IDC_ED_LINEAR_DAMPING,"CustEdit",WS_TABSTOP,67,100,22,10
+    CONTROL         "",IDC_SP_LINEAR_DAMPING,"SpinnerControl",0x0,90,100,7,10
+    LTEXT           "Angular Damping",IDC_LBL_ANGULAR_DAMPING,9,112,56,10
+    CONTROL         "",IDC_ED_ANGULAR_DAMPING,"CustEdit",WS_TABSTOP,67,112,22,10
+    CONTROL         "",IDC_SP_ANGULAR_DAMPING,"SpinnerControl",0x0,90,112,7,10
+    LTEXT           "Max. Linear Velocity",IDC_LBL_MAX_LINEAR_VELOCITY,9,124,56,16
+    CONTROL         "",IDC_ED_MAX_LINEAR_VELOCITY,"CustEdit",WS_TABSTOP,67,128,22,10
+    CONTROL         "",IDC_SP_MAX_LINEAR_VELOCITY,"SpinnerControl",0x0,90,128,7,10
+    LTEXT           "Max. Angular Velocity",IDC_LBL_MAX_ANGULAR_VELOCITY,9,143,56,16
+    CONTROL         "",IDC_ED_MAX_ANGULAR_VELOCITY,"CustEdit",WS_TABSTOP,67,147,22,10
+    CONTROL         "",IDC_SP_MAX_ANGULAR_VELOCITY,"SpinnerControl",0x0,90,147,7,10
+    LTEXT           "Penetration Depth",IDC_LBL_PENETRATION_DEPTH,9,162,56,16
+    CONTROL         "",IDC_ED_PENETRATION_DEPTH,"CustEdit",WS_TABSTOP,67,166,22,10
+    CONTROL         "",IDC_SP_PENETRATION_DEPTH,"SpinnerControl",0x0,90,166,7,10
+END
+
+IDD_CAPSULEPARAM DIALOGEX 0, 0, 112, 86
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+    CONTROL         "",IDC_RADIUS1,"CustEdit",WS_TABSTOP,49,5,36,10
+    RTEXT           "Radius 1:",-1,15,6,31,8
+    CONTROL         "",IDC_RADSPINNER1,"SpinnerControl",0x0,87,5,7,10
+    CONTROL         "",IDC_RADIUS2,"CustEdit",WS_TABSTOP,49,18,36,10
+    RTEXT           "Radius 2:",-1,15,18,31,8
+    CONTROL         "",IDC_RADSPINNER2,"SpinnerControl",0x0,87,18,7,10
+    CONTROL         "Cap Pos 1:",IDC_LBL_POS1,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,12,31,39,8
+    CONTROL         "",IDC_ED_POS1_X,"CustEdit",WS_TABSTOP,7,42,20,10
+    CONTROL         "",IDC_SP_POS1_X,"SpinnerControl",0x0,28,42,7,10
+    CONTROL         "",IDC_ED_POS1_Y,"CustEdit",WS_TABSTOP,40,42,20,10
+    CONTROL         "",IDC_SP_POS1_Y,"SpinnerControl",0x0,61,42,7,10
+    CONTROL         "",IDC_ED_POS1_Z,"CustEdit",WS_TABSTOP,73,42,20,10
+    CONTROL         "",IDC_SP_POS1_Z,"SpinnerControl",0x0,94,42,7,10
+    CONTROL         "Cap Pos 2:",IDC_LBL_POS2,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,12,56,39,8
+    CONTROL         "",IDC_ED_POS2_X,"CustEdit",WS_TABSTOP,7,68,20,10
+    CONTROL         "",IDC_SP_POS2_X,"SpinnerControl",0x0,28,68,7,10
+    CONTROL         "",IDC_ED_POS2_Y,"CustEdit",WS_TABSTOP,40,68,20,10
+    CONTROL         "",IDC_SP_POS2_Y,"SpinnerControl",0x0,61,68,7,10
+    CONTROL         "",IDC_ED_POS2_Z,"CustEdit",WS_TABSTOP,73,68,20,10
+    CONTROL         "",IDC_SP_POS2_Z,"SpinnerControl",0x0,94,68,7,10
+END
+
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -169,6 +247,20 @@ BEGIN
         TOPMARGIN, 7
         BOTTOMMARGIN, 34
     END
+
+    IDD_RigidBody, DIALOG
+    BEGIN
+        LEFTMARGIN, 4
+        RIGHTMARGIN, 102
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 233
+    END
+
+    IDD_CAPSULEPARAM, DIALOG
+    BEGIN
+        RIGHTMARGIN, 108
+        BOTTOMMARGIN, 85
+    END
 END
 #endif    // APSTUDIO_INVOKED
 
@@ -362,6 +454,190 @@ BEGIN
     0
 END
 
+IDD_RigidBody DLGINIT
+BEGIN
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x7453, 0x6e6f, 0x0065, 
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x6c43, 0x746f, 0x0068, 
+    IDC_CB_MATERIAL, 0x403, 5, 0
+0x6944, 0x7472, "\000" 
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x6c47, 0x7361, 0x0073, 
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x7247, 0x7361, 0x0073, 
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x654d, 0x6174, 0x006c, 
+    IDC_CB_MATERIAL, 0x403, 8, 0
+0x724f, 0x6167, 0x696e, 0x0063, 
+    IDC_CB_MATERIAL, 0x403, 5, 0
+0x6b53, 0x6e69, "\000" 
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x6157, 0x6574, 0x0072, 
+    IDC_CB_MATERIAL, 0x403, 5, 0
+0x6f57, 0x646f, "\000" 
+    IDC_CB_MATERIAL, 0x403, 12, 0
+0x6548, 0x7661, 0x2079, 0x7453, 0x6e6f, 0x0065, 
+    IDC_CB_MATERIAL, 0x403, 12, 0
+0x6548, 0x7661, 0x2079, 0x654d, 0x6174, 0x006c, 
+    IDC_CB_MATERIAL, 0x403, 11, 0
+0x6548, 0x7661, 0x2079, 0x6f57, 0x646f, "\000" 
+    IDC_CB_MATERIAL, 0x403, 6, 0
+0x6843, 0x6961, 0x006e, 
+    IDC_CB_MATERIAL, 0x403, 5, 0
+0x6e53, 0x776f, "\000" 
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x7453, 0x6e6f, 0x2065, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x6c43, 0x746f, 0x2068, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 12, 0
+0x6944, 0x7472, 0x5320, 0x6174, 0x7269, 0x0073, 
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x6c47, 0x7361, 0x2073, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x7247, 0x7361, 0x2073, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x654d, 0x6174, 0x206c, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 15, 0
+0x724f, 0x6167, 0x696e, 0x2063, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 12, 0
+0x6b53, 0x6e69, 0x5320, 0x6174, 0x7269, 0x0073, 
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x6157, 0x6574, 0x2072, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 12, 0
+0x6f57, 0x646f, 0x5320, 0x6174, 0x7269, 0x0073, 
+    IDC_CB_MATERIAL, 0x403, 19, 0
+0x6548, 0x7661, 0x2079, 0x7453, 0x6e6f, 0x2065, 0x7453, 0x6961, 0x7372, 
+"\000" 
+    IDC_CB_MATERIAL, 0x403, 19, 0
+0x6548, 0x7661, 0x2079, 0x654d, 0x6174, 0x206c, 0x7453, 0x6961, 0x7372, 
+"\000" 
+    IDC_CB_MATERIAL, 0x403, 18, 0
+0x6548, 0x7661, 0x2079, 0x6f57, 0x646f, 0x5320, 0x6174, 0x7269, 0x0073, 
+
+    IDC_CB_MATERIAL, 0x403, 13, 0
+0x6843, 0x6961, 0x206e, 0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_MATERIAL, 0x403, 12, 0
+0x6e53, 0x776f, 0x5320, 0x6174, 0x7269, 0x0073, 
+    IDC_CB_MATERIAL, 0x403, 9, 0
+0x6c45, 0x7665, 0x7461, 0x726f, "\000" 
+    IDC_CB_LAYER, 0x403, 13, 0
+0x6e55, 0x6469, 0x6e65, 0x6974, 0x6966, 0x6465, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x7453, 0x7461, 0x6369, "\000" 
+    IDC_CB_LAYER, 0x403, 11, 0
+0x6e41, 0x6d69, 0x7453, 0x7461, 0x6369, "\000" 
+    IDC_CB_LAYER, 0x403, 12, 0
+0x7254, 0x6e61, 0x7073, 0x7261, 0x6e65, 0x0074, 
+    IDC_CB_LAYER, 0x403, 8, 0
+0x6c43, 0x7475, 0x6574, 0x0072, 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x6557, 0x7061, 0x6e6f, "\000" 
+    IDC_CB_LAYER, 0x403, 11, 0
+0x7250, 0x6a6f, 0x6365, 0x6974, 0x656c, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x7053, 0x6c65, 0x006c, 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x6942, 0x6570, 0x0064, 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x7254, 0x6565, "\000" 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x7250, 0x706f, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x6157, 0x6574, 0x0072, 
+    IDC_CB_LAYER, 0x403, 8, 0
+0x7254, 0x6769, 0x6567, 0x0072, 
+    IDC_CB_LAYER, 0x403, 8, 0
+0x6554, 0x7272, 0x6961, 0x006e, 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x7254, 0x7061, "\000" 
+    IDC_CB_LAYER, 0x403, 14, 0
+0x6f4e, 0x436e, 0x6c6f, 0x696c, 0x6164, 0x6c62, 0x0065, 
+    IDC_CB_LAYER, 0x403, 10, 0
+0x6c43, 0x756f, 0x5464, 0x6172, 0x0070, 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x7247, 0x756f, 0x646e, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x6f50, 0x7472, 0x6c61, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x7453, 0x6961, 0x7372, "\000" 
+    IDC_CB_LAYER, 0x403, 15, 0
+0x6843, 0x7261, 0x6f43, 0x746e, 0x6f72, 0x6c6c, 0x7265, "\000" 
+    IDC_CB_LAYER, 0x403, 9, 0
+0x7641, 0x696f, 0x4264, 0x786f, "\000" 
+    IDC_CB_LAYER, 0x403, 2, 0
+0x003f, 
+    IDC_CB_LAYER, 0x403, 2, 0
+0x003f, 
+    IDC_CB_LAYER, 0x403, 11, 0
+0x6143, 0x656d, 0x6172, 0x6950, 0x6b63, "\000" 
+    IDC_CB_LAYER, 0x403, 9, 0
+0x7449, 0x6d65, 0x6950, 0x6b63, "\000" 
+    IDC_CB_LAYER, 0x403, 12, 0
+0x694c, 0x656e, 0x664f, 0x6953, 0x6867, 0x0074, 
+    IDC_CB_LAYER, 0x403, 9, 0
+0x6150, 0x6874, 0x6950, 0x6b63, "\000" 
+    IDC_CB_LAYER, 0x403, 12, 0
+0x7543, 0x7473, 0x6d6f, 0x6950, 0x6b63, 0x0031, 
+    IDC_CB_LAYER, 0x403, 12, 0
+0x7543, 0x7473, 0x6d6f, 0x6950, 0x6b63, 0x0032, 
+    IDC_CB_LAYER, 0x403, 15, 0
+0x7053, 0x6c65, 0x456c, 0x7078, 0x6f6c, 0x6973, 0x6e6f, "\000" 
+    IDC_CB_LAYER, 0x403, 13, 0
+0x7244, 0x706f, 0x6970, 0x676e, 0x6950, 0x6b63, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x744f, 0x6568, 0x0072, 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x6548, 0x6461, "\000" 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x6f42, 0x7964, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x7053, 0x6e69, 0x3165, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x7053, 0x6e69, 0x3265, "\000" 
+    IDC_CB_LAYER, 0x403, 10, 0
+0x554c, 0x7070, 0x7265, 0x7241, 0x006d, 
+    IDC_CB_LAYER, 0x403, 9, 0
+0x464c, 0x726f, 0x4165, 0x6d72, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x484c, 0x6e61, 0x0064, 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x544c, 0x6968, 0x6867, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x434c, 0x6c61, 0x0066, 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x464c, 0x6f6f, 0x0074, 
+    IDC_CB_LAYER, 0x403, 10, 0
+0x5552, 0x7070, 0x7265, 0x7241, 0x006d, 
+    IDC_CB_LAYER, 0x403, 9, 0
+0x4652, 0x726f, 0x4165, 0x6d72, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x4852, 0x6e61, 0x0064, 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x5452, 0x6968, 0x6867, "\000" 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x4352, 0x6c61, 0x0066, 
+    IDC_CB_LAYER, 0x403, 6, 0
+0x4652, 0x6f6f, 0x0074, 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x6154, 0x6c69, "\000" 
+    IDC_CB_LAYER, 0x403, 11, 0
+0x6953, 0x6564, 0x6557, 0x7061, 0x6e6f, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x6853, 0x6569, 0x646c, "\000" 
+    IDC_CB_LAYER, 0x403, 7, 0
+0x7551, 0x7669, 0x7265, "\000" 
+    IDC_CB_LAYER, 0x403, 11, 0
+0x6142, 0x6b63, 0x6557, 0x7061, 0x6e6f, "\000" 
+    IDC_CB_LAYER, 0x403, 11, 0
+0x6142, 0x6b63, 0x6557, 0x7061, 0x6e6f, "\000" 
+    IDC_CB_LAYER, 0x403, 9, 0
+0x6f50, 0x796e, 0x6154, 0x6c69, "\000" 
+    IDC_CB_LAYER, 0x403, 5, 0
+0x6957, 0x676e, "\000" 
+    0
+END
+
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -376,6 +652,57 @@ BEGIN
     IDS_PARAMS              "Parameters"
     IDS_SPIN                "Spin"
     IDS_ANIM_PARAMS         "Animation"
+    IDS_RB_Capsule          "bhkCapsule"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_RADIUS           "Radius"
+    IDS_RB_SEGS             "Segments"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_RADIUS1          "Radius 1"
+    IDS_RB_RADIUS2          "Radius 2"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_SPHERE           "bhkSphere"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_SPHERE_CLASS     "bhkSphere"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_PRIMITIVES       "Standard Primitives"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_DS_PARAMCHG         "Parameter Change"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_PARAMETERS       "Parameters"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_SMOOTH           "Smooth"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RB_CAPSULE_CLASS    "bhkCapsule"
+    IDS_RB_RIGIDBODY_PARAM  "Rigid Body Parameters"
+    IDS_RB_CAP_POS1         "Cap Position 1"
+    IDS_RB_CAP_POS2         "Cap Position 2"
 END
 
 #endif    // English (U.S.) resources
diff --git a/NifProps/bhkCapsuleObj.cpp b/NifProps/bhkCapsuleObj.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e237f2f0ed72998ffd14811579bf889fc60925d9
--- /dev/null
+++ b/NifProps/bhkCapsuleObj.cpp
@@ -0,0 +1,1010 @@
+/**********************************************************************
+*<
+FILE: bhkCapsuleObj.cpp
+
+DESCRIPTION:	Collision Capsule Object Implementation
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+  V1.0 - Derived from 3ds max prim sphere  example
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#pragma warning( disable:4800 )
+#include <max.h>
+#include "MAX_Mem.h"
+#include <map>
+#include "NifProps.h"
+#include "iparamm.h"
+#include "Simpobj.h"
+#include "surf_api.h"
+#include "notify.h"
+#include "macroRec.h"
+#include "bhkRigidBodyInterface.h"
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+static void BuildScubaMesh(Mesh &mesh, int segs, int smooth, int llsegs, 
+                    float radius1, float radius2, Point3 cap1, Point3 cap2);
+
+const Class_ID BHKCAPSULEOBJECT_CLASS_ID = Class_ID(0x7f8f629a, 0x1d88470a);
+
+class bhkCapsuleObject : public SimpleObject, public IParamArray, public bhkRigidBodyIfcHelper
+{
+public:			
+   // Class vars
+   static IParamMap *pmapParam;
+   static float crtRadius;
+   static float crtRadius1;
+   static float crtRadius2;
+   static Point3 crtCapPos1;
+   static Point3 crtCapPos2;
+   static IObjParam *ip;
+
+   bhkCapsuleObject(BOOL loading);		
+   ~bhkCapsuleObject();		
+
+   // From Object
+   int CanConvertToType(Class_ID obtype);
+   Object* ConvertToType(TimeValue t, Class_ID obtype);
+   void GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist);
+
+   CreateMouseCallBack* GetCreateMouseCallBack();
+   void BeginEditParams( IObjParam  *ip, ULONG flags,Animatable *prev);
+   void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
+   RefTargetHandle Clone(RemapDir& remap = NoRemap());
+   TCHAR *GetObjectName() { return GetString(IDS_RB_Capsule); }
+
+   // Animatable methods		
+   void DeleteThis() {delete this;}
+   Class_ID ClassID() { return BHKCAPSULEOBJECT_CLASS_ID; } 
+   SClass_ID SuperClassID() { return HELPER_CLASS_ID; }
+
+   // From ReferenceTarget
+   IOResult Load(ILoad *iload);
+   IOResult Save(ISave *isave);
+
+   // From ref
+   int NumRefs() {return 2;}
+   RefTargetHandle GetReference(int i);
+   void SetReference(int i, RefTargetHandle rtarg);
+
+   // From SimpleObject
+   void BuildMesh(TimeValue t);
+   BOOL OKtoDisplay(TimeValue t);
+   void InvalidateUI();
+   ParamDimension *GetParameterDim(int pbIndex);
+   TSTR GetParameterName(int pbIndex);		
+
+   void UpdateUI();
+
+   BaseInterface* GetInterface(Interface_ID id);
+
+};
+
+class CapsuleObjCreateCallBack : public CreateMouseCallBack {
+   IPoint2 sp[4];
+   bhkCapsuleObject *ob;
+   Point3 p[4];
+public:
+   int proc( ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat);
+   void SetObj(bhkCapsuleObject *obj) {ob = obj;}
+};
+static CapsuleObjCreateCallBack CapsuleCreateCB;
+
+// Misc stuff
+#define MAX_SEGMENTS	200
+#define MIN_SEGMENTS	4
+
+#define MIN_RADIUS		float(0)
+#define MAX_RADIUS		float(1.0E30)
+
+#define MIN_SMOOTH		0
+#define MAX_SMOOTH		1
+
+#define DEF_SEGMENTS	32	// 16
+#define DEF_RADIUS		float(0.0)
+
+#define SMOOTH_ON	1
+#define SMOOTH_OFF	0
+
+#define MIN_SLICE	float(-1.0E30)
+#define MAX_SLICE	float( 1.0E30)
+
+
+//--- ClassDescriptor and class vars ---------------------------------
+
+static BOOL sInterfaceAdded = FALSE;
+
+// The class descriptor for Capsule
+class bhkCapsuleClassDesc : public ClassDesc2 
+{
+public:
+   int 			   IsPublic() { return 1; }
+   void *			Create(BOOL loading = FALSE)
+   {
+      AddInterface (GetbhkRigidBodyInterfaceDesc());
+      return new bhkCapsuleObject(loading);
+   }
+   const TCHAR *	ClassName() { return GetString(IDS_RB_CAPSULE_CLASS); }
+   SClass_ID		SuperClassID() { return HELPER_CLASS_ID; }
+   Class_ID		   ClassID() { return BHKCAPSULEOBJECT_CLASS_ID; }
+   const TCHAR* 	Category() { return "NifTools"; }
+   void			   ResetClassParams(BOOL fileReset);
+};
+
+static bhkCapsuleClassDesc CapsuleDesc;
+extern ClassDesc2* GetbhkCapsuleDesc() { return &CapsuleDesc; }
+
+// in prim.cpp  - The dll instance handle
+extern HINSTANCE hInstance;
+
+IParamMap *bhkCapsuleObject::pmapParam  = NULL;
+IObjParam *bhkCapsuleObject::ip         = NULL;
+float bhkCapsuleObject::crtRadius1      = 0.0f;
+float bhkCapsuleObject::crtRadius2      = 0.0f;
+Point3 bhkCapsuleObject::crtCapPos1;
+Point3 bhkCapsuleObject::crtCapPos2;
+
+
+void bhkCapsuleClassDesc::ResetClassParams(BOOL fileReset)
+{
+   bhkCapsuleObject::crtRadius1      = 0.0f;
+   bhkCapsuleObject::crtRadius2      = 0.0f;
+   bhkCapsuleObject::crtCapPos1.Set(0,0,0);
+   bhkCapsuleObject::crtCapPos2.Set(0,0,0);
+}
+
+
+//--- Parameter map/block descriptors -------------------------------
+
+// Parameter block indices
+enum CapsuleParamIndicies
+{
+   PB_RADIUS1,
+   PB_RADIUS2,
+   PB_CAP_POS1,
+   PB_CAP_POS2,
+};
+
+
+//
+//
+// Parameters
+
+static ParamUIDesc descParam[] = {
+   // Radius 1
+   ParamUIDesc(
+   PB_RADIUS1,
+   EDITTYPE_UNIVERSE,
+   IDC_RADIUS1,IDC_RADSPINNER1,
+   MIN_RADIUS,MAX_RADIUS,
+   SPIN_AUTOSCALE),	
+
+   // Radius 2
+   ParamUIDesc(
+   PB_RADIUS2,
+   EDITTYPE_UNIVERSE,
+   IDC_RADIUS2,IDC_RADSPINNER2,
+   MIN_RADIUS,MAX_RADIUS,
+   SPIN_AUTOSCALE),	
+
+   ParamUIDesc(
+   PB_CAP_POS1,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_POS1_X,IDC_SP_POS1_X,
+   IDC_ED_POS1_Y,IDC_SP_POS1_Y,
+   IDC_ED_POS1_Z,IDC_SP_POS1_Z,
+   float(-1.0E30),float(1.0E30),
+   SPIN_AUTOSCALE),
+
+   ParamUIDesc(
+   PB_CAP_POS2,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_POS2_X,IDC_SP_POS2_X,
+   IDC_ED_POS2_Y,IDC_SP_POS2_Y,
+   IDC_ED_POS2_Z,IDC_SP_POS2_Z,
+   float(-1.0E30),float(1.0E30),
+   SPIN_AUTOSCALE),
+
+};
+const int PARAMDESC_LENGTH = _countof(descParam);
+
+static ParamBlockDescID descVer0[] = {
+   { TYPE_FLOAT, NULL, TRUE, 0 },		
+   { TYPE_FLOAT, NULL, TRUE, 1 },		
+   { TYPE_POINT3, NULL, TRUE, 2 },
+   { TYPE_POINT3, NULL, TRUE, 3 } 
+};
+const int PBLOCK_LENGTH = _countof(descVer0);
+static ParamBlockDescID *curDescVer = descVer0;
+
+// Array of old versions
+//static ParamVersionDesc versions[] = {
+//   ParamVersionDesc(descVer0,_countof(descVer0),0),
+//};
+//const int NUM_OLDVERSIONS = _countof(versions);
+static ParamVersionDesc* versions = NULL;
+const int NUM_OLDVERSIONS = 0;
+
+// Current version
+const int CURRENT_VERSION = NUM_OLDVERSIONS + 1;
+static ParamVersionDesc curVersion(descVer0,_countof(descVer0),CURRENT_VERSION);
+
+class CapsuleParamDlgProc : public ParamMapUserDlgProc {
+public:
+   bhkCapsuleObject *so;
+   HWND thishWnd;
+
+   CapsuleParamDlgProc(bhkCapsuleObject *s) {so=s;thishWnd=NULL;}
+   BOOL DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
+   void Update(TimeValue t);
+   void DeleteThis() {delete this;}
+
+   //--- ParamDlgProc --------------------------------
+   void TurnSpinner(HWND hWnd,int SpinNum,BOOL ison)
+   {	
+      ISpinnerControl *spin2 = GetISpinner(GetDlgItem(hWnd,SpinNum));
+      if (ison) spin2->Enable();else spin2->Disable();
+      ReleaseISpinner(spin2);
+   };
+
+};
+
+void CapsuleParamDlgProc::Update(TimeValue t)
+{
+   if (!thishWnd) 
+      return;
+   return;
+}
+
+BOOL CapsuleParamDlgProc::DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+   thishWnd=hWnd;
+   switch (msg) 
+   {
+   case WM_INITDIALOG: 
+      {
+         Update(t);
+         break;
+      }
+   case WM_COMMAND:
+      //switch (LOWORD(wParam)) 
+      //{
+      //}
+      break;	
+   }
+   return FALSE;
+}
+
+//--- Capsule methods -------------------------------
+
+
+bhkCapsuleObject::bhkCapsuleObject(BOOL loading)
+{
+   SetAFlag(A_PLUGIN1);
+   MakeRefByID(FOREVER, 0, CreateParameterBlock(curDescVer, PBLOCK_LENGTH, CURRENT_VERSION));
+   assert(pblock);
+   MakeRefByID(FOREVER, 1, GetRBBlock());
+
+   pblock->SetValue(PB_RADIUS1,0,crtRadius1);
+   pblock->SetValue(PB_RADIUS2,0,crtRadius2);
+   pblock->SetValue(PB_CAP_POS1,0,crtCapPos1);
+   pblock->SetValue(PB_CAP_POS2,0,crtCapPos2);
+}
+
+bhkCapsuleObject::~bhkCapsuleObject() 
+{
+   CapsuleCreateCB.SetObj(NULL);
+   if (pmapParam) {
+      pmapParam->SetUserDlgProc(NULL);
+      DestroyCPParamMap(pmapParam);
+      pmapParam  = NULL;
+   }
+}
+
+#define NEWMAP_CHUNKID	0x0100
+
+IOResult bhkCapsuleObject::Load(ILoad *iload) 
+{
+   ClearAFlag(A_PLUGIN1);
+
+   IOResult res;
+   while (IO_OK==(res=iload->OpenChunk())) 
+   {
+      switch (iload->CurChunkID()) 
+      {	
+      case NEWMAP_CHUNKID:
+         SetAFlag(A_PLUGIN1);
+         break;
+      }
+      iload->CloseChunk();
+      if (res!=IO_OK)  return res;
+   }
+   return IO_OK;
+}
+
+IOResult bhkCapsuleObject::Save(ISave *isave)
+{
+   if (TestAFlag(A_PLUGIN1)) {
+      isave->BeginChunk(NEWMAP_CHUNKID);
+      isave->EndChunk();
+   }
+   return IO_OK;
+}
+
+RefTargetHandle bhkCapsuleObject::GetReference(int i) 
+{
+   if (i == 1) 
+      return GetRBBlock();
+   return pblock;
+}
+
+void bhkCapsuleObject::SetReference(int i, RefTargetHandle rtarg) 
+{
+   if (i == 1)
+      return;
+   pblock=(IParamBlock*)rtarg;
+}
+
+BaseInterface* bhkCapsuleObject::GetInterface(Interface_ID id)
+{
+   if (id == BHKRIGIDBODYINTERFACE_DESC)
+      return this;
+   return SimpleObject::GetInterface(id);
+}
+
+
+
+void bhkCapsuleObject::BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev)
+{
+   SimpleObject::BeginEditParams(ip,flags,prev);
+
+   // Gotta make a new one.
+   if (NULL == pmapParam) 
+   {
+      pmapParam = CreateCPParamMap(
+         descParam,PARAMDESC_LENGTH,
+         pblock,
+         ip,
+         hInstance,
+         MAKEINTRESOURCE(IDD_CAPSULEPARAM),
+         GetString(IDS_RB_PARAMETERS),
+         0);
+   }
+   this->ip = ip;
+
+   if(pmapParam) {
+      // A callback for the type in.
+      pmapParam->SetUserDlgProc(new CapsuleParamDlgProc(this));
+   }
+   BeginEditRBParams(ip, flags, prev);
+}
+
+void bhkCapsuleObject::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next )
+{		
+   SimpleObject::EndEditParams(ip,flags,next);
+   this->ip = NULL;
+
+   if (pmapParam && flags&END_EDIT_REMOVEUI ) {
+      pmapParam->SetUserDlgProc(NULL);
+      DestroyCPParamMap(pmapParam);
+      pmapParam  = NULL;
+   }
+   EndEditRBParams(ip, flags, next);
+}
+
+void bhkCapsuleObject::BuildMesh(TimeValue t)
+{
+   int segs = 12;
+   int hsegs = 1;
+   int smooth = 1;
+
+   // Start the validity interval at forever and widdle it down.
+   //FixHeight(pblock,t,(pmapParam?pmapParam->GetHWnd():NULL),increate);
+   ivalid = FOREVER;
+
+   float radius1, radius2;
+   Point3 pos1, pos2;
+   pblock->GetValue(PB_RADIUS1,t,radius1,ivalid);
+   pblock->GetValue(PB_RADIUS2,t,radius2,ivalid);
+   pblock->GetValue(PB_CAP_POS1,t,pos1,ivalid);
+   pblock->GetValue(PB_CAP_POS2,t,pos2,ivalid);
+   LimitValue(radius1, MIN_RADIUS, MAX_RADIUS);
+   LimitValue(radius2, MIN_RADIUS, MAX_RADIUS);
+   if (radius2 == MIN_RADIUS) radius2 = radius1;
+
+   if (radius1 == 0)
+   {
+      mesh.setNumVerts(0);
+      mesh.setNumFaces(0);
+      mesh.setNumTVerts(0);
+      mesh.setNumTVFaces(0);
+      mesh.setSmoothFlags(smooth != 0);
+   }
+   else
+   {
+      BuildScubaMesh(mesh, segs, smooth, hsegs, radius2, radius1, pos2, pos1);
+   }
+}
+
+Object* bhkCapsuleObject::ConvertToType(TimeValue t, Class_ID obtype)
+{
+   return 0;
+   //return SimpleObject::ConvertToType(t,obtype);
+}
+
+int bhkCapsuleObject::CanConvertToType(Class_ID obtype)
+{
+   return 0;
+}
+
+void bhkCapsuleObject::GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist)
+{
+   Object::GetCollapseTypes(clist, nlist);
+}
+
+int CapsuleObjCreateCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ) 
+{
+   float r;
+   Point3 center;
+   if (ob == NULL)
+   {
+      return CREATE_ABORT;
+   }
+
+   if (msg == MOUSE_FREEMOVE)
+   {
+      vpt->SnapPreview(m,m,NULL, SNAP_IN_3D);
+   }
+
+   if (msg==MOUSE_POINT||msg==MOUSE_MOVE) 
+   {
+      switch(point) 
+      {
+      case 0:  // only happens with MOUSE_POINT msg
+         ob->pblock->SetValue(PB_RADIUS1,0,0.0f);
+         ob->pblock->SetValue(PB_RADIUS2,0,0.0f);
+         ob->suspendSnap = TRUE;				
+         sp[0] = m;
+         p[0] = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D);
+         ob->pblock->SetValue(PB_CAP_POS1,0,p[0]);
+         ob->pblock->SetValue(PB_CAP_POS2,0,p[0]);
+         mat.SetTrans(p[0]);
+         break;
+
+      case 1: // Fix radius of first cap
+         mat.IdentityMatrix();
+         //mat.PreRotateZ(HALFPI);
+         sp[1] = m;
+         p[1] = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D);
+         r = Length(p[1]-p[0]);
+         mat.SetTrans(p[0]);
+
+         ob->pblock->SetValue(PB_RADIUS1,0,r);
+         ob->pblock->SetValue(PB_RADIUS2,0,r);
+         ob->pmapParam->Invalidate();
+
+         if (flags&MOUSE_CTRL) 
+         {
+            float ang = (float)atan2(p[1].y-p[0].y,p[1].x-p[0].x);					
+            mat.PreRotateZ(ob->ip->SnapAngle(ang));
+         }
+         if (msg==MOUSE_POINT) 
+         {
+            if (Length(m-sp[0])<3 || Length(p[1]-p[0])<0.1f) {
+               ob->suspendSnap = FALSE;
+               return CREATE_ABORT;
+            }
+         }
+         break;
+
+      case 2: // Get second point
+         mat.IdentityMatrix();
+         //mat.PreRotateZ(HALFPI);
+         sp[2] = m;
+         p[2] = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D);
+         r = Length(p[1]-p[0]);
+         mat.SetTrans(p[0]);
+
+         ob->pblock->SetValue(PB_CAP_POS2,0,p[2]);
+         ob->pmapParam->Invalidate();
+
+         //if (flags&MOUSE_CTRL) 
+         //{
+         //   float ang = (float)atan2(p1.y-p[0].y,p1.x-p[0].x);					
+         //   mat.PreRotateZ(ob->ip->SnapAngle(ang));
+         //}
+         //if (msg==MOUSE_POINT) 
+         //{
+         //   ob->suspendSnap = FALSE;
+         //   return (Length(m-sp[0])<3 || Length(p1-p[0])<0.1f)?CREATE_ABORT:CREATE_STOP;
+         //}
+         break;
+
+      case 3: // Get second point
+         mat.IdentityMatrix();
+         //mat.PreRotateZ(HALFPI);
+         sp[3] = m;
+         p[3] = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D);
+         if (flags&MOUSE_CTRL) // ignore radius
+         {
+            r = Length(p[1]-p[0]);
+            ob->pblock->SetValue(PB_RADIUS2,0,r);
+         }
+         else
+         {
+            // start radius at r1
+            r = Length((p[3]-p[2]) + (p[1]-p[0]));
+            ob->pblock->SetValue(PB_RADIUS2,0,r);
+         }
+         ob->pmapParam->Invalidate();
+         if (msg==MOUSE_POINT) 
+         {
+            ob->suspendSnap = FALSE;
+            return CREATE_STOP;
+         }
+         break;
+      }
+   }
+   else if (msg == MOUSE_ABORT) 
+   {		
+      ob->suspendSnap = FALSE;
+      return CREATE_ABORT;
+   }
+   return TRUE;
+}
+
+CreateMouseCallBack* bhkCapsuleObject::GetCreateMouseCallBack() 
+{
+   CapsuleCreateCB.SetObj(this);
+   return(&CapsuleCreateCB);
+}
+
+
+BOOL bhkCapsuleObject::OKtoDisplay(TimeValue t) 
+{
+   float radius;
+   pblock->GetValue(PB_RADIUS1,t,radius,FOREVER);
+   if (radius==0.0f) return FALSE;
+   else return TRUE;
+}
+
+void bhkCapsuleObject::InvalidateUI() 
+{
+   if (pmapParam) pmapParam->Invalidate();
+}
+
+ParamDimension *bhkCapsuleObject::GetParameterDim(int pbIndex) 
+{
+   switch (pbIndex) 
+   {
+   case PB_RADIUS1:
+      return stdWorldDim;			
+   case PB_RADIUS2:
+      return stdWorldDim;			
+   case PB_CAP_POS1:
+      return stdWorldDim;			
+   case PB_CAP_POS2:
+      return stdWorldDim;			
+   default:
+      return defaultDim;
+   }
+}
+
+TSTR bhkCapsuleObject::GetParameterName(int pbIndex) 
+{
+   switch (pbIndex) 
+   {
+   case PB_RADIUS1:
+      return TSTR(GetString(IDS_RB_RADIUS1));			
+   case PB_RADIUS2:
+      return TSTR(GetString(IDS_RB_RADIUS2));			
+   case PB_CAP_POS1:
+      return TSTR(GetString(IDS_RB_CAP_POS1));			
+   case PB_CAP_POS2:
+      return TSTR(GetString(IDS_RB_CAP_POS2));			
+   default:
+      return TSTR(_T(""));
+   }
+}
+
+RefTargetHandle bhkCapsuleObject::Clone(RemapDir& remap) 
+{
+   bhkCapsuleObject* newob = new bhkCapsuleObject(FALSE);	
+   newob->ReplaceReference(0,remap.CloneRef(pblock));
+   newob->ivalid.SetEmpty();	
+   BaseClone(this, newob, remap);
+   return(newob);
+}
+
+void bhkCapsuleObject::UpdateUI()
+{
+   if (ip == NULL)
+      return;
+   CapsuleParamDlgProc* dlg = static_cast<CapsuleParamDlgProc*>(pmapParam->GetUserDlgProc());
+   dlg->Update(ip->GetTime());
+}
+
+void AddFace(Face *f,int a,int b,int c,int evis,int smooth_group)
+{ 
+   const int ALLF = 4;
+   f[0].setSmGroup(smooth_group);
+   f[0].setMatID((MtlID)0); 	 /*default */
+   if (evis==0) f[0].setEdgeVisFlags(1,1,0);
+   else if (evis==1) f[0].setEdgeVisFlags(0,1,1);
+   else if (evis==2) f[0].setEdgeVisFlags(0,0,1);
+   else if (evis==ALLF) f[0].setEdgeVisFlags(1,1,1);
+   else f[0].setEdgeVisFlags(1,0,1);	
+   f[0].setVerts(a,b,c);
+}
+
+void BuildScubaMesh(Mesh &mesh, int segs, int smooth, int llsegs, 
+                    float radius1, float radius2, Point3 cap1, Point3 cap2)
+{
+   Point3 p;
+   int ix,jx,ic = 1;
+   int nf=0,nv=0, capsegs=(int)(segs/2.0f),csegs=0;
+   float ang;	
+   float startAng = 0.0f;	
+   float totalPie = TWOPI;
+   int lsegs = llsegs-1 + 2*capsegs;
+   int levels=csegs*2+(llsegs-1);
+   int capv=segs,sideedge=capsegs+csegs;
+   int totlevels=levels+capsegs*2+2;
+   int tvinslice=totlevels+totlevels-2;
+   float delta = (float)2.0*PI/(float)segs;
+   int VertexPerLevel=segs;
+   int nfaces=2*segs*(levels+1);
+   int ntverts=2*(segs+1)+llsegs-1;
+   int *edgelstl=new int[totlevels];
+   int *edgelstr=new int[totlevels];
+   int lastlevel=totlevels-1,dcapv=capv-1,dvertper=VertexPerLevel-1;
+   edgelstr[0] = edgelstl[0] = 0;
+   edgelstr[1] = 1;
+   edgelstl[1] = capv;
+   for (int i=2;i<=sideedge;i++){ 
+      edgelstr[i]=edgelstr[i-1]+capv;
+      edgelstl[i]=edgelstr[i]+dcapv;
+   }
+   while ((i<lastlevel)&&(i<=totlevels-sideedge)){ 
+      edgelstr[i]=edgelstr[i-1]+VertexPerLevel;
+      edgelstl[i]=edgelstr[i]+dcapv;
+      i++;
+   }
+   while (i<lastlevel) { 
+      edgelstr[i]=edgelstr[i-1]+capv;
+      edgelstl[i]=edgelstr[i]+dcapv;
+      i++;
+   }
+   edgelstl[lastlevel]= (edgelstr[lastlevel]=edgelstl[i-1]+1);
+   int nverts=edgelstl[lastlevel]+1;
+   nfaces+=2*segs*(2*capsegs-1);
+
+   mesh.setNumVerts(nverts);
+   mesh.setNumFaces(nfaces);
+   mesh.setSmoothFlags(smooth != 0);
+   mesh.setNumTVerts(0);
+   mesh.setNumTVFaces(0);
+   mesh.setSmoothFlags(smooth != 0);
+
+   // bottom vertex 
+   float cylh = (cap1 - cap2).Length();
+   float height = cylh + radius1 + radius2;
+   mesh.setVert(nv, Point3(0.0f,0.0f,height));
+   mesh.setVert(nverts-1, Point3(0.0f,0.0f,0.0f));		
+
+   // Top (1) and bottom (2) cap vertices
+   float ru,cang,sang;
+   int msegs=segs,deltaend=nverts-capv-1;
+   ang = startAng;	 
+   msegs--;
+   float rincr=PI/(2.0f*capsegs),aincr;
+   for (jx = 0; jx<=msegs; jx++) 
+   {
+      cang=(float)cos(ang);
+      sang=(float)sin(ang);
+      for(ix=1; ix<=sideedge; ix++) {
+         aincr = (rincr*(float)ix);
+         ru=(float)sin(aincr);
+
+         p.x = cang*radius1*ru;
+         p.y = sang*radius1*ru;	
+         p.z = (jx==0) ? height-radius1*(1.0f-(float)cos(aincr)) : mesh.verts[edgelstr[ix]].z;
+         mesh.setVert(edgelstr[ix]+jx, p);
+
+         p.x = cang*radius2*ru;
+         p.y = sang*radius2*ru;	
+         p.z = (jx==0) ? radius2*(1.0f-(float)cos(aincr)) : mesh.verts[edgelstr[lastlevel-ix]].z ;
+         mesh.setVert(edgelstr[lastlevel-ix]+jx,p);
+      }
+      ang += delta;
+   }
+
+   //// Middle vertices 
+   //int sidevs,startv=edgelstr[sideedge],deltav;				
+   //for(ix=1; ix<llsegs; ix++) {
+   //   // Put center vertices all the way up
+   //   float   u = float(ix)/float(llsegs);
+   //   float rad = (radius1*(1.0f-u) + radius2*u);
+   //   p.z = cylh *((float)ix/float(llsegs)) + radius2;
+   //   ang = startAng;
+   //   for (sidevs=0;sidevs<VertexPerLevel;sidevs++)
+   //      p.x = (float)cos(ang)*rad;
+   //      p.y = (float)sin(ang)*rad;
+   //      mesh.setVert(nv, p);
+   //      nv++;
+   //      ang += delta;
+   //   }	
+   //}
+
+   //top layer done, now reflect sides down 
+   int sidevs,deltav;
+   int startv=edgelstr[sideedge];
+   int endv=edgelstr[totlevels-capsegs-1]; 
+   if (llsegs>1)
+   {
+      float sincr = cylh/llsegs;
+      for (sidevs=0;sidevs<VertexPerLevel;sidevs++)
+      {
+         Point3 topp = mesh.verts[startv];
+         Point3 botp = mesh.verts[endv];
+         p.x = (topp.x + botp.x) /  2.0f;
+         p.y = (topp.y + botp.y) /  2.0f;
+         deltav=VertexPerLevel;
+         for (ic=1;ic<llsegs;ic++)
+         {
+            p.z = topp.z-sincr*ic;
+            mesh.setVert(startv+deltav, p);
+            deltav+=VertexPerLevel;
+         }
+         startv++;
+      }
+   }
+   int lasttvl=0,lasttvr=0;
+   int lvert=segs;
+   int t0,t1,b0,b1,tvt0=0,tvt1=0,tvb0=1,tvb1=2,fc=0,smoothgr=(smooth?4:0),vseg=segs+1;
+   int tvcount=0,lowerside=lastlevel-sideedge,onside=0;
+
+   BOOL ok,wrap;
+   // Now make faces ---
+   for (int clevel=0;clevel<lastlevel-1;clevel++)
+   {
+      t1=(t0=edgelstr[clevel])+1;
+      b1=(b0=edgelstr[clevel+1])+1;
+      ok=1; wrap=FALSE;
+      if ((clevel>0)&&(onside==1)) {
+         tvt0++;tvt1++;tvb0++,tvb1++;
+      }
+      if (clevel==1) {
+         tvt0=1;tvt1=2;
+      }
+      if (clevel==sideedge) {
+         tvt1+=lvert;tvt0+=lvert;tvb0+=vseg;tvb1+=vseg;onside++;
+      } else if (clevel==lowerside) {
+         tvt1+=vseg;tvt0+=vseg;tvb0+=lvert;tvb1+=lvert;onside++;
+      }
+      while ((b0<edgelstl[clevel+1])||ok)
+      {
+         if (b1==edgelstr[clevel+2]) {
+            b1=edgelstr[clevel+1]; 
+            t1=edgelstr[clevel];
+            ok=FALSE;
+            wrap=(onside!=1);
+         }
+         if (smooth) smoothgr=4;
+         AddFace(&mesh.faces[fc++],t0,b0,b1,0,smoothgr);
+         if (clevel>0) {
+            AddFace(&mesh.faces[fc++],t0,b1,t1,1,smoothgr);
+            t0++;t1++;
+         }
+         b0++;b1++;tvb0++,tvb1++;
+      }
+   }
+   smoothgr=(smooth?4:0);
+   t1=(t0=edgelstr[lastlevel-1])+1;b0=edgelstr[lastlevel];
+   int lastpt=lastlevel;
+   if (onside==1) {
+      tvt0++;
+      tvt1++;
+      tvb0++;
+      tvb1++;
+   }
+   if (sideedge==1) {
+      tvt1+=vseg;
+      tvt0+=vseg;
+      tvb0+=lvert;
+      tvb1+=lvert;
+      onside++;
+   }
+   while (t0<edgelstl[lastpt]) {
+      if (t1==edgelstr[lastlevel]) {
+         t1=edgelstr[lastlevel-1];
+         tvt1-=segs;
+      }
+      AddFace(&mesh.faces[fc++],t0,b0,t1,1,smoothgr);
+      t0++;t1++;
+   }
+   for (i=0;i<nverts;i++) 
+      mesh.verts[i].z -= radius2;
+
+   if (edgelstr) delete []edgelstr;
+   if (edgelstl) delete []edgelstl;
+   assert(fc==mesh.numFaces);
+   //	assert(nv==mesh.numVerts);
+   mesh.InvalidateTopologyCache();
+}
+
+#if 0
+void BuildCylinderMesh(Mesh &mesh,
+                       int segs, int smooth, int llsegs, int capsegs, 
+                       float radius1, float radius2, float height, 
+                       int doPie, float pie1, float pie2)
+{
+   Point3 p;
+   int ix,na,nb,nc,nd,jx,kx, ic = 1;
+   int nf=0,nv=0, lsegs;
+   float delta,ang, u;	
+   float totalPie, startAng = 0.0f;	
+
+   lsegs = llsegs-1 + 2*capsegs;
+
+   // Make pie2 < pie1 and pie1-pie2 < TWOPI
+   while (pie1 < pie2) pie1 += TWOPI;
+   while (pie1 > pie2+TWOPI) pie1 -= TWOPI;
+   if (pie1==pie2) totalPie = TWOPI;
+   else totalPie = pie1-pie2;	
+
+   delta = (float)2.0*PI/(float)segs;
+
+   if (height<0) delta = -delta;
+
+   int nverts;
+   int nfaces;
+   nverts = 2+segs*(lsegs);
+   nfaces = 2*segs*(lsegs);	
+   mesh.setNumVerts(nverts);
+   mesh.setNumFaces(nfaces);
+   mesh.setSmoothFlags(smooth != 0);
+   mesh.setNumTVerts(0);
+   mesh.setNumTVFaces(0);
+
+   // bottom vertex 
+   mesh.setVert(nv, Point3(0.0,0.0,0.0));
+   nv++;
+
+   // Bottom cap vertices	
+   for(ix=0; ix<capsegs; ix++) {
+
+      // Put center vertices all the way up
+      p.z = 0.0f;
+      u   = float(ix+1)/float(capsegs);
+      ang = startAng;
+      for (jx = 0; jx<segs; jx++) {			
+         p.x = (float)cos(ang)*radius1*u;
+         p.y = (float)sin(ang)*radius1*u;	
+         mesh.setVert(nv, p);
+         nv++;
+         ang += delta;
+      }	
+   }
+
+   // Middle vertices 
+   for(ix=1; ix<llsegs; ix++) {
+
+      // Put center vertices all the way up
+      float   u = float(ix)/float(llsegs);
+      float rad = (radius1*(1.0f-u) + radius2*u);
+      p.z = height*((float)ix/float(llsegs));
+      ang = startAng;
+      for (jx = 0; jx<segs; jx++) {
+         p.x = (float)cos(ang)*rad;
+         p.y = (float)sin(ang)*rad;
+         mesh.setVert(nv, p);
+         nv++;
+         ang += delta;
+      }	
+   }
+
+   // Top cap vertices	
+   for(ix=0; ix<capsegs; ix++) {
+
+      // Put center vertices all the way up
+      p.z = height;
+      u   = 1.0f-float(ix)/float(capsegs);
+      ang = startAng;
+      for (jx = 0; jx<segs; jx++) {			
+         p.x = (float)cos(ang)*radius2*u;		
+         p.y = (float)sin(ang)*radius2*u;	
+         mesh.setVert(nv, p);
+         nv++;
+         ang += delta;
+      }	
+   }
+
+   /* top vertex */
+   mesh.setVert(nv, (float)0.0, (float)0.0, height);
+   nv++;
+
+   // Now make faces ---
+
+   BitArray startSliceFaces;
+   BitArray endSliceFaces;
+   BitArray topCapFaces;
+   BitArray botCapFaces;
+
+   // Make bottom cap		
+
+   for(ix=1; ix<=segs; ++ix) {
+      nc=(ix==segs)?1:ix+1;
+      mesh.faces[nf].setEdgeVisFlags(capsegs>1,1,capsegs>1);
+      mesh.faces[nf].setSmGroup(1);
+      mesh.faces[nf].setVerts(0,nc,ix);
+      mesh.faces[nf].setMatID(1);
+      nf++;
+   }
+
+   int topCapStartFace = 0;
+
+   /* Make midsection */
+   for(ix=0; ix<lsegs-1; ++ix) {
+      jx=ix*segs+1;
+      for(kx=0; kx<segs; ++kx) {
+         DWORD grp = 0;
+         int mtlid;
+         BOOL inSlice = FALSE;
+
+         if (kx==segs) {
+            mtlid = 4;
+            grp = (1<<2);
+            inSlice = TRUE;
+         } else if (ix < capsegs-1 || ix >= capsegs+llsegs-1) {
+            grp = 1;
+            mtlid = (ix<capsegs-1)?0:1;
+         } else {		
+            mtlid = 2;
+            if (smooth) {				
+               grp = (1<<3);	
+            }			
+         }
+
+         na = jx+kx;
+         nb = na+segs;
+         nc = (kx==(segs-1))? jx+segs: nb+1;
+         nd = (kx==(segs-1))? jx : na+1;			
+
+         mesh.faces[nf].setEdgeVisFlags(0,!inSlice,1);
+         mesh.faces[nf].setSmGroup(grp);
+         mesh.faces[nf].setVerts(na,nc,nb);
+         mesh.faces[nf].setMatID(mtlid);
+         nf++;
+
+         mesh.faces[nf].setEdgeVisFlags(!inSlice,1,0);
+         mesh.faces[nf].setSmGroup(grp);
+         mesh.faces[nf].setVerts(na,nd,nc);
+         mesh.faces[nf].setMatID(mtlid);
+         nf++;
+      }
+   }
+
+   //Make Top cap 			
+   na = mesh.getNumVerts()-1;	
+   jx = (lsegs-1)*segs+1;
+
+   for(ix=0; ix<segs; ++ix) {
+      nb = jx+ix;
+      nc = (ix==segs-1)? jx: nb+1;		
+      mesh.faces[nf].setEdgeVisFlags(capsegs>1,1,capsegs>1);		
+      mesh.faces[nf].setSmGroup( 1);
+      mesh.faces[nf].setVerts(na,nb,nc);
+      mesh.faces[nf].setMatID(0);
+      nf++;
+   }
+
+   assert(nf==mesh.numFaces);
+   assert(nv==mesh.numVerts);
+   mesh.InvalidateTopologyCache();
+}
+#endif
\ No newline at end of file
diff --git a/NifProps/bhkRigidBodyInterface.cpp b/NifProps/bhkRigidBodyInterface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..24c62f144ea535eaba48d5a8642b6fda8e71f104
--- /dev/null
+++ b/NifProps/bhkRigidBodyInterface.cpp
@@ -0,0 +1,556 @@
+/**********************************************************************
+*<
+FILE: bhkRigidBodyInterface.cpp
+
+DESCRIPTION:	Collision RigidBody Object Implementation
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+  V1.0 - Derived from 3ds max prim sphere example
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#pragma warning( disable:4800 )
+#include <max.h>
+#include "MAX_Mem.h"
+#include <map>
+#include "NifProps.h"
+#include "iparamm.h"
+#include "Simpobj.h"
+#include "notify.h"
+#include "macroRec.h"
+#include "bhkRigidBodyInterface.h"
+#include "NifPlugins.h"
+#include "NifGui.h"
+#include "NifStrings.h"
+//--- Parameter map/block descriptors -------------------------------
+
+
+FPInterfaceDesc thebhkRigidBodyInterface(
+   BHKRIGIDBODYINTERFACE_DESC, _T("bhkRigidBody"), -1/*IDS_OPS*/, 0, FP_CORE,
+   properties,
+      bhkRigidBodyInterface::get_material, bhkRigidBodyInterface::set_material, _T("Material"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_material,
+      bhkRigidBodyInterface::get_layer, bhkRigidBodyInterface::set_layer, _T("Layer"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_layer,
+      bhkRigidBodyInterface::get_mass, bhkRigidBodyInterface::set_mass, _T("Mass"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_friction, bhkRigidBodyInterface::set_friction, _T("Friction"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_restitution, bhkRigidBodyInterface::set_restitution, _T("Restitution"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_lineardamping, bhkRigidBodyInterface::set_lineardamping, _T("LinearDamping"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_angulardamping, bhkRigidBodyInterface::set_angulardamping, _T("AngularDamping"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_maxlinearvelocity, bhkRigidBodyInterface::set_maxlinearvelocity, _T("MaxLinearVelocity"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_maxangularvelocity, bhkRigidBodyInterface::set_maxangularvelocity, _T("MaxAngularVelocity"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_penetrationdepth, bhkRigidBodyInterface::get_penetrationdepth, _T("PenetrationDepth"), 0, TYPE_FLOAT,
+      bhkRigidBodyInterface::get_motionsystem, bhkRigidBodyInterface::set_motionsystem, _T("MotionSystem"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_motionsystem,
+      bhkRigidBodyInterface::get_qualitytype, bhkRigidBodyInterface::set_qualitytype, _T("QualityType"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_qualitytype,
+   enums,
+      bhkRigidBodyInterface::enum_material, 31,
+         "Stone", 0,
+         "Cloth", 1,
+         "Dirt", 2,
+         "Glass", 3,
+         "Grass", 4,
+         "Metal", 5,
+         "Organic", 6,
+         "Skin", 7,
+         "Water", 8,
+         "Wood", 9,
+         "Heavy Stone", 10, 
+         "Heavy Metal", 11,
+         "Heavy Wood", 12,
+         "Chain", 13,
+         "Snow", 14,
+         "Stone Stairs", 15,
+         "Cloth Stairs", 16,
+         "Dirt Stairs", 17,
+         "Glass Stairs", 18,
+         "Grass Stairs", 19,
+         "Metal Stairs", 20,
+         "Organic Stairs", 21,
+         "Skin Stairs", 22,
+         "Water Stairs", 23,
+         "Wood Stairs", 24,
+         "Heavy Stone Stairs", 25,
+         "Heavy Metal Stairs", 26,
+         "Heavy Wood Stairs", 27,
+         "Chain Stairs", 28,
+         "Snow Stairs", 29,
+         "Elevator", 30,
+      bhkRigidBodyInterface::enum_layer, 57,
+         "Unidentified", 0,
+         "Static", 1,
+         "AnimStatic", 2,
+         "Transparent", 3,
+         "Clutter", 4,
+         "Weapon", 5,
+         "Projectile", 6,
+         "Spell", 7,
+         "Biped", 8,
+         "Tree", 9,
+         "Prop", 10,
+         "Water", 11,
+         "Trigger", 12,
+         "Terrain", 13,
+         "Trap", 14,
+         "NonCollidable", 15,
+         "CloudTrap", 16,
+         "Ground", 17,
+         "Portal", 18,
+         "Stairs", 19,
+         "CharController", 20,
+         "AvoidBox", 21,
+         "Unknown(22)", 22,
+         "Unknown(23)", 23,
+         "CameraPick", 24,
+         "ItemPick", 25,
+         "LineOfSight", 26,
+         "PathPick", 27,
+         "CustomPick1", 28,
+         "CustomPick2", 29,
+         "SpellExplosion", 30,
+         "DroppingPick", 31,
+         "Other", 32,
+         "Head", 33,
+         "Body", 34,
+         "Spine1", 35,
+         "Spine2", 36,
+         "LUpperArm", 37,
+         "LForeArm", 38,
+         "LHand", 39,
+         "LThigh", 40,
+         "LCalf", 41,
+         "LFoot", 42,
+         "RUpperArm", 43,
+         "RForeArm", 44,
+         "RHand", 45,
+         "RThigh", 46,
+         "RCalf", 47,
+         "RFoot", 48,
+         "Tail", 49,
+         "SideWeapon", 50,
+         "Shield", 51,
+         "Quiver", 52,
+         "BackWeapon", 53,
+         "BackWeapon", 54,
+         "PonyTail", 55,
+         "Wing", 56,
+      bhkRigidBodyInterface::enum_motionsystem, 10,
+         "Keyframed(0)", 0,
+         "Box(1)", 1,
+         "Sphere(2)", 2,
+         "Sphere(2)", 3,
+         "Box(4)", 4,
+         "Box(5)", 5,
+         "Keyframed(6)", 6,
+         "Keyframed(7)", 7,
+         "Box(8)", 8,
+         "Keyframed(9)", 9,
+      bhkRigidBodyInterface::enum_qualitytype, 9,
+         "Moving", 0,
+         "Fixed", 1,
+         "Keyframed", 2,
+         "Moving(3)", 3,
+         "Moving(4)", 4,
+         "Critical", 5,
+         "Bullet", 6,
+         "User", 7,
+         "Null", 8,
+   end);
+
+FPInterfaceDesc *bhkRigidBodyInterface::GetDesc() {
+   return &thebhkRigidBodyInterface;
+}
+
+FPInterfaceDesc* GetbhkRigidBodyInterfaceDesc()
+{
+   return &thebhkRigidBodyInterface;
+}
+
+//
+//
+// Parameters
+
+static ParamUIDesc descRigidBodyParam[] = {
+
+   // Mass
+   ParamUIDesc(
+   PB_RB_MASS,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_MASS,IDC_SP_MASS,
+   0, 1000,
+   SPIN_AUTOSCALE),	
+
+   // Friction
+   ParamUIDesc(
+   PB_RB_FRICTION,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_FRICTION,IDC_SP_FRICTION,
+   0, 10,
+   SPIN_AUTOSCALE),
+
+   // Restitution
+   ParamUIDesc(
+   PB_RB_RESTITUTION,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_RESTITUTION,IDC_SP_RESTITUTION,
+   0, 10,
+   SPIN_AUTOSCALE),
+
+   // Linear Damping
+   ParamUIDesc(
+   PB_RB_LINEAR_DAMPING,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_LINEAR_DAMPING,IDC_SP_LINEAR_DAMPING,
+   0, 10,
+   SPIN_AUTOSCALE),
+
+   // Angular Damping
+   ParamUIDesc(
+   PB_RB_ANGULAR_DAMPING,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_ANGULAR_DAMPING,IDC_SP_ANGULAR_DAMPING,
+   0, 10,
+   SPIN_AUTOSCALE),
+
+   // Max Linear Velocity
+   ParamUIDesc(
+   PB_RB_MAX_LINEAR_VELOCITY,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_MAX_LINEAR_VELOCITY,IDC_SP_MAX_LINEAR_VELOCITY,
+   0, 10,
+   SPIN_AUTOSCALE),
+
+   // Max Angular Velocity
+   ParamUIDesc(
+   PB_RB_MAX_ANGULAR_VELOCITY,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_MAX_ANGULAR_VELOCITY,IDC_SP_MAX_ANGULAR_VELOCITY,
+   0, 10,
+   SPIN_AUTOSCALE),
+
+   // Penetration Depth
+   ParamUIDesc(
+   PB_RB_PENETRATION_DEPTH,
+   EDITTYPE_UNIVERSE,
+   IDC_ED_PENETRATION_DEPTH,IDC_SP_PENETRATION_DEPTH,
+   0, 10,
+   SPIN_AUTOSCALE),
+};
+const int descRigidBodyParamLength = _countof(descRigidBodyParam);
+
+static ParamBlockDescID gRigidBlockParamDesc[] = {
+   { TYPE_INT, NULL, FALSE, PB_RB_MATERIAL },
+   { TYPE_INT, NULL, FALSE, PB_RB_LAYER },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_MASS },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_FRICTION },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_RESTITUTION },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_LINEAR_DAMPING },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_ANGULAR_DAMPING },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_MAX_LINEAR_VELOCITY },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_MAX_ANGULAR_VELOCITY },
+   { TYPE_FLOAT, NULL, FALSE, PB_RB_PENETRATION_DEPTH },
+   { TYPE_INT, NULL, FALSE, PB_RB_MOTION_SYSTEM },
+   { TYPE_INT, NULL, FALSE, PB_RB_QUALITY_TYPE },
+};
+const int descRigidBodyDescIDLength = _countof(gRigidBlockParamDesc);
+
+class RigidBodyParamDlgProc : public ParamMapUserDlgProc {
+public:
+   bhkRigidBodyInterface *so;
+   HWND thishWnd;
+   NpComboBox		mCbLayer;
+   NpComboBox		mCbMaterial;
+   NpComboBox		mCbMotionSystem;
+   NpComboBox		mCbQualityType;
+
+   RigidBodyParamDlgProc(bhkRigidBodyInterface *s) {so=s;thishWnd=NULL;}
+   BOOL DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
+   void Update(TimeValue t);
+   void DeleteThis() {delete this;}
+
+   //--- ParamDlgProc --------------------------------
+   void TurnSpinner(HWND hWnd,int SpinNum,BOOL ison)
+   {	
+      ISpinnerControl *spin2 = GetISpinner(GetDlgItem(hWnd,SpinNum));
+      if (ison) spin2->Enable();else spin2->Disable();
+      ReleaseISpinner(spin2);
+   };
+
+};
+
+void RigidBodyParamDlgProc::Update(TimeValue t)
+{
+   if (!thishWnd) 
+      return;
+
+   
+   mCbMaterial.select(max(0, min(so->GetMaterial(t), mCbMaterial.count()-1)));
+   mCbLayer.select(max(0, min(so->GetLayer(t), mCbLayer.count()-1)));
+   mCbMotionSystem.select(max(0, min(so->GetMotionSystem(t), mCbMotionSystem.count()-1)));
+   mCbQualityType.select(max(0, min(so->GetQualityType(t), mCbQualityType.count()-1)));
+   return;
+}
+
+BOOL RigidBodyParamDlgProc::DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+   thishWnd=hWnd;
+   switch (msg) 
+   {
+   case WM_INITDIALOG: 
+      {
+         mCbMaterial.init(GetDlgItem(hWnd, IDC_CB_MATERIAL));
+         for (const char **str = NpHvkMaterialNames; *str; ++str)
+            mCbMaterial.add(*str);
+
+         mCbLayer.init(GetDlgItem(hWnd, IDC_CB_LAYER));
+         for (const char **str = NpHvkLayerNames; *str; ++str)
+            mCbLayer.add(*str);
+
+         mCbMotionSystem.init(GetDlgItem(hWnd, IDC_CB_MOTION_SYSTEM));         
+         for (const char **str = NpHvkMotionSystems; *str; ++str)
+            mCbMotionSystem.add(*str);
+
+         mCbQualityType.init(GetDlgItem(hWnd, IDC_CB_QUALITY_TYPE));
+         for (const char **str = NpHvkQualityTypes; *str; ++str)
+            mCbQualityType.add(*str);
+
+         Update(t);
+         break;
+      }
+   case WM_COMMAND:
+      switch (LOWORD(wParam))
+      {
+      case IDC_CB_MATERIAL:
+         if (HIWORD(wParam)==CBN_SELCHANGE) {
+            so->SetMaterial( mCbMaterial.selection(), t );
+         }
+         break;
+
+      case IDC_CB_LAYER:
+         if (HIWORD(wParam)==CBN_SELCHANGE) {
+            so->SetLayer( mCbLayer.selection(), t );
+         }
+         break;
+
+      case IDC_CB_MOTION_SYSTEM:
+         if (HIWORD(wParam)==CBN_SELCHANGE) {
+            so->SetMotionSystem( mCbMotionSystem.selection(), t );
+         }
+         break;
+
+      case IDC_CB_QUALITY_TYPE:
+         if (HIWORD(wParam)==CBN_SELCHANGE) {
+            so->SetQualityType( mCbQualityType.selection(), t );
+         }
+         break;
+      }
+      break;
+   }
+   return FALSE;
+}
+
+//--- RigidBody methods -------------------------------
+
+IParamMap *bhkRigidBodyIfcHelper::rbpmapParam = NULL;
+IObjParam *bhkRigidBodyIfcHelper::rbip = NULL;
+
+bhkRigidBodyIfcHelper::bhkRigidBodyIfcHelper()
+{
+   rbpblock = CreateParameterBlock(gRigidBlockParamDesc, descRigidBodyDescIDLength, 1);
+   rbpblock->SetValue(PB_RB_MATERIAL,0,NP_DEFAULT_HVK_MATERIAL);
+   rbpblock->SetValue(PB_RB_LAYER,0,NP_DEFAULT_HVK_LAYER);
+   rbpblock->SetValue(PB_RB_MASS,0,NP_DEFAULT_HVK_MASS);
+   rbpblock->SetValue(PB_RB_FRICTION,0,NP_DEFAULT_HVK_FRICTION);
+   rbpblock->SetValue(PB_RB_RESTITUTION,0,NP_DEFAULT_HVK_RESTITUTION);
+   rbpblock->SetValue(PB_RB_LINEAR_DAMPING,0,NP_DEFAULT_HVK_LINEAR_DAMPING);
+   rbpblock->SetValue(PB_RB_ANGULAR_DAMPING,0,NP_DEFAULT_HVK_ANGULAR_DAMPING);
+   rbpblock->SetValue(PB_RB_MAX_LINEAR_VELOCITY,0,NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY);
+   rbpblock->SetValue(PB_RB_MAX_ANGULAR_VELOCITY,0,NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY);
+   rbpblock->SetValue(PB_RB_PENETRATION_DEPTH,0,NP_DEFAULT_HVK_PENETRATION_DEPTH);
+   rbpblock->SetValue(PB_RB_MOTION_SYSTEM,0,NP_DEFAULT_HVK_MOTION_SYSTEM);
+   rbpblock->SetValue(PB_RB_QUALITY_TYPE,0,NP_DEFAULT_HVK_QUALITY_TYPE);
+
+}
+
+bhkRigidBodyIfcHelper::~bhkRigidBodyIfcHelper()
+{
+   if (rbpmapParam) {
+      rbpmapParam->SetUserDlgProc(NULL);
+      DestroyCPParamMap(rbpmapParam);
+      rbpmapParam  = NULL;
+   }
+}
+
+IParamBlock* bhkRigidBodyIfcHelper::GetRBBlock()
+{
+   return rbpblock;
+}
+
+void bhkRigidBodyIfcHelper::BeginEditRBParams(IObjParam *ip,ULONG flags,Animatable *prev)
+{
+   // Gotta make a new one.
+   if (NULL == rbpmapParam) 
+   {
+      rbpmapParam = CreateCPParamMap(
+         descRigidBodyParam,descRigidBodyParamLength,
+         rbpblock,
+         ip,
+         hInstance,
+         MAKEINTRESOURCE(IDD_RigidBody),
+         GetString(IDS_RB_RIGIDBODY_PARAM),
+         0);
+   }
+   this->rbip = ip;
+
+   if(rbpmapParam) {
+      // A callback for the type in.
+      rbpmapParam->SetUserDlgProc(new RigidBodyParamDlgProc(this));
+   }
+}
+
+void bhkRigidBodyIfcHelper::EndEditRBParams( IObjParam *ip, ULONG flags,Animatable *next )
+{
+   if (rbpmapParam && flags&END_EDIT_REMOVEUI ) {
+      rbpmapParam->SetUserDlgProc(NULL);
+      DestroyCPParamMap(rbpmapParam);
+      rbpmapParam = NULL;
+   }
+}
+
+
+void bhkRigidBodyIfcHelper::SetMaterial(int value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_MATERIAL,time,value);
+}
+
+int bhkRigidBodyIfcHelper::GetMaterial(TimeValue time, Interval& valid) const
+{
+   int value = NP_DEFAULT_HVK_MATERIAL;
+   rbpblock->GetValue(PB_RB_MATERIAL,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetLayer(int value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_LAYER,time,value);
+}
+int bhkRigidBodyIfcHelper::GetLayer(TimeValue time, Interval& valid) const 
+{
+   int value = NP_DEFAULT_HVK_LAYER;
+   rbpblock->GetValue(PB_RB_LAYER,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetMass(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_MASS,time,value);
+}
+float bhkRigidBodyIfcHelper::GetMass(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_MASS;
+   rbpblock->GetValue(PB_RB_MASS,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetFriction(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_FRICTION,time,value);
+}
+
+float bhkRigidBodyIfcHelper::GetFriction(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_FRICTION;
+   rbpblock->GetValue(PB_RB_FRICTION,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetRestitution (float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_RESTITUTION,time,value);
+}
+
+float bhkRigidBodyIfcHelper::GetRestitution (TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_RESTITUTION;
+   rbpblock->GetValue(PB_RB_RESTITUTION,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetLinearDamping(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_LINEAR_DAMPING,time,value);
+}
+
+float bhkRigidBodyIfcHelper::GetLinearDamping(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_LINEAR_DAMPING;
+   rbpblock->GetValue(PB_RB_LINEAR_DAMPING,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetAngularDamping(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_ANGULAR_DAMPING,time,value);
+}
+
+float bhkRigidBodyIfcHelper::GetAngularDamping(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_ANGULAR_DAMPING;
+   rbpblock->GetValue(PB_RB_ANGULAR_DAMPING,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetMaxLinearVelocity(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_MAX_LINEAR_VELOCITY,time,value);
+}
+
+float bhkRigidBodyIfcHelper::GetMaxLinearVelocity(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY;
+   rbpblock->GetValue(PB_RB_MAX_LINEAR_VELOCITY,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetMaxAngularVelocity(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_MAX_ANGULAR_VELOCITY,time,value);
+}
+float bhkRigidBodyIfcHelper::GetMaxAngularVelocity(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY;
+   rbpblock->GetValue(PB_RB_MAX_ANGULAR_VELOCITY,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetPenetrationDepth(float value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_PENETRATION_DEPTH,time,value);
+}
+float bhkRigidBodyIfcHelper::GetPenetrationDepth(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_PENETRATION_DEPTH;
+   rbpblock->GetValue(PB_RB_PENETRATION_DEPTH,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetMotionSystem(int value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_MOTION_SYSTEM,time,value);
+}
+int bhkRigidBodyIfcHelper::GetMotionSystem(TimeValue time, Interval& valid) const 
+{
+   int value = NP_DEFAULT_HVK_MOTION_SYSTEM;
+   rbpblock->GetValue(PB_RB_MOTION_SYSTEM,time,value,valid);
+   return value;
+}
+
+void bhkRigidBodyIfcHelper::SetQualityType(int value, TimeValue time)
+{
+   rbpblock->SetValue(PB_RB_QUALITY_TYPE,time,value);
+}
+int bhkRigidBodyIfcHelper::GetQualityType(TimeValue time, Interval& valid) const 
+{
+   float value = NP_DEFAULT_HVK_QUALITY_TYPE;
+   rbpblock->GetValue(PB_RB_QUALITY_TYPE,time,value,valid);
+   return value;
+}
diff --git a/NifProps/bhkRigidBodyInterface.h b/NifProps/bhkRigidBodyInterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..28cab3e0cec43e6db8642ba4e06864d757a249ff
--- /dev/null
+++ b/NifProps/bhkRigidBodyInterface.h
@@ -0,0 +1,178 @@
+/**********************************************************************
+*<
+FILE: bhkRigidBodyInterface.hpp
+
+DESCRIPTION:	Collision RigidBody Object Declration
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+  V1.0 - Derived from 3ds max prim RigidBody example
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#ifndef __BHKRIGIDBODYINTERFACE__H
+#define __BHKRIGIDBODYINTERFACE__H
+
+#include <ifnpub.h>
+
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+//! The unique instance of the rigid body interface
+extern CoreExport FPInterfaceDesc gbhkRigidBodyDesc;
+
+#define BHKRIGIDBODYINTERFACE_DESC   Interface_ID(0x056aad53, 0x52c54024)
+extern FPInterfaceDesc* GetbhkRigidBodyInterfaceDesc();
+
+class bhkRigidBodyInterface : public FPMixinInterface
+{
+public:			
+   virtual void		SetMaterial(int value, TimeValue time) = 0;
+   virtual int		   GetMaterial(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetLayer(int value, TimeValue time) = 0;
+   virtual int		   GetLayer(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetMass(float value, TimeValue time) = 0;
+   virtual float		GetMass(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetFriction(float value, TimeValue time) = 0;
+   virtual float		GetFriction(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetRestitution (float value, TimeValue time) = 0;
+   virtual float		GetRestitution (TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetLinearDamping(float value, TimeValue time) = 0;
+   virtual float		GetLinearDamping(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetAngularDamping(float value, TimeValue time) = 0;
+   virtual float		GetAngularDamping(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetMaxLinearVelocity(float value, TimeValue time) = 0;
+   virtual float		GetMaxLinearVelocity(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetMaxAngularVelocity(float value, TimeValue time) = 0;
+   virtual float		GetMaxAngularVelocity(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetPenetrationDepth(float value, TimeValue time) = 0;
+   virtual float		GetPenetrationDepth(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetMotionSystem(int value, TimeValue time) = 0;
+   virtual int		   GetMotionSystem(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   virtual void		SetQualityType(int value, TimeValue time) = 0;
+   virtual int		   GetQualityType(TimeValue time, Interval& valid = FOREVER) const = 0 ;
+
+   //Function Publishing System
+   enum {  
+      get_material, set_material,  enum_material,
+      get_layer, set_layer, enum_layer,
+      get_mass, set_mass,  
+      get_friction, set_friction,  
+      get_restitution, set_restitution,  
+      get_lineardamping, set_lineardamping,  
+      get_angulardamping, set_angulardamping,  
+      get_maxlinearvelocity, set_maxlinearvelocity,  
+      get_maxangularvelocity, set_maxangularvelocity,  
+      get_penetrationdepth, set_penetrationdepth,  
+      get_motionsystem, set_motionsystem, enum_motionsystem,
+      get_qualitytype, set_qualitytype,  enum_qualitytype,
+   };
+
+   //Function Map For Mixin Interface
+   //*************************************************
+   BEGIN_FUNCTION_MAP
+      PROP_TFNS(get_material, GetMaterial, set_material, SetMaterial, TYPE_INT);
+      PROP_TFNS(get_layer, GetLayer, set_layer, SetLayer, TYPE_INT);
+      PROP_TFNS(get_mass, GetMass, set_mass, SetMass, TYPE_FLOAT);
+      PROP_TFNS(get_friction, GetFriction, set_friction, SetFriction, TYPE_FLOAT);
+      PROP_TFNS(get_restitution, GetRestitution, set_restitution, SetRestitution, TYPE_FLOAT);
+      PROP_TFNS(get_lineardamping, GetLinearDamping, set_lineardamping, SetLinearDamping, TYPE_FLOAT);
+      PROP_TFNS(get_angulardamping, GetAngularDamping, set_angulardamping, SetAngularDamping, TYPE_FLOAT);
+      PROP_TFNS(get_maxlinearvelocity, GetMaxLinearVelocity, set_maxlinearvelocity, SetMaxLinearVelocity, TYPE_FLOAT);
+      PROP_TFNS(get_maxangularvelocity, GetMaxAngularVelocity, set_maxangularvelocity, SetMaxAngularVelocity, TYPE_FLOAT);
+      PROP_TFNS(get_penetrationdepth, GetPenetrationDepth, set_penetrationdepth, SetPenetrationDepth, TYPE_FLOAT);
+      PROP_TFNS(get_motionsystem, GetMotionSystem, set_motionsystem, SetMotionSystem, TYPE_INT);
+      PROP_TFNS(get_qualitytype, GetQualityType, set_qualitytype, SetQualityType, TYPE_INT);
+   END_FUNCTION_MAP
+
+   FPInterfaceDesc* GetDesc();    // <-- must implement 
+   //**************************************************
+};
+
+//--- Parameter map/block descriptors -------------------------------
+
+// Parameter block indices
+enum RigidBodyParamIndicies
+{
+   PB_RB_MATERIAL,
+   PB_RB_LAYER,
+   PB_RB_MASS,
+   PB_RB_FRICTION,
+   PB_RB_RESTITUTION,
+   PB_RB_LINEAR_DAMPING,
+   PB_RB_ANGULAR_DAMPING,
+   PB_RB_MAX_LINEAR_VELOCITY,
+   PB_RB_MAX_ANGULAR_VELOCITY,
+   PB_RB_PENETRATION_DEPTH,
+   PB_RB_MOTION_SYSTEM,
+   PB_RB_QUALITY_TYPE,
+};
+
+class bhkRigidBodyIfcHelper : public bhkRigidBodyInterface
+{
+public:
+   bhkRigidBodyIfcHelper();
+   ~bhkRigidBodyIfcHelper();
+
+   IParamBlock*      GetRBBlock();
+   void              BeginEditRBParams(IObjParam *ip,ULONG flags,Animatable *prev);
+   void              EndEditRBParams(IObjParam *ip,ULONG flags,Animatable *next);
+
+   virtual void		SetMaterial(int value, TimeValue time);
+   virtual int		   GetMaterial(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetLayer(int value, TimeValue time);
+   virtual int		   GetLayer(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetMass(float value, TimeValue time);
+   virtual float		GetMass(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetFriction(float value, TimeValue time);
+   virtual float		GetFriction(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetRestitution (float value, TimeValue time);
+   virtual float		GetRestitution (TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetLinearDamping(float value, TimeValue time);
+   virtual float		GetLinearDamping(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetAngularDamping(float value, TimeValue time);
+   virtual float		GetAngularDamping(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetMaxLinearVelocity(float value, TimeValue time);
+   virtual float		GetMaxLinearVelocity(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetMaxAngularVelocity(float value, TimeValue time);
+   virtual float		GetMaxAngularVelocity(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetPenetrationDepth(float value, TimeValue time);
+   virtual float		GetPenetrationDepth(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetMotionSystem(int value, TimeValue time);
+   virtual int		   GetMotionSystem(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual void		SetQualityType(int value, TimeValue time);
+   virtual int		   GetQualityType(TimeValue time, Interval& valid = FOREVER) const ;
+
+   virtual FPInterfaceDesc* GetDesc() { return GetbhkRigidBodyInterfaceDesc(); }
+   
+protected:
+   IParamBlock* rbpblock;
+   static IParamMap *rbpmapParam;
+   static IObjParam *rbip;
+};
+
+#endif
\ No newline at end of file
diff --git a/NifProps/bhkSphereObj.cpp b/NifProps/bhkSphereObj.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b8984cfe7ea1c703f02ec00abb0d39ff2dca838
--- /dev/null
+++ b/NifProps/bhkSphereObj.cpp
@@ -0,0 +1,679 @@
+/**********************************************************************
+*<
+FILE: bhkSphereObj.cpp
+
+DESCRIPTION:	Collision Sphere Object Implementation
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+  V1.0 - Derived from 3ds max prim sphere example
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#pragma warning( disable:4800 )
+#include <max.h>
+#include "MAX_Mem.h"
+#include <map>
+#include "NifProps.h"
+#include "iparamm.h"
+#include "Simpobj.h"
+#include "surf_api.h"
+#include "notify.h"
+#include "macroRec.h"
+#include "bhkRigidBodyInterface.h"
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+const Class_ID bhkSphereObject_CLASS_ID = Class_ID(0x8d532427, 0x8b4c64c7);
+
+class bhkSphereObject : public SimpleObject, public IParamArray, public bhkRigidBodyIfcHelper
+{
+public:			
+   // Class vars
+   static IParamMap *pmapParam;
+   static int dlgSegments;
+   static int dlgSmooth;
+   static float crtRadius;
+   static IObjParam *ip;
+
+   bhkSphereObject(BOOL loading);		
+   ~bhkSphereObject();		
+
+   // From Object
+   int CanConvertToType(Class_ID obtype);
+   Object* ConvertToType(TimeValue t, Class_ID obtype);
+   void GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist);
+
+   CreateMouseCallBack* GetCreateMouseCallBack();
+   void BeginEditParams( IObjParam  *ip, ULONG flags,Animatable *prev);
+   void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
+   RefTargetHandle Clone(RemapDir& remap = NoRemap());
+   TCHAR *GetObjectName() { return GetString(IDS_RB_SPHERE); }
+
+   // From GeomObject
+   int IntersectRay(TimeValue t, Ray& ray, float& at, Point3& norm);
+
+   // Animatable methods		
+   void DeleteThis() {delete this;}
+   Class_ID ClassID() { return bhkSphereObject_CLASS_ID; } 
+   SClass_ID SuperClassID() { return HELPER_CLASS_ID; }
+
+   // From ReferenceTarget
+   IOResult Load(ILoad *iload);
+   IOResult Save(ISave *isave);
+
+   // From ref
+   int NumRefs() {return 2;}
+   RefTargetHandle GetReference(int i);
+   void SetReference(int i, RefTargetHandle rtarg);
+
+   // From SimpleObject
+   void BuildMesh(TimeValue t);
+   BOOL OKtoDisplay(TimeValue t);
+   void InvalidateUI();
+   ParamDimension *GetParameterDim(int pbIndex);
+   TSTR GetParameterName(int pbIndex);		
+
+   void SetParams(float rad, int segs, BOOL smooth=TRUE);
+   void UpdateUI();
+
+   BaseInterface* GetInterface(Interface_ID id);
+
+};
+
+
+// Misc stuff
+#define MAX_SEGMENTS	200
+#define MIN_SEGMENTS	4
+
+#define MIN_RADIUS		float(0)
+#define MAX_RADIUS		float(1.0E30)
+
+#define MIN_SMOOTH		0
+#define MAX_SMOOTH		1
+
+#define DEF_SEGMENTS	32	// 16
+#define DEF_RADIUS		float(0.0)
+
+#define SMOOTH_ON	1
+#define SMOOTH_OFF	0
+
+#define MIN_SLICE	float(-1.0E30)
+#define MAX_SLICE	float( 1.0E30)
+
+
+//--- ClassDescriptor and class vars ---------------------------------
+
+static BOOL sInterfaceAdded = FALSE;
+
+// The class descriptor for sphere
+class bhkSphereClassDesc : public ClassDesc2 
+{
+public:
+   int 			   IsPublic() { return 1; }
+   void *			Create(BOOL loading = FALSE)
+   {
+      AddInterface (GetbhkRigidBodyInterfaceDesc());
+      return new bhkSphereObject(loading);
+   }
+   const TCHAR *	ClassName() { return GetString(IDS_RB_SPHERE_CLASS); }
+   SClass_ID		SuperClassID() { return HELPER_CLASS_ID; }
+   Class_ID		   ClassID() { return bhkSphereObject_CLASS_ID; }
+   const TCHAR* 	Category() { return "NifTools"; }
+   void			   ResetClassParams(BOOL fileReset);
+};
+
+static bhkSphereClassDesc sphereDesc;
+extern ClassDesc2* GetbhkSphereDesc() { return &sphereDesc; }
+
+// in prim.cpp  - The dll instance handle
+extern HINSTANCE hInstance;
+
+int bhkSphereObject::dlgSegments       = DEF_SEGMENTS;
+int bhkSphereObject::dlgSmooth         = SMOOTH_ON;
+IParamMap *bhkSphereObject::pmapParam  = NULL;
+IObjParam *bhkSphereObject::ip         = NULL;
+float bhkSphereObject::crtRadius       = 0.0f;
+
+void bhkSphereClassDesc::ResetClassParams(BOOL fileReset)
+{
+   bhkSphereObject::dlgSegments    = DEF_SEGMENTS;
+   bhkSphereObject::dlgSmooth      = SMOOTH_ON;
+   bhkSphereObject::crtRadius      = 0.0f;
+}
+
+
+//--- Parameter map/block descriptors -------------------------------
+
+// Parameter block indices
+enum SphereParamIndicies
+{
+   PB_RADIUS = 0,
+   PB_SEGS = 1,
+   PB_SMOOTH = 2,
+};
+
+
+//
+//
+// Parameters
+
+static ParamUIDesc descParam[] = {
+   // Radius
+   ParamUIDesc(
+   PB_RADIUS,
+   EDITTYPE_UNIVERSE,
+   IDC_RADIUS,IDC_RADSPINNER,
+   MIN_RADIUS,MAX_RADIUS,
+   SPIN_AUTOSCALE),	
+
+   // Segments
+   ParamUIDesc(
+   PB_SEGS,
+   EDITTYPE_INT,
+   IDC_SEGMENTS,IDC_SEGSPINNER,
+   (float)MIN_SEGMENTS,(float)MAX_SEGMENTS,
+   0.1f),
+
+   // Smooth
+   ParamUIDesc(PB_SMOOTH,TYPE_SINGLECHEKBOX,IDC_OBSMOOTH),
+};
+const int PARAMDESC_LENGTH = _countof(descParam);
+
+static ParamBlockDescID descVer0[] = {
+   { TYPE_FLOAT, NULL, TRUE, 0 },		
+   { TYPE_INT, NULL, TRUE, 1 },
+   { TYPE_INT, NULL, TRUE, 2 } 
+};
+const int PBLOCK_LENGTH = _countof(descVer0);
+static ParamBlockDescID *curDescVer = descVer0;
+
+// Array of old versions
+//static ParamVersionDesc versions[] = {
+//   ParamVersionDesc(descVer0,_countof(descVer0),0),
+//};
+//const int NUM_OLDVERSIONS = _countof(versions);
+static ParamVersionDesc* versions = NULL;
+const int NUM_OLDVERSIONS = 0;
+
+// Current version
+const int CURRENT_VERSION = NUM_OLDVERSIONS + 1;
+static ParamVersionDesc curVersion(descVer0,_countof(descVer0),CURRENT_VERSION);
+
+class SphereParamDlgProc : public ParamMapUserDlgProc {
+public:
+   bhkSphereObject *so;
+   HWND thishWnd;
+
+   SphereParamDlgProc(bhkSphereObject *s) {so=s;thishWnd=NULL;}
+   BOOL DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
+   void Update(TimeValue t);
+   void DeleteThis() {delete this;}
+
+   //--- ParamDlgProc --------------------------------
+   void TurnSpinner(HWND hWnd,int SpinNum,BOOL ison)
+   {	
+      ISpinnerControl *spin2 = GetISpinner(GetDlgItem(hWnd,SpinNum));
+      if (ison) spin2->Enable();else spin2->Disable();
+      ReleaseISpinner(spin2);
+   };
+
+};
+
+void SphereParamDlgProc::Update(TimeValue t)
+{
+   if (!thishWnd) 
+      return;
+   return;
+}
+
+BOOL SphereParamDlgProc::DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+   thishWnd=hWnd;
+   switch (msg) 
+   {
+   case WM_INITDIALOG: 
+      {
+         Update(t);
+         break;
+      }
+   case WM_COMMAND:
+      //switch (LOWORD(wParam)) 
+      //{
+      //}
+      break;	
+   }
+   return FALSE;
+}
+
+//--- Sphere methods -------------------------------
+
+
+bhkSphereObject::bhkSphereObject(BOOL loading)
+{
+   SetAFlag(A_PLUGIN1);
+   MakeRefByID(FOREVER, 0, CreateParameterBlock(curDescVer, PBLOCK_LENGTH, CURRENT_VERSION));
+   assert(pblock);
+   MakeRefByID(FOREVER, 1, GetRBBlock());
+
+   pblock->SetValue(PB_RADIUS,0,crtRadius);
+   pblock->SetValue(PB_SMOOTH,0,dlgSmooth);
+   pblock->SetValue(PB_SEGS,0,dlgSegments);	
+}
+
+bhkSphereObject::~bhkSphereObject() {
+}
+
+#define NEWMAP_CHUNKID	0x0100
+
+IOResult bhkSphereObject::Load(ILoad *iload) 
+{
+   ClearAFlag(A_PLUGIN1);
+
+   IOResult res;
+   while (IO_OK==(res=iload->OpenChunk())) 
+   {
+      switch (iload->CurChunkID()) 
+      {	
+      case NEWMAP_CHUNKID:
+         SetAFlag(A_PLUGIN1);
+         break;
+      }
+      iload->CloseChunk();
+      if (res!=IO_OK)  return res;
+   }
+   return IO_OK;
+}
+
+IOResult bhkSphereObject::Save(ISave *isave)
+{
+   if (TestAFlag(A_PLUGIN1)) {
+      isave->BeginChunk(NEWMAP_CHUNKID);
+      isave->EndChunk();
+   }
+   return IO_OK;
+}
+
+RefTargetHandle bhkSphereObject::GetReference(int i) 
+{
+   if (i == 1) 
+      return GetRBBlock();
+   return pblock;
+}
+
+void bhkSphereObject::SetReference(int i, RefTargetHandle rtarg) 
+{
+   if (i == 1)
+      return;
+   pblock=(IParamBlock*)rtarg;
+}
+
+BaseInterface* bhkSphereObject::GetInterface(Interface_ID id)
+{
+   if (id == BHKRIGIDBODYINTERFACE_DESC)
+      return this;
+   return SimpleObject::GetInterface(id);
+}
+
+
+
+void bhkSphereObject::BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev)
+{
+   SimpleObject::BeginEditParams(ip,flags,prev);
+
+   // Gotta make a new one.
+   if (NULL == pmapParam) 
+   {
+      pmapParam = CreateCPParamMap(
+         descParam,PARAMDESC_LENGTH,
+         pblock,
+         ip,
+         hInstance,
+         MAKEINTRESOURCE(IDD_SPHEREPARAM2),
+         GetString(IDS_RB_PARAMETERS),
+         0);
+   }
+   this->ip = ip;
+
+   if(pmapParam) {
+      // A callback for the type in.
+      pmapParam->SetUserDlgProc(new SphereParamDlgProc(this));
+   }
+   BeginEditRBParams(ip, flags, prev);
+}
+
+void bhkSphereObject::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next )
+{		
+   SimpleObject::EndEditParams(ip,flags,next);
+   this->ip = NULL;
+
+   if (flags&END_EDIT_REMOVEUI ) {
+      DestroyCPParamMap(pmapParam);
+      pmapParam  = NULL;
+   }
+   EndEditRBParams(ip, flags, next);
+
+   // Save these values in class variables so the next object created will inherit them.
+   pblock->GetValue(PB_SEGS,ip->GetTime(),dlgSegments,FOREVER);
+   pblock->GetValue(PB_SMOOTH,ip->GetTime(),dlgSmooth,FOREVER);	
+}
+
+void bhkSphereObject::SetParams(float rad, int segs, BOOL smooth)
+{
+   pblock->SetValue(PB_RADIUS,0, rad);				
+   pblock->SetValue(PB_SEGS,0, segs);				
+   pblock->SetValue(PB_SMOOTH,0, smooth);				
+}			   
+
+void bhkSphereObject::BuildMesh(TimeValue t)
+{
+   Point3 p;	
+   int ix,na,nb,nc,nd,jx,kx;
+   int nf=0,nv=0;
+   float delta, delta2;
+   float a,alt,secrad,secang,b,c;
+   int segs, smooth;
+   float radius;
+   float hemi = 0.0f;
+   BOOL genUVs = TRUE;
+   float startAng = 0.0f;
+   if (TestAFlag(A_PLUGIN1)) startAng = HALFPI;
+
+   // Start the validity interval at forever and whittle it down.
+   ivalid = FOREVER;
+   pblock->GetValue(PB_RADIUS, t, radius, ivalid);
+   pblock->GetValue(PB_SEGS, t, segs, ivalid);
+   pblock->GetValue(PB_SMOOTH, t, smooth, ivalid);
+   LimitValue(segs, MIN_SEGMENTS, MAX_SEGMENTS);
+   LimitValue(smooth, MIN_SMOOTH, MAX_SMOOTH);
+   LimitValue(radius, MIN_RADIUS, MAX_RADIUS);
+
+   float totalPie(0.0f);
+   if (hemi>=1.0f) hemi = 0.9999f;
+   hemi = (1.0f-hemi) * PI;
+   float basedelta=2.0f*PI/(float)segs;
+   delta2 = basedelta;
+   delta  = basedelta;
+
+   int rows = int(hemi/delta) + 1;
+   int realsegs=segs;
+   int nverts = rows * realsegs + 2;
+   int nfaces = rows * realsegs * 2;
+   mesh.setNumVerts(nverts);
+   mesh.setNumFaces(nfaces);
+   mesh.setSmoothFlags(smooth != 0);
+   int lastvert=nverts-1;
+
+   // Top vertex 
+   mesh.setVert(nv, 0.0f, 0.0f, radius);
+   nv++;
+
+   // Middle vertices 
+   alt=delta;
+   for(ix=1; ix<=rows; ix++) {		
+      a = (float)cos(alt)*radius;		
+      secrad = (float)sin(alt)*radius;
+      secang = startAng; //0.0f
+      for(jx=0; jx<segs; ++jx) {
+         b = (float)cos(secang)*secrad;
+         c = (float)sin(secang)*secrad;
+         mesh.setVert(nv++,b,c,a);
+         secang+=delta2;
+      }
+      alt+=delta;		
+   }
+
+   /* Bottom vertex */
+   mesh.setVert(nv++, 0.0f, 0.0f,-radius);
+
+   // Now make faces 
+
+   BitArray startSliceFaces;
+   BitArray endSliceFaces;
+
+   // Make top conic cap
+   for(ix=1; ix<=segs; ++ix) {
+      nc=(ix==segs)?1:ix+1;
+      mesh.faces[nf].setEdgeVisFlags(1,1,1);
+      mesh.faces[nf].setSmGroup(smooth?1:0);
+      mesh.faces[nf].setMatID(1);
+      mesh.faces[nf].setVerts(0, ix, nc);
+      nf++;
+   }
+
+   /* Make midsection */
+   int lastrow=rows-1,lastseg=segs-1,almostlast=lastseg-1;
+   for(ix=1; ix<rows; ++ix) {
+      jx=(ix-1)*segs+1;
+      for(kx=0; kx<segs; ++kx) {
+         na = jx+kx;
+         nb = na+segs;
+         nc = (kx==lastseg)? jx+segs: nb+1;
+         nd = (kx==lastseg)? jx : na+1;
+
+         mesh.faces[nf].setEdgeVisFlags(1,1,0);
+         mesh.faces[nf].setSmGroup(smooth?1:0);
+         mesh.faces[nf].setMatID(1); 
+         mesh.faces[nf].setVerts(na,nb,nc);
+         nf++;
+
+         mesh.faces[nf].setEdgeVisFlags(0,1,1);
+         mesh.faces[nf].setSmGroup(smooth?1:0);
+         mesh.faces[nf].setMatID(1);
+         mesh.faces[nf].setVerts(na,nc,nd);
+         nf++;
+      }
+   }
+
+   // Make bottom conic cap
+   na = mesh.getNumVerts()-1;
+   int botsegs=segs;
+   jx = (rows-1)*segs+1;lastseg=botsegs-1;
+   int fstart = nf;
+   for(ix=0; ix<botsegs; ++ix) {
+      nc = ix + jx;
+      nb = (ix==lastseg)?jx:nc+1;
+      mesh.faces[nf].setEdgeVisFlags(1,1,1);
+      mesh.faces[nf].setSmGroup(smooth?1:0);
+      mesh.faces[nf].setMatID(1);
+      mesh.faces[nf].setVerts(na, nb, nc);
+      nf++;
+   }
+
+   mesh.setNumTVerts(0);
+   mesh.setNumTVFaces(0);
+   mesh.InvalidateTopologyCache();
+}
+
+Object* bhkSphereObject::ConvertToType(TimeValue t, Class_ID obtype)
+{
+   return 0;
+   //return SimpleObject::ConvertToType(t,obtype);
+}
+
+int bhkSphereObject::CanConvertToType(Class_ID obtype)
+{
+   return 0;
+}
+
+
+void bhkSphereObject::GetCollapseTypes(Tab<Class_ID> &clist,Tab<TSTR*> &nlist)
+{
+   Object::GetCollapseTypes(clist, nlist);
+}
+
+class SphereObjCreateCallBack : public CreateMouseCallBack {
+   IPoint2 sp0;
+   bhkSphereObject *ob;
+   Point3 p0;
+public:
+   int proc( ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat);
+   void SetObj(bhkSphereObject *obj) {ob = obj;}
+};
+
+int SphereObjCreateCallBack::proc(ViewExp *vpt,int msg, int point, int flags, IPoint2 m, Matrix3& mat ) 
+{
+   float r;
+   Point3 p1,center;
+
+   if (msg == MOUSE_FREEMOVE)
+   {
+      vpt->SnapPreview(m,m,NULL, SNAP_IN_3D);
+   }
+
+   if (msg==MOUSE_POINT||msg==MOUSE_MOVE) 
+   {
+      switch(point) 
+      {
+      case 0:  // only happens with MOUSE_POINT msg
+         ob->pblock->SetValue(PB_RADIUS,0,0.0f);
+         ob->suspendSnap = TRUE;				
+         sp0 = m;
+         p0 = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D);
+         mat.SetTrans(p0);
+         break;
+      case 1:
+         mat.IdentityMatrix();
+         //mat.PreRotateZ(HALFPI);
+         p1 = vpt->SnapPoint(m,m,NULL,SNAP_IN_3D);
+         r = Length(p1-p0);
+         mat.SetTrans(p0);
+
+         ob->pblock->SetValue(PB_RADIUS,0,r);
+         ob->pmapParam->Invalidate();
+
+         if (flags&MOUSE_CTRL) 
+         {
+            float ang = (float)atan2(p1.y-p0.y,p1.x-p0.x);					
+            mat.PreRotateZ(ob->ip->SnapAngle(ang));
+         }
+
+         if (msg==MOUSE_POINT) 
+         {
+            ob->suspendSnap = FALSE;
+            return (Length(m-sp0)<3 || Length(p1-p0)<0.1f)?CREATE_ABORT:CREATE_STOP;
+         }
+         break;					   
+      }
+   }
+   else
+      if (msg == MOUSE_ABORT) 
+      {		
+         return CREATE_ABORT;
+      }
+
+      return TRUE;
+}
+
+static SphereObjCreateCallBack sphereCreateCB;
+
+CreateMouseCallBack* bhkSphereObject::GetCreateMouseCallBack() 
+{
+   sphereCreateCB.SetObj(this);
+   return(&sphereCreateCB);
+}
+
+
+BOOL bhkSphereObject::OKtoDisplay(TimeValue t) 
+{
+   float radius;
+   pblock->GetValue(PB_RADIUS,t,radius,FOREVER);
+   if (radius==0.0f) return FALSE;
+   else return TRUE;
+}
+
+// From GeomObject
+int bhkSphereObject::IntersectRay(TimeValue t, Ray& ray, float& at, Point3& norm)
+{
+   int smooth;
+   pblock->GetValue(PB_SMOOTH,t,smooth,FOREVER);
+
+   float r;
+   float a, b, c, ac4, b2, at1, at2;
+   float root;
+   BOOL neg1, neg2;
+
+   pblock->GetValue(PB_RADIUS,t,r,FOREVER);
+
+   a = DotProd(ray.dir,ray.dir);
+   b = DotProd(ray.dir,ray.p) * 2.0f;
+   c = DotProd(ray.p,ray.p) - r*r;
+
+   ac4 = 4.0f * a * c;
+   b2 = b*b;
+
+   if (ac4 > b2) return 0;
+
+   // We want the smallest positive root
+   root = float(sqrt(b2-ac4));
+   at1 = (-b + root) / (2.0f * a);
+   at2 = (-b - root) / (2.0f * a);
+   neg1 = at1<0.0f;
+   neg2 = at2<0.0f;
+   if (neg1 && neg2) 
+      return 0;
+   else if (neg1 && !neg2) 
+      at = at2;
+   else if (!neg1 && neg2) 
+      at = at1;
+   else if (at1<at2) 
+      at = at1;
+   else 
+      at = at2;
+   norm = Normalize(ray.p + at*ray.dir);
+   return 1;
+}
+
+void bhkSphereObject::InvalidateUI() 
+{
+   if (pmapParam) pmapParam->Invalidate();
+}
+
+ParamDimension *bhkSphereObject::GetParameterDim(int pbIndex) 
+{
+   switch (pbIndex) 
+   {
+   case PB_RADIUS:
+      return stdWorldDim;			
+   case PB_SEGS:
+      return stdSegmentsDim;			
+   case PB_SMOOTH:
+      return stdNormalizedDim;
+   default:
+      return defaultDim;
+   }
+}
+
+TSTR bhkSphereObject::GetParameterName(int pbIndex) 
+{
+   switch (pbIndex) 
+   {
+   case PB_RADIUS:
+      return TSTR(GetString(IDS_RB_RADIUS));			
+   case PB_SEGS:
+      return TSTR(GetString(IDS_RB_SEGS));			
+   case PB_SMOOTH:
+      return TSTR(GetString(IDS_RB_SMOOTH));			
+   default:
+      return TSTR(_T(""));
+   }
+}
+
+RefTargetHandle bhkSphereObject::Clone(RemapDir& remap) 
+{
+   bhkSphereObject* newob = new bhkSphereObject(FALSE);	
+   newob->ReplaceReference(0,remap.CloneRef(pblock));
+   newob->ivalid.SetEmpty();	
+   BaseClone(this, newob, remap);
+   return(newob);
+}
+
+void bhkSphereObject::UpdateUI()
+{
+   if (ip == NULL)
+      return;
+   SphereParamDlgProc* dlg = static_cast<SphereParamDlgProc*>(pmapParam->GetUserDlgProc());
+   dlg->Update(ip->GetTime());
+}
diff --git a/NifProps/resource.h b/NifProps/resource.h
index dca7d86eae8011de5bc7422f796345f3db16c8de..53b8d4f6b9cc2453c0d42ce4ca41e52365175e7a 100755
--- a/NifProps/resource.h
+++ b/NifProps/resource.h
@@ -2,6 +2,29 @@
 // Microsoft Visual C++ generated include file.
 // Used by NifProps.rc
 //
+#define IDD_SPHEREPARAM2                101
+#define IDC_RADIUS                      1000
+#define IDC_SEGMENTS                    1001
+#define IDC_RADIUS1                     1002
+#define IDC_RADIUS2                     1003
+#define IDC_SEGSPINNER                  1004
+#define IDC_RADSPINNER                  1005
+#define IDC_OBSMOOTH                    1006
+#define IDC_RADSPINNER1                 1007
+#define IDC_RADSPINNER2                 1008
+#define IDC_ED_POS1_X                   1700
+#define IDC_SP_POS1_X                   1701
+#define IDC_ED_POS1_Y                   1702
+#define IDC_SP_POS1_Y                   1703
+#define IDC_ED_POS1_Z                   1704
+#define IDC_SP_POS1_Z                   1705
+#define IDC_ED_POS2_X                   1706
+#define IDC_SP_POS2_X                   1707
+#define IDC_ED_POS2_Y                   1708
+#define IDC_SP_POS2_Y                   1709
+#define IDC_ED_POS2_Z                   1710
+#define IDC_SP_POS2_Z                   1711
+#define IDC_LBL_POS1                    1712
 #define IDS_LIBDESCRIPTION              11001
 #define IDD_PANEL                       11001
 #define IDC_LBL_BSXFLAGS                11001
@@ -10,19 +33,29 @@
 #define IDC_ED_BSXFLAGS                 11002
 #define IDS_CLASS_NAME                  11003
 #define IDC_ED_STRINGSEXTRA             11003
+#define IDD_RigidBody                   11003
 #define IDS_PARAMS                      11004
 #define IDC_LBL_STRINGSEXTRA            11004
+#define IDD_CAPSULEPARAM                11004
 #define IDS_SPIN                        11005
 #define IDC_GRP_OBJECT                  11005
 #define IDS_ANIM_PARAMS                 11006
 #define IDC_CHK_ISCOLL                  11006
 #define IDC_GRP_HAVOK                   11007
 #define IDC_HVK_BEGIN                   11007
+#define IDS_RB_Capsule                  11007
+#define IDS_RB_CAPSULE                  11007
+#define IDS_RB_Capsule                  11007
 #define IDC_LBL_MATERIAL                11008
+#define IDS_RB_CAPSULE_CLASS            11008
 #define IDC_CB_MATERIAL                 11009
+#define IDS_RB_RIGIDBODY_PARAM          11009
 #define IDC_LBL_LAYER                   11010
+#define IDS_RB_CAP_POS1                 11010
 #define IDC_CB_LAYER                    11011
+#define IDS_RB_CAP_POS2                 11011
 #define IDC_LBL_CENTER                  11012
+#define IDC_LBL_POS2                    11013
 #define IDC_ED_CENTER_X                 11490
 #define IDC_SP_CENTER_X                 11491
 #define IDC_ED_CENTER_Y                 11492
@@ -63,6 +96,16 @@
 #define IDC_ED_ANIM_PRIORITY            11601
 #define IDC_SP_ANIM_PRIORITY            11602
 #define IDC_ANIM_END                    11602
+#define IDS_RB_PARAMETERS               30028
+#define IDS_RB_RADIUS                   30045
+#define IDS_RB_SEGS                     30046
+#define IDS_RB_SMOOTH                   30048
+#define IDS_RB_PRIMITIVES               30264
+#define IDS_RB_SPHERE                   30503
+#define IDS_RB_RADIUS1                  30516
+#define IDS_RB_RADIUS2                  30517
+#define IDS_RB_SPHERE_CLASS             31298
+#define IDS_DS_PARAMCHG                 31316
 
 // Next default values for new objects
 // 
@@ -70,7 +113,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        103
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1700
+#define _APS_NEXT_CONTROL_VALUE         1713
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif