diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index db4fa85f2c157698262f2df209eedb363990ae4b..902dc78cfc998d967e3d5e31b4c4edac2bff90d8 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -27,6 +27,28 @@
 
     Change log
     ----------
+    
+      0.2
+      -----
+    o Importer
+      - Added Vertex Color modifier support
+      - Fixed issue with static animation import
+      - Fixed issue with skin vertex weight count import
+      
+    o Exporter
+      - Upgraded to Visual Studio 2005
+      - Dropped registry support in favor of INI file.
+      - Dropped Official Max6 support because I do not have SDK to compile it with (try the max 7 files).
+      - Fixed issue with importing glossiness setting on textures.
+      - Fixed issues with export of vertex color. Alpha map is now exported as part of the normal color map.
+      - No longer exports meshes associated with bone or biped nodes.
+      - No longer exports extra NiNode when exporting NiTriGeom-based objects (can be reset in ini file)
+      - Mass, Restitution(Ellasticity), and Friction now share values with Reactor(Havok)
+      - Modified UPB export to actually export the values in the UserPropBuffer not just a fixed list.
+      - Added Skin Modifier export
+      
+    o NifProps Utility
+      - Mass, Restitution(Ellasticity), and Friction now share values with Reactor(Havok)
 
       0.1.5
       -----
diff --git a/MaxNifTools.ini b/MaxNifTools.ini
index 13800f9c7d798749280a9a2d4fa110b4bdb2a0f7..c7b2529df4b160dd7e4ce3bba87ac4db452b4627 100644
--- a/MaxNifTools.ini
+++ b/MaxNifTools.ini
@@ -11,6 +11,8 @@ KnownApplications=Oblivion;Morrowind;Civ4;DAoC
 Reparse=0
 
 [MaxNifExport]
+; Max SDK Plugin Version (0 - AutoSelect; 0x17700d00 - Max6, 0x1f401100 - Max8)  Default: 0 
+MaxSDKVersion=0
 ; Current Application to get setting/directory information from.  Should match KnownApps above or Auto.
 ;   Auto will check the import file against the known root paths use that app if a directory match is found.
 CurrentApp=Auto
@@ -32,10 +34,20 @@ VertexColors=1
 RemapIndices=1
 ; Texture Prefix if texture not found in AppSettings directory. Default:textures
 TexturePrefix=textures
-; Version to tell 3ds Max that we are for compatibility. Default:013
-Version=013
+; Add Export additional NiNodes for Meshes. Default: 0
+ExportExtraNodes=0
+; Export Skin Modifier when attached to bones. Default: 1
+ExportSkin=1
+; Export UserPropBuffer. Default: 0
+UserPropBuffer=0
+; Flatten Node Hierarchy. Default: 0
+FlattenHierarchy=0
+; Remove Unreferenced Bones. Default: 0
+RemoveUnreferencedBones=0
 
 [MaxNifImport]
+; Max SDK Plugin Version (0 - AutoSelect; 0x17700d00 - Max6, 0x1f401100 - Max8)  Default: 0 
+MaxSDKVersion=0x17700d00
 ; Current Application to get setting/directory information from.  Should match KnownApps above or Auto.
 ;   Auto will check the import file against the known root paths use that app if a directory match is found.
 CurrentApp=Auto
@@ -51,13 +63,15 @@ ShowTextures=1
 EnableAutoSmooth=1
 ; AutoSmooth angle. Default: 30
 AutoSmoothAngle=30.0
-; Remove Double/Illegal faces on meshes on import
+; Remove Double/Illegal faces on meshes on import. Default:1
 RemoveDoubleFaces=1
 RemoveIllegalFaces=1
-; EnableSkinSupport attempt to skin the mesh if bones are available
+; EnableSkinSupport attempt to skin the mesh if bones are available. Default:1
 EnableSkinSupport=1
-; Enable Havok Collision Support
+; Enable Havok Collision Support. Default:1
 EnableCollision=1
+; Vertex Color Support mode. (0-Disable; 1-Bake into mesh; 2-Use VertexPaint Modifier) Default:1
+VertexColorMode=1
 
 [BipedImport]
 ; Top level bone import setting. Default:1
diff --git a/NifCommon/niutils.cpp b/NifCommon/niutils.cpp
index 9296d470d5ff329b1552990d1e1be2649ab241c5..3f2190afafdf38b239afd0220d83afe5d2fa0793 100644
--- a/NifCommon/niutils.cpp
+++ b/NifCommon/niutils.cpp
@@ -558,3 +558,60 @@ INode* FindINode(Interface *i, NiObjectNETRef node)
    }
    return NULL;
 }
+
+Matrix3 GetNodeLocalTM(INode *n, TimeValue t)
+{
+   Matrix3 m3 = n->GetNodeTM(t);
+   Matrix3 m3p = n->GetParentTM(t);
+   m3p.Invert();
+   return m3 * m3p;
+}
+
+// Locate a TriObject in an Object if it exists
+TriObject* GetTriObject(Object *o)
+{
+   if (o && o->CanConvertToType(triObjectClassID))
+      return (TriObject *)o->ConvertToType(0, triObjectClassID);
+   while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o)
+   {
+      IDerivedObject* dobj = (IDerivedObject *)(o);
+      o = dobj->GetObjRef();
+      if (o && o->CanConvertToType(triObjectClassID))
+         return (TriObject *)o->ConvertToType(0, triObjectClassID);
+   }
+   return NULL;
+}
+
+// Get or Create the Skin Modifier
+Modifier *GetSkin(INode *node)
+{
+   Object* pObj = node->GetObjectRef();
+   if (!pObj) return NULL;
+   while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
+   {
+      IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
+      int Idx = 0;
+      while (Idx < pDerObj->NumModifiers())
+      {
+         // Get the modifier. 
+         Modifier* mod = pDerObj->GetModifier(Idx);
+         if (mod->ClassID() == SKIN_CLASSID)
+         {
+            // is this the correct Physique Modifier based on index?
+            return mod;
+         }
+         Idx++;
+      }
+      pObj = pDerObj->GetObjRef();
+   }
+
+   IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
+
+   //create a skin modifier and add it
+   Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
+   dobj->SetAFlag(A_LOCK_TARGET);
+   dobj->AddModifier(skinMod);
+   dobj->ClearAFlag(A_LOCK_TARGET);
+   node->SetObjectRef(dobj);
+   return skinMod;
+}
diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h
index c3ebf85bfa4760228dd0856a1ac43e5b0ef5b9e8..1a8cf8f0ef58eda64eee8555d53fd0b4ef37c198 100644
--- a/NifCommon/niutils.h
+++ b/NifCommon/niutils.h
@@ -58,19 +58,20 @@ inline TCHAR *Trim(TCHAR*&p) {
 // Case insensitive string equivalence test for collections
 struct ltstr
 {
-   bool operator()(const TCHAR* s1, const TCHAR* s2) const
+   bool operator()(const char* s1, const char* s2) const
    { return _tcsicmp(s1, s2) < 0; }
 
-   bool operator()(const std::string& s1, const std::string& s2) const
-   { return s1.compare(s2) < 0; }
+   bool operator()(const string& s1, const string& s2) const
+   { return _tcsicmp(s1.c_str(), s2.c_str()) < 0; }
 
-   bool operator()(const std::string& s1, const TCHAR * s2) const
-   { return s1.compare(s2) < 0; }
+   bool operator()(const string& s1, const char * s2) const
+   { return _tcsicmp(s1.c_str(), s2) < 0; }
 
-   bool operator()(const TCHAR * s1, const std::string& s2) const
-   { return s2.compare(s1) >= 0; }
+   bool operator()(const char * s1, const string& s2) const
+   { return _tcsicmp(s1, s2.c_str()) >= 0; }
 };
 
+
 // Case insensitive string equivalence but numbers are sorted together
 struct NumericStringEquivalence
 {
@@ -228,6 +229,7 @@ enum PosRotScale
 extern void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs = prsDefault, TimeValue t = 0);
 extern void PosRotScaleNode(Control *c, Point3 p, Quat& q, float s, PosRotScale prs = prsDefault, TimeValue t = 0);
 extern void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs = prsDefault, TimeValue t = 0);
+extern Matrix3 GetNodeLocalTM(INode *n, TimeValue t = 0);
 
 extern Niflib::NiNodeRef FindNodeByName( const vector<Niflib::NiNodeRef>& blocks, const string& name );
 extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match);
@@ -269,20 +271,15 @@ static inline Color TOCOLOR(const Niflib::Color3& c3) {
    return Color(c3.r, c3.g, c3.b);
 }
 
-static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false){
-   Niflib::Vector3 pos; Niflib::Matrix33 rot; float scale;
-   tm.Decompose(pos, rot, scale);
-   Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3());
-   if (invert) m.Invert();
-   m.SetTrans(Point3(pos.x, pos.y, pos.z));
-   return m;
-}
-
 
 static inline Point3 TOPOINT3(const Niflib::Vector3& v){
    return Point3(v.x, v.y, v.z);
 }
 
+static inline Niflib::Vector3 TOVECTOR3(const Point3& v){
+   return Niflib::Vector3(v.x, v.y, v.z);
+}
+
 static inline Quat TOQUAT(const Niflib::Quaternion& q, bool inverse = false){
    Quat qt(q.x, q.y, q.z, q.w);
    return (inverse) ? qt.Inverse() : qt;
@@ -299,6 +296,22 @@ static inline AngAxis TOANGAXIS(const Niflib::Quaternion& q, bool inverse = fals
    return AngAxis(q.x, q.y, q.z, q.w);
 }
 
+static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false){
+   Niflib::Vector3 pos; Niflib::Matrix33 rot; float scale;
+   tm.Decompose(pos, rot, scale);
+   Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3());
+   if (invert) m.Invert();
+   m.SetTrans(Point3(pos.x, pos.y, pos.z));
+   return m;
+}
+
+static inline Niflib::Matrix44 TOMATRIX4(const Matrix3 &tm, bool invert = false){
+   Niflib::Matrix33 m3(tm.GetRow(0)[0], tm.GetRow(0)[1], tm.GetRow(0)[2],
+                       tm.GetRow(1)[0], tm.GetRow(1)[1], tm.GetRow(1)[2],
+                       tm.GetRow(2)[0], tm.GetRow(2)[1], tm.GetRow(2)[2]);
+   Niflib::Matrix44 m4(TOVECTOR3(tm.GetTrans()), m3, 1.0f);
+   return m4;
+}
 
 template <typename U, typename T>
 inline Niflib::Ref<U> SelectFirstObjectOfType( vector<Niflib::Ref<T> > const & objs ) {
@@ -321,4 +334,7 @@ inline Niflib::Ref<U> SelectFirstObjectOfType( list<Niflib::Ref<T> > const & obj
 TSTR PrintMatrix3(Matrix3& m);
 TSTR PrintMatrix44(Niflib::Matrix44& m);
 
+extern Modifier *GetSkin(INode *node);
+extern TriObject* GetTriObject(Object *o);
+
 #endif // _NIUTILS_H_
\ No newline at end of file
diff --git a/NifExport/Config.cpp b/NifExport/Config.cpp
index 659d7b07610fbf54d2579b65210f4f1fe32f982f..5c4937cadb20e65d8f923e8f922cad1db6c0e984 100755
--- a/NifExport/Config.cpp
+++ b/NifExport/Config.cpp
@@ -48,7 +48,6 @@ void Exporter::writeConfig(Interface *i)
       LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
       PathCombine(iniName, pluginDir, "MaxNifTools.ini");
 
-      SetIniValue(NifExportSection, "Version", mVersion, iniName);
       SetIniValue(NifExportSection, "GenerateStrips", mTriStrips, iniName);
       SetIniValue(NifExportSection, "IncludeHidden", mExportHidden, iniName);
       SetIniValue(NifExportSection, "FurnatureMarkers", mExportFurn, iniName);
@@ -92,7 +91,7 @@ void Exporter::readConfig(Interface *i)
       LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
       PathCombine(iniName, pluginDir, "MaxNifTools.ini");
 
-      mVersion = GetIniValue<int>(NifExportSection, "Version", 013, iniName);
+      //mVersion = GetIniValue<int>(NifExportSection, "Version", 013, iniName);
       mTriStrips = GetIniValue<bool>(NifExportSection, "GenerateStrips", true, iniName);
       mExportHidden = GetIniValue<bool>(NifExportSection, "IncludeHidden", false, iniName);
       mExportFurn = GetIniValue<bool>(NifExportSection, "FurnatureMarkers", true, iniName);
@@ -102,6 +101,13 @@ void Exporter::readConfig(Interface *i)
       mTexPrefix = GetIniValue<string>(NifExportSection, "TexturePrefix", "textures", iniName);
       mExportCollision = GetIniValue<bool>(NifExportSection, "ExportCollision", true, iniName);
       mRemapIndices = GetIniValue(NifExportSection, "RemapIndices", true, iniName);
+
+      mExportExtraNodes = GetIniValue(NifExportSection, "ExportExtraNodes", false, iniName);
+      mExportSkin = GetIniValue(NifExportSection, "ExportSkin", false, iniName);
+      mUserPropBuffer = GetIniValue(NifExportSection, "UserPropBuffer", false, iniName);
+      mFlattenHierarchy = GetIniValue(NifExportSection, "FlattenHierarchy", false, iniName);
+      mRemoveUnreferencedBones = GetIniValue(NifExportSection, "RemoveUnreferencedBones", false, iniName);
+      mSortNodesToEnd = GetIniValue(NifExportSection, "SortNodesToEnd", false, iniName);
   }
 }
 
diff --git a/NifExport/DllEntry.cpp b/NifExport/DllEntry.cpp
index 6fdd5ac3362c37472c5e0e98040ebd64eae453c9..3d0f8fad0569996db48f4cec1a1b8d941defcd5f 100755
--- a/NifExport/DllEntry.cpp
+++ b/NifExport/DllEntry.cpp
@@ -1,9 +1,12 @@
 #include "pch.h"
+#include "niutils.h"
 
 extern ClassDesc2* GetNifExportDesc();
 
+static void InitializeLibSettings();
 HINSTANCE hInstance;
 int controlsInit = FALSE;
+int libVersion = VERSION_3DSMAX;
 
 // This function is called by Windows when the DLL is loaded.  This 
 // function may also be called many times during time critical operations
@@ -20,10 +23,31 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved)
 		InitCustomControls(hInstance);	// Initialize MAX's custom controls
 		InitCommonControls();			// Initialize Win95 controls
 	}
-			
+   if (fdwReason == DLL_PROCESS_ATTACH)
+      InitializeLibSettings();
 	return (TRUE);
 }
 
+void InitializeLibSettings()
+{
+   Interface *gi = GetCOREInterface();
+   TCHAR iniName[MAX_PATH];
+   if (gi) {
+      LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR);
+      PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+   } else {
+      GetModuleFileName(NULL, iniName, _countof(iniName));
+      if (LPTSTR fname = PathFindFileName(iniName))
+         fname = NULL;
+      PathAddBackslash(iniName);
+      PathAppend(iniName, "plugcfg");
+      PathAppend(iniName, "MaxNifTools.ini");
+   }
+   libVersion = GetIniValue("MaxNifExport", "MaxSDKVersion", libVersion, iniName);
+   if (libVersion == 0)
+      libVersion = VERSION_3DSMAX;
+}
+
 // This function returns a string that describes the DLL and where the user
 // could purchase the DLL if they don't have it.
 __declspec( dllexport ) const TCHAR* LibDescription()
@@ -52,7 +76,7 @@ __declspec( dllexport ) ClassDesc* LibClassDesc(int i)
 // to catch obsolete DLLs.
 __declspec( dllexport ) ULONG LibVersion()
 {
-	return VERSION_3DSMAX;
+	return libVersion;
 }
 
 TCHAR *GetString(int id)
diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp
index 05eaed77d3bd5e1ba58cad5167290667817b30c3..a38bfe331892fd4a3c5d67ff83de4f2c477aad17 100755
--- a/NifExport/Exporter.cpp
+++ b/NifExport/Exporter.cpp
@@ -14,6 +14,12 @@ string Exporter::mTexPrefix="textures";
 bool Exporter::mExportCollision=true;
 bool Exporter::mRemapIndices=true;
 bool Exporter::mUseRegistry=false;
+bool Exporter::mExportExtraNodes=false;
+bool Exporter::mExportSkin=false;
+bool Exporter::mUserPropBuffer=false;
+bool Exporter::mFlattenHierarchy=false;
+bool Exporter::mRemoveUnreferencedBones=false;
+bool Exporter::mSortNodesToEnd=false;
 
 Exporter::Exporter(Interface *i, AppSettings *appSettings)
    : mI(i), mAppSettings(appSettings)
@@ -22,32 +28,11 @@ Exporter::Exporter(Interface *i, AppSettings *appSettings)
 
 Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
 {
-	BSXFlagsRef bsx = DynamicCast<BSXFlags>(CreateBlock("BSXFlags"));
+	BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
 	bsx->SetName("BSX");
 	bsx->SetFlags(0x00000002);
    root->AddExtraData(DynamicCast<NiExtraData>(bsx));
-
-   // Write the actual UPB sans any np_ prefixed strings
-   TSTR upb;
-   node->GetUserPropBuffer(upb);
-   if (!upb.isNull())
-   {
-      string line;
-      istringstream istr(string(upb), ios_base::out);
-      ostringstream ostr;
-      while (!istr.eof()) {
-         std::getline(istr, line);
-         if (!line.empty() && 0 != line.compare(0, 3, "np_"))
-            ostr << line << endl;
-      }
-      if (!ostr.str().empty())
-      {
-         NiStringExtraDataRef strings = DynamicCast<NiStringExtraData>(CreateBlock("NiStringExtraData"));	
-         strings->SetName("UPB");
-         strings->SetData(ostr.str());
-         root->AddExtraData(DynamicCast<NiExtraData>(strings));
-      }
-   }
+   exportUPB(root, node);
 
 	mNiRoot = root;
 	
@@ -58,11 +43,22 @@ Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
 		
 	if (mExportCollision)
 	{
-	
 		result = exportCollision(root, node);
 		if (result != Ok)
 			return result;
 	}
+   // handle post export callbacks (like skin)
+   for (CallbackList::iterator cb = mPostExportCallbacks.begin(); cb != mPostExportCallbacks.end(); cb = mPostExportCallbacks.erase(cb))
+   {
+      (*cb)->execute();
+      delete (*cb);
+   }
+   // Remove unreferenced Bones
+   if (mRemoveUnreferencedBones)
+      removeUnreferencedBones(mNiRoot);
+   if (mSortNodesToEnd)
+      sortNodes(mNiRoot);
+
 	return Ok;
 }
 
diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h
index 01a991ba2fe40bd00f867d01c8cb891331b72233..a931c1be48718980b30b77c6e6906be033f09872 100755
--- a/NifExport/Exporter.h
+++ b/NifExport/Exporter.h
@@ -19,6 +19,15 @@ public:
 		Skip
 	};
 
+   // Callback for post-processing instructions
+   struct NiCallback
+   {
+      NiCallback() {};
+      virtual ~NiCallback() {};
+      virtual Result execute() = 0;
+   };
+
+
 	/* exporter version */
 	static int				mVersion;
 
@@ -33,7 +42,13 @@ public:
 	static bool				mVertexColors;
 	static float			mWeldThresh;
 	static bool				mExportCollision;
-	static bool				mRemapIndices;
+   static bool				mRemapIndices;
+   static bool				mExportExtraNodes;
+   static bool				mExportSkin;
+   static bool				mUserPropBuffer;
+   static bool          mFlattenHierarchy;
+   static bool          mRemoveUnreferencedBones;
+   static bool          mSortNodesToEnd;
 
 	Exporter(Interface *i, AppSettings *appSettings);
 
@@ -67,14 +82,20 @@ private:
 		Triangles			faces;
 		vector<TexCoord>	uvs;
 		vector<Color4>		vcolors;
+      vector<int>       vidx;
+      TriStrips         strips;
 	};
 
 	// maps face groups to material ID
 	typedef std::map<int, FaceGroup>	FaceGroups;	
-
+   typedef std::map<string, NiNodeRef>	NodeMap;	
+   typedef std::list<NiCallback*> CallbackList;
+   
 	Interface				*mI;
 	NiNodeRef				mNiRoot;
    AppSettings          *mAppSettings;
+   NodeMap              mNodeMap;
+   CallbackList         mPostExportCallbacks;
 
 	Result					exportCollision(NiNodeRef &root, INode *node);
 	Result					exportMeshes(NiNodeRef &root, INode *node);
@@ -89,7 +110,8 @@ private:
 	bool					equal(const Vector3 &a, const Point3 &b, float thresh);
 	BitmapTex				*getTexture(Mtl *mtl);
 	void					getTextureMatrix(Matrix3 &mat, Mtl *mtl);
-	NiNodeRef				makeNode(NiNodeRef &parent, INode *maxNode, bool local=true);
+   NiNodeRef				makeNode(NiNodeRef &parent, INode *maxNode, bool local=true);
+   NiNodeRef				getNode(const string& name);
 	// returns true if the node contains collision objects
 	bool					isCollisionGroup(INode *maxNode, bool root=true);
 	// returns true if the node contains meshes
@@ -97,19 +119,19 @@ private:
 
 	/* tristrips */
 	void					strippify(TriStrips &strips, vector<Vector3> &verts, vector<Vector3> &norms, const Triangles &tris);
-	void					strippify(TriStrips &strips, FaceGroup &grp);
+	void					strippify(FaceGroup &grp);
 	NiTriStripsDataRef		makeTriStripsData(const TriStrips &strips);
 
 	/* mesh export */
 	// adds a vertex to a face group if it doesn't exist yet. returns new or previous index into the
 	// vertex array.
-	int 					addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matrix3 &texm);
+	int 					addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matrix3 &texm, vector<Color4>& vertColors);
 	// adds a face to a face group
-	void					addFace(FaceGroup &grp, int face, const int vi[3], Mesh *mesh, const Matrix3 &texm);
+	void					addFace(FaceGroup &grp, int face, const int vi[3], Mesh *mesh, const Matrix3 &texm, vector<Color4>& vertColors);
 	// creates face groups from faces with same sub material id
-	bool					splitMesh(INode *node, Mesh *, FaceGroups &grps, TimeValue t);
+	bool					splitMesh(INode *node, Mesh *, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors);
 	// creates a NiTriStrips or NiTriShape hierarchy from a face group
-	bool					makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp);
+	NiTriBasedGeomRef makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp, bool exportStrips);
 	// splits mesh and converts it into nif blocks
 	Result					exportMesh(NiNodeRef &parent, INode *node, TimeValue t);
 
@@ -134,6 +156,15 @@ private:
 	bhkSphereRepShapeRef	makeBoxShape(Object *obj);
 	bhkSphereRepShapeRef	makeSphereShape(Object *obj);
 	bhkSphereRepShapeRef	makeCapsuleShape(Object *obj);
+
+   /* skin export */
+   bool makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t);
+   bool exportSkin();
+
+   /* misc export */
+   bool exportUPB(NiNodeRef &root, INode *node);
+   bool removeUnreferencedBones(NiNodeRef node);
+   void sortNodes(NiNodeRef node);
 };
 
 #endif 
diff --git a/NifExport/Mesh.cpp b/NifExport/Mesh.cpp
index 54e9fb973a149de5f86c598f2f2dab29a381de6f..2ec3cb8d42e6eafd40293523d2efb49b427ccf48 100755
--- a/NifExport/Mesh.cpp
+++ b/NifExport/Mesh.cpp
@@ -1,5 +1,12 @@
 #include "pch.h"
-
+#include "niutils.h"
+#include "iskin.h"
+#ifdef USE_BIPED
+#  include <cs/BipedApi.h>
+#endif
+#include "obj/NiSkinInstance.h"
+#include "obj/NiSkinData.h"
+#include "obj/NiSkinPartition.h"
 /*
 
 void FPUtility::GetAlphaVal(void)
@@ -46,24 +53,51 @@ Exporter::Result Exporter::exportMeshes(NiNodeRef &parent, INode *node)
 	if (coll ||	(node->IsHidden() && !mExportHidden && !coll) || (mSelectedOnly && !node->Selected()))
 		return Skip;
 
+   bool local = !mFlattenHierarchy;
+   NiNodeRef nodeParent = mFlattenHierarchy ? mNiRoot : parent;
+
 	NiNodeRef newParent;
 	TimeValue t = 0;
 	ObjectState os = node->EvalWorldState(t); 
-	if (os.obj && os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
-	{
-		newParent = makeNode(parent, node);
 
-		Result result;
-		result = exportMesh(newParent, node, t);
-		if (result != Ok)
-			return result;
-
-	} else
-	if (isMeshGroup(node))
+   // Always skip bones and bipeds
+   SClass_ID scid = node->SuperClassID();
+   Class_ID ncid = node->ClassID();
+   TSTR nodeName = node->GetName();
+   TSTR nodeClass; node->GetClassName(nodeClass);
+
+   // For some unusual reason, bones named Helper are converted to Meshes and 
+   //   lose their Bone properties except a new node named Bone seem to show up
+   if (node->IsBoneShowing() || strmatch(nodeName, "Bone"))
+      newParent = makeNode(nodeParent, node, local);
+   else if (os.obj && os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
+	{
+      TSTR objClass;
+      os.obj->GetClassName(objClass);
+      SClass_ID oscid = os.obj->SuperClassID();
+      Class_ID oncid = os.obj->ClassID();
+      if (os.obj && (os.obj->ClassID() == BONE_OBJ_CLASSID || os.obj->ClassID() == Class_ID(BONE_CLASS_ID,0)))
+         newParent = makeNode(nodeParent, node, local);
+#ifdef USE_BIPED
+      else if (os.obj && os.obj->node->ClassID() == BIPED_CLASS_ID)
+         newParent = makeNode(nodeParent, node, local);
+#endif
+      else
+      {
+         newParent = (mExportExtraNodes) ? makeNode(nodeParent, node, local) : nodeParent;
+
+         Result result;
+         result = exportMesh(newParent, node, t);
+         if (result != Ok)
+            return result;
+      }
+	} 
+   else if (isMeshGroup(node) && local) // only create node if local
 	{
-		newParent = makeNode(parent, node);
+		newParent = makeNode(parent, node, local);
 
-	} else
+	} 
+   else
 		newParent = parent;
 
 	for (int i=0; i<node->NumberOfChildren(); i++) 
@@ -81,31 +115,93 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 {	
 	ObjectState os = node->EvalWorldState(t);
 
+   bool local = !mFlattenHierarchy;
+
 	TriObject *tri = (TriObject *)os.obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0));
 	if (!tri)
 		return Error;
 
 	Mesh *mesh = &tri->GetMesh();
+
+   // Note that calling setVCDisplayData will clear things like normals so we set this up first
+   vector<Color4> vertColors;
+   if (mVertexColors)
+   {
+      bool hasvc = false;
+      if (mesh->mapSupport(MAP_ALPHA))
+      {
+         mesh->setVCDisplayData(MAP_ALPHA);
+         int n = mesh->getNumVertCol();
+         if (n > vertColors.size())
+            vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
+         VertColor *vertCol = mesh->vertColArray;
+         if (vertCol) {
+            for (int i=0; i<n; ++i) {
+               VertColor c = vertCol[ i ];
+               float a = (c.x + c.y + c.z) / 3.0f;
+               vertColors[i].a = a;
+               hasvc |= (a != 1.0f);
+            }
+         }
+      }
+      if (mesh->mapSupport(0))
+      {
+         mesh->setVCDisplayData(0);
+         VertColor *vertCol = mesh->vertColArray;
+         int n = mesh->getNumVertCol();
+         if (n > vertColors.size())
+            vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
+         if (vertCol) {
+            for (int i=0; i<n; ++i) {
+               VertColor col = vertCol[ i ];
+               vertColors[i] = Color4(col.x, col.y, col.z, vertColors[i].a);
+               hasvc |= (col.x != 1.0f || col.y != 1.0f || col.z != 1.0f);
+            }
+         }
+      }
+      if (!hasvc) vertColors.clear();
+   }
+
 	mesh->buildNormals();
 
 	Result result = Ok;
 	while (1)
 	{
 		FaceGroups grps;
-		if (!splitMesh(node, mesh, grps, t))
+		if (!splitMesh(node, mesh, grps, t, vertColors))
 		{
 			result = Error;
 			break;
 		}
+      bool exportStrips = mTriStrips;
+
+      // Disable strips if exporting skin nodes, for now
+      //if (exportStrips && mExportSkin && GetSkin(node) != NULL)
+      //   exportStrips = false;
+
 
+      Matrix33 rot; Vector3 trans;
+      nodeTransform(rot, trans, node, t, local);
+      Matrix44 tm(trans, rot, 1.0f);
+
+      TSTR basename = node->NodeName();
+      TSTR format = (!basename.isNull() && grps.size() > 1) ? "%s:%d" : "%s";
+
+      int i=1;
 		FaceGroups::iterator grp;
-		for (grp=grps.begin(); grp!=grps.end(); ++grp)
+		for (grp=grps.begin(); grp!=grps.end(); ++grp, ++i)
 		{
-			if (!makeMesh(ninode, getMaterial(node, grp->first), grp->second))
+         string name = FormatString(basename, basename.data(), i);
+         NiTriBasedGeomRef shape = makeMesh(ninode, getMaterial(node, grp->first), grp->second, exportStrips);
+         if (shape == NULL)
 			{
 				result = Error;
 				break;
 			}
+
+         shape->SetName(name);
+         shape->SetLocalTransform(tm);
+         makeSkin(shape, node, grp->second, t);
 		}
 
 		break;
@@ -117,28 +213,25 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 	return result;
 }
 
-bool Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp)
+NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp, bool exportStrips)
 {
 	NiTriBasedGeomRef shape;
 	NiTriBasedGeomDataRef data;
 
-	if (mTriStrips)
+	if (exportStrips)
 	{
-		NiTriStripsRef stripsShape = DynamicCast<NiTriStrips>(CreateBlock("NiTriStrips"));
+		NiTriStripsRef stripsShape = CreateNiObject<NiTriStrips>();
 		shape = DynamicCast<NiTriBasedGeom>(stripsShape);
-
-		TriStrips strips;
-		strippify(strips, grp);
-		NiTriStripsDataRef stripData = makeTriStripsData(strips);
+		strippify(grp);
+		NiTriStripsDataRef stripData = makeTriStripsData(grp.strips);
 		data = DynamicCast<NiTriBasedGeomData>(stripData);
 
 	} else
 	{
-		NiTriShapeRef tris = DynamicCast<NiTriShape>(CreateBlock("NiTriShape"));
-		NiTriShapeDataRef triData = DynamicCast<NiTriShapeData>(CreateBlock("NiTriShapeData"));
+		NiTriShapeRef tris = CreateNiObject<NiTriShape>();
+		NiTriShapeDataRef triData = CreateNiObject<NiTriShapeData>();
 		data = DynamicCast<NiTriBasedGeomData>(triData);
 		shape = DynamicCast<NiTriBasedGeom>(tris);
-
 		triData->SetTriangles(grp.faces);
 	}
 
@@ -162,21 +255,27 @@ bool Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp)
 
 	parent->AddChild(DynamicCast<NiAVObject>(shape));
 
-	return true;
+	return shape;
 }
 
-int Exporter::addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matrix3 &texm)
+int Exporter::addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matrix3 &texm, vector<Color4>& vertColors)
 {
-	Point3 pt = mesh->verts[ mesh->faces[ face ].v[ vi ] ];
-	Point3 norm = getVertexNormal(mesh, face, mesh->getRVertPtr(mesh->faces[ face ].v[ vi ]));
+   int vidx = mesh->faces[ face ].v[ vi ];
+	Point3 pt = mesh->verts[ vidx ];
+	Point3 norm = getVertexNormal(mesh, face, mesh->getRVertPtr(vidx));
 
 	Point3 uv;
-	if (mesh->tvFace)
+	if (mesh->tVerts && mesh->tvFace)
 		uv = mesh->tVerts[ mesh->tvFace[ face ].t[ vi ]] * texm;
 
-	VertColor col;
-	if (mVertexColors && mesh->vertCol)
-		col = mesh->vertCol[ mesh->vcFace[ face ].t[ vi ] ];
+   Color4 col(1.0f, 1.0f, 1.0f);
+   if (mVertexColors && !vertColors.empty()){
+      TVFace *vcFace = mesh->vcFaceData ? mesh->vcFaceData : mesh->vcFace;
+      if (vcFace) {
+         int vidx = vcFace[ face ].t[ vi ];
+         col = vertColors[vidx];
+      }
+   }
 
 	for (int i=0; i<grp.verts.size(); i++)
 	{
@@ -186,38 +285,40 @@ int Exporter::addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matr
 			if (mesh->tvFace && (grp.uvs[i].u!=uv.x || grp.uvs[i].v!=uv.y))
 				continue;
 
-			if (mVertexColors && mesh->vertCol &&
-				(grp.vcolors[i].r!=col.x ||
-				 grp.vcolors[i].g!=col.y ||
-				 grp.vcolors[i].b!=col.z))
+			if (mVertexColors && !vertColors.empty() &&
+				(grp.vcolors[i].r!=col.r ||
+				 grp.vcolors[i].g!=col.g ||
+				 grp.vcolors[i].b!=col.b))
 				continue;
 
 			return i;
 		}
 	}
 	
+   grp.vidx.push_back(vidx);
 	grp.verts.push_back(Vector3(pt.x, pt.y, pt.z));
 	grp.vnorms.push_back(Vector3(norm.x, norm.y, norm.z));
 
 	if (mesh->tvFace)
 		grp.uvs.push_back(TexCoord(uv.x, uv.y));
 
-	if (mVertexColors && mesh->vertCol)
-		grp.vcolors.push_back(Color4(col.x, col.y, col.z, 1));
+   if (mVertexColors && !vertColors.empty()){
+		grp.vcolors.push_back(col);
+   }
 
 	return grp.verts.size()-1;
 }
 
-void Exporter::addFace(FaceGroup &grp, int face, const int vi[3], Mesh *mesh, const Matrix3 &texm)
+void Exporter::addFace(FaceGroup &grp, int face, const int vi[3], Mesh *mesh, const Matrix3 &texm, vector<Color4>& vertColors)
 {
 	Triangle tri;
 	for (int i=0; i<3; i++)
-		tri[i] = addVertex(grp, face, vi[i], mesh, texm);
+		tri[i] = addVertex(grp, face, vi[i], mesh, texm, vertColors);
 
 	grp.faces.push_back(tri);
 }
 
-bool Exporter::splitMesh(INode *node, Mesh *mesh, FaceGroups &grps, TimeValue t)
+bool Exporter::splitMesh(INode *node, Mesh *mesh, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors)
 {
 	Mtl* nodeMtl = node->GetMtl();
 	Matrix3 tm = node->GetObjTMAfterWSM(t);
@@ -251,8 +352,162 @@ bool Exporter::splitMesh(INode *node, Mesh *mesh, FaceGroups &grps, TimeValue t)
 
 		texm *= flip;
 
-		addFace(grps[mtlID], i, vi, mesh, texm);
+		addFace(grps[mtlID], i, vi, mesh, texm, vertColors);
 	}
 
 	return true;
 }
+
+// Callback interface to register a Skin after entire structure is built due to contraints
+//   in the nif library
+struct SkinInstance : public Exporter::NiCallback
+{  
+   typedef vector<SkinWeight> SkinWeightList;
+   typedef vector<SkinWeightList> BoneWeightList;
+   typedef vector<float> WeightList;
+   typedef vector<ushort> BoneList;
+   typedef vector<ushort> Strip;
+   typedef vector<Strip> Strips;
+   typedef vector<Triangle> Triangles;
+
+
+   Exporter *owner;
+   // Common Data
+   NiTriBasedGeomRef shape;
+   vector<NiNodeRef> boneList;
+
+   // Bone to weight map
+   BoneWeightList boneWeights;
+
+   // Partition
+   int nWeightsPerVertex;
+   vector<WeightList> vertexWeights;
+   BoneList boneMap;
+   vector<ushort> vertexMap;
+   Strips strips;
+   vector<BoneList> boneIndexList;
+   Triangles triangles;
+
+   SkinInstance(Exporter *Owner) : owner(Owner) {}
+   virtual ~SkinInstance() {}
+   virtual Exporter::Result execute();
+};
+
+
+bool Exporter::makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t)
+{
+   if (!mExportSkin)
+      return false;
+
+   if (grp.verts.empty())
+      return false;
+
+   //get the skin modifier
+   Modifier *mod = GetSkin(node);
+   if (!mod)
+      return false;
+
+   ISkin *skin = (ISkin *) mod->GetInterface(I_SKIN);
+   if (!skin)
+      return false;
+
+   ISkinContextData *skinData = skin->GetContextInterface(node);
+   if (!skinData)
+      return false;
+
+   if (grp.strips.empty())
+      strippify(grp);
+
+   // Create new call back to finish export
+   SkinInstance* si = new SkinInstance(this);
+   mPostExportCallbacks.push_back(si);
+
+   si->shape = shape;
+   for (TriStrips::iterator strip = grp.strips.begin(); strip != grp.strips.end(); ++strip)
+      si->strips.push_back(*strip);
+
+   // Get number of weights per vertex
+   int nWeightsPerVertex = 4;
+   Interval ivalid;
+   IParamBlock2 *params = mod->GetParamBlockByID(2/*advanced*/);
+   params->GetValue(0x7/*bone_Limit*/, 0, nWeightsPerVertex, ivalid);
+   si->nWeightsPerVertex = nWeightsPerVertex;
+
+   // Get bone references (may not actually exist in proper structure at this time)
+   int totalBones = skin->GetNumBones();
+   si->boneWeights.resize(totalBones);
+   si->boneList.resize(totalBones);
+   si->boneMap.resize(totalBones);
+   for (int i=0; i<totalBones; ++i) {
+      string name = skin->GetBoneName(i);
+      si->boneList[i] = getNode(name);
+      si->boneMap[i] = i;
+   }
+
+   vector<int>& vidx = grp.vidx;
+   vector<Vector3>& verts = grp.verts;
+   int nv = vidx.size();
+   si->vertexMap.resize(nv);
+   si->vertexWeights.resize(nv);
+   si->boneIndexList.resize(nv);
+
+   for (int i=0; i<nv; ++i)
+   {
+      SkinInstance::WeightList& vertexWeights = si->vertexWeights[i];
+      SkinInstance::BoneList& boneIndexList = si->boneIndexList[i];
+      vertexWeights.assign(nWeightsPerVertex, 0.0f);
+      boneIndexList.resize(nWeightsPerVertex, 0);
+      si->vertexMap[i] = i;
+
+      int vi = vidx[i];
+      Vector3& vert = verts[i];
+      int nbones = skinData->GetNumAssignedBones(vi);
+      for (int j=0; j<nbones; ++j)
+      {
+         SkinWeight sw;
+         sw.index = i;
+         sw.weight = skinData->GetBoneWeight(vi,j);
+         int boneIndex = skinData->GetAssignedBone(vi,j);
+
+         SkinInstance::SkinWeightList& weights = si->boneWeights[boneIndex];
+         weights.push_back(sw);
+
+         boneIndexList[j] = boneIndex;
+         vertexWeights[j] = sw.weight;
+      }         
+   }
+   return true;
+}
+
+Exporter::Result SkinInstance::execute()
+{
+   shape->BindSkin(boneList);
+   uint bone = 0;
+   for (BoneWeightList::iterator bitr = boneWeights.begin(); bitr != boneWeights.end(); ++bitr, ++bone) 
+      shape->SetBoneWeights(bone, (*bitr));
+
+   NiSkinInstanceRef skin = shape->GetSkinInstance();
+   NiSkinPartitionRef partition = CreateNiObject<NiSkinPartition>();
+   partition->SetNumPartitions(1);
+   partition->SetWeightsPerVertex(0, nWeightsPerVertex);
+   partition->SetBoneMap(0, boneMap);
+   partition->SetNumVertices(0, ushort(vertexMap.size()) );
+   partition->SetVertexMap(0, vertexMap);
+   partition->EnableVertexWeights(0, true);
+   for (int i=0; i<vertexWeights.size(); ++i) {
+      partition->SetVertexWeights(0, i, vertexWeights[i]);
+   }
+   //partition->SetTriangles()
+   partition->SetStripCount(0, ushort(strips.size()) );
+   for (int i=0; i<strips.size(); ++i) {
+      partition->SetStrip(0, i, strips[i]);
+   }
+   partition->EnableVertexBoneIndices(0, true);
+   for (int i=0; i<boneIndexList.size(); ++i) {
+      partition->SetVertexBoneIndices(0, i, boneIndexList[i]);
+   }
+
+   skin->SetSkinPartition(partition);
+
+   return Exporter::Ok;
+}
diff --git a/NifExport/NifExport_VC80.vcproj b/NifExport/NifExport_VC80.vcproj
index f26a65a4a35a47778d2a073f6d79715570ba76fa..fc8e25e2ef0100924b32526d640dbcbc0fd0b6c3 100644
--- a/NifExport/NifExport_VC80.vcproj
+++ b/NifExport/NifExport_VC80.vcproj
@@ -54,7 +54,7 @@
 				FavorSizeOrSpeed="1"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="C:\3dsmax8\maxsdk\include;..\..\niflib;..\NifCommon"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS"
 				StringPooling="true"
 				ExceptionHandling="2"
 				RuntimeLibrary="0"
@@ -156,7 +156,7 @@
 				AdditionalOptions="/LD /FI&quot;$(ProjectDir)pch.h&quot;"
 				Optimization="0"
 				AdditionalIncludeDirectories="C:\3dsmax8\maxsdk\include;..\..\niflib;..\NifCommon"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS"
 				StringPooling="true"
 				MinimalRebuild="true"
 				ExceptionHandling="2"
@@ -253,7 +253,7 @@
 				AdditionalOptions="/LD /FI&quot;$(ProjectDir)pch.h&quot;"
 				Optimization="0"
 				AdditionalIncludeDirectories="C:\3dsmax6\maxsdk\include;..\..\niflib;..\NifCommon"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS"
 				StringPooling="true"
 				MinimalRebuild="true"
 				ExceptionHandling="2"
@@ -356,7 +356,7 @@
 				FavorSizeOrSpeed="1"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="C:\3dsmax6\maxsdk\include;..\..\niflib;..\NifCommon"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS"
 				StringPooling="true"
 				ExceptionHandling="2"
 				RuntimeLibrary="0"
@@ -464,7 +464,7 @@
 				FavorSizeOrSpeed="1"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="C:\3dsmax7\maxsdk\include;..\..\niflib;..\NifCommon"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS"
 				StringPooling="true"
 				ExceptionHandling="2"
 				RuntimeLibrary="0"
@@ -566,7 +566,7 @@
 				AdditionalOptions="/LD /FI&quot;$(ProjectDir)pch.h&quot;"
 				Optimization="0"
 				AdditionalIncludeDirectories="C:\3dsmax7\maxsdk\include;..\..\niflib;..\NifCommon"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS"
 				StringPooling="true"
 				MinimalRebuild="true"
 				ExceptionHandling="2"
diff --git a/NifExport/Strips.cpp b/NifExport/Strips.cpp
index 2294710d7249029a500e5649b3a5e5a36eeba07a..8443bb7d7a049f1467b4a70e0b0eaa257131ff40 100755
--- a/NifExport/Strips.cpp
+++ b/NifExport/Strips.cpp
@@ -70,8 +70,10 @@ void Exporter::strippify(TriStrips &strips, vector<Vector3> &verts, vector<Vecto
 	}
 }
 
-void Exporter::strippify(TriStrips &strips, FaceGroup &grp)
+void Exporter::strippify(FaceGroup &grp)
 {
+   TriStrips &strips = grp.strips;
+   strips.clear();
 	unsigned short *data = (unsigned short *)malloc(grp.faces.size() * 3 * 2);
 
 	int i;
diff --git a/NifExport/Util.cpp b/NifExport/Util.cpp
index 930b0ca58c211383b3c3e0309a4fb0fd7be433b3..c714c707df901670b79a70dac5346865aa847a5a 100755
--- a/NifExport/Util.cpp
+++ b/NifExport/Util.cpp
@@ -88,10 +88,22 @@ bool Exporter::equal(const Vector3 &a, const Point3 &b, float thresh)
 		   (fabsf(a.z-b.z) <= thresh);
 }
 
+NiNodeRef Exporter::getNode(const string& name)
+{
+   NodeMap::iterator itr = mNodeMap.find(name);
+   if (itr != mNodeMap.end())
+      return (*itr).second;
+   NiNodeRef node = CreateNiObject<NiNode>();
+   node->SetName(name);
+   mNodeMap[name] = node;
+   return node;
+}
+
 NiNodeRef Exporter::makeNode(NiNodeRef &parent, INode *maxNode, bool local)
 {
-	NiNodeRef node = DynamicCast<NiNode>(CreateBlock("NiNode"));
-	
+   string name = (char*)maxNode->GetName();
+   NiNodeRef node = getNode(name);
+
 	Matrix33 rot;
 	Vector3 trans;
 	TimeValue t = 0;
@@ -99,8 +111,8 @@ NiNodeRef Exporter::makeNode(NiNodeRef &parent, INode *maxNode, bool local)
 	
 	node->SetLocalRotation(rot);
 	node->SetLocalTranslation(trans);
-	string name = (char*)maxNode->GetName();
-	node->SetName(name);
+
+   exportUPB(node, maxNode);
 
 	parent->AddChild(DynamicCast<NiAVObject>(node));
 	return node;
@@ -152,3 +164,91 @@ bool Exporter::isMeshGroup(INode *maxNode, bool root)
 
 	return false;
 }
+
+bool Exporter::exportUPB(NiNodeRef &root, INode *node)
+{
+   bool ok = false;
+   if (!mUserPropBuffer)
+      return ok;
+
+   // Write the actual UPB sans any np_ prefixed strings
+   TSTR upb;
+   node->GetUserPropBuffer(upb);
+   if (!upb.isNull())
+   {
+      string line;
+      istringstream istr(string(upb), ios_base::out);
+      ostringstream ostr;
+      while (!istr.eof()) {
+         std::getline(istr, line);
+         if (!line.empty() && 0 != line.compare(0, 3, "np_"))
+            ostr << line << endl;
+      }
+      if (!ostr.str().empty())
+      {
+         NiStringExtraDataRef strings = CreateNiObject<NiStringExtraData>();	
+         strings->SetName("UPB");
+         strings->SetData(ostr.str());
+         root->AddExtraData(DynamicCast<NiExtraData>(strings));
+         ok = true;
+      }
+   }
+   return ok;
+}
+
+
+bool Exporter::removeUnreferencedBones(NiNodeRef node)
+{
+   NiNodeRef parent = node->GetParent();
+   bool remove = (NULL != parent) && !node->IsSkinInfluence();
+   Matrix44 ntm = node->GetLocalTransform();
+   vector<NiAVObjectRef> children = node->GetChildren();
+   for (vector<NiAVObjectRef>::iterator itr = children.begin(); itr != children.end(); ++itr)
+   {
+      NiAVObjectRef& child = (*itr);
+      bool childRemove = false;
+      if (child->IsDerivedType(NiNode::TypeConst()))
+      {
+         childRemove = removeUnreferencedBones(StaticCast<NiNode>(child));
+      }
+      if (childRemove)
+      {
+         node->RemoveChild(child);
+      }
+      else if (remove) // Reparent abandoned nodes to root
+      {
+         Matrix44 tm = child->GetLocalTransform();
+         child->SetLocalTransform( ntm * tm );
+         node->RemoveChild(child);
+         mNiRoot->AddChild(child);
+      }
+   }
+   return remove;
+}
+
+struct SortNodeEquivalence
+{
+   inline bool operator()(const NiAVObjectRef& lhs, const NiAVObjectRef& rhs) const
+   {
+      if (!lhs) return !rhs;
+      if (!rhs) return true;
+      string ltype = lhs->GetType().GetTypeName();
+      string rtype = rhs->GetType().GetTypeName();
+      if (ltype == "NiNode")
+         return false;
+      if (rtype == "NiNode")
+         return true;
+      if (ltype == rtype)
+         return false;
+      return (ltype < rtype); 
+   }
+};
+
+void Exporter::sortNodes(NiNodeRef node)
+{
+   node->SortChildren(SortNodeEquivalence());
+
+   vector<NiNodeRef> children = DynamicCast<NiNode>(node->GetChildren());
+   for (vector<NiNodeRef>::iterator itr = children.begin(); itr != children.end(); ++itr)
+      sortNodes(*itr);
+}
\ No newline at end of file
diff --git a/NifImport/DllEntry.cpp b/NifImport/DllEntry.cpp
index 436e5afcd8c994c68a7df75ce9743d574eb2d14d..5647fa4c9fced0a99ffc31ed01cc28358f4a336b 100644
--- a/NifImport/DllEntry.cpp
+++ b/NifImport/DllEntry.cpp
@@ -14,9 +14,11 @@
 #include "MaxNifImport.h"
 
 extern ClassDesc2* GetMaxNifImportDesc();
+static void InitializeLibSettings();
 
 HINSTANCE hInstance;
 int controlsInit = FALSE;
+int libVersion = VERSION_3DSMAX;
 
 // This function is called by Windows when the DLL is loaded.  This 
 // function may also be called many times during time critical operations
@@ -33,10 +35,33 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved)
 		InitCustomControls(hInstance);	// Initialize MAX's custom controls
 		InitCommonControls();			// Initialize Win95 controls
 	}
+   if (fdwReason == DLL_PROCESS_ATTACH)
+      InitializeLibSettings();
 			
 	return (TRUE);
 }
 
+void InitializeLibSettings()
+{
+   Interface *gi = GetCOREInterface();
+   TCHAR iniName[MAX_PATH];
+   if (gi) {
+      LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR);
+      PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+   } else {
+      GetModuleFileName(NULL, iniName, _countof(iniName));
+      if (LPTSTR fname = PathFindFileName(iniName))
+         fname = NULL;
+      PathAddBackslash(iniName);
+      PathAppend(iniName, "plugcfg");
+      PathAppend(iniName, "MaxNifTools.ini");
+   }
+   libVersion = GetIniValue("MaxNifImport", "MaxSDKVersion", libVersion, iniName);
+   if (libVersion == 0)
+      libVersion = VERSION_3DSMAX;
+
+}
+
 // This function returns a string that describes the DLL and where the user
 // could purchase the DLL if they don't have it.
 __declspec( dllexport ) const TCHAR* LibDescription()
@@ -60,14 +85,6 @@ __declspec( dllexport ) ClassDesc* LibClassDesc(int i)
 	}
 }
 
-// This function returns a pre-defined constant indicating the version of 
-// the system under which it was compiled.  It is used to allow the system
-// to catch obsolete DLLs.
-__declspec( dllexport ) ULONG LibVersion()
-{
-	return VERSION_3DSMAX;
-}
-
 TCHAR *GetString(int id)
 {
 	static TCHAR buf[256];
@@ -77,3 +94,10 @@ TCHAR *GetString(int id)
 	return NULL;
 }
 
+// This function returns a pre-defined constant indicating the version of 
+// the system under which it was compiled.  It is used to allow the system
+// to catch obsolete DLLs.
+__declspec( dllexport ) ULONG LibVersion()
+{
+   return ULONG(libVersion);
+}
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
index c126177aae030b3551ac3a03b6712db9ab690ca7..9307e84af31e007e901db9da469638c18d7e2250 100644
--- a/NifImport/ImportAnimation.cpp
+++ b/NifImport/ImportAnimation.cpp
@@ -310,7 +310,7 @@ bool KFMImporter::ImportAnimation()
 
                // Set initial conditions
                Point3 p = TOPOINT3(interp->GetTranslation());
-               Quat q = TOQUAT(interp->GetRotation());
+               Quat q = TOQUAT(interp->GetRotation(), true);
                float s = interp->GetScale();
                PosRotScaleNode(c, p, q, s, prsDefault, 0);
 
@@ -326,7 +326,7 @@ bool KFMImporter::ImportAnimation()
 
                // Set initial conditions
                Point3 p = TOPOINT3(interp->GetTranslation());
-               Quat q = TOQUAT(interp->GetRotation());
+               Quat q = TOQUAT(interp->GetRotation(), true);
                float s = interp->GetScale();
                PosRotScaleNode(c, p, q, s, prsDefault, 0);
 
@@ -349,7 +349,7 @@ bool KFMImporter::ImportAnimation()
                if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) {
                   // Set initial conditions
                   Point3 p = TOPOINT3(interp->GetTranslation());
-                  Quat q = TOQUAT(interp->GetRotation());
+                  Quat q = TOQUAT(interp->GetRotation(), true);
                   float s = interp->GetScale();
                   PosRotScaleNode(c, p, q, s, prsDefault, 0);
 
diff --git a/NifImport/ImportCollision.cpp b/NifImport/ImportCollision.cpp
index 64552101b0a50b635e77b084921501ccd1f22cfd..7d4b307c7d961929b3860fe303224454d896c039 100644
--- a/NifImport/ImportCollision.cpp
+++ b/NifImport/ImportCollision.cpp
@@ -18,6 +18,7 @@ HISTORY:
 #include "obj\bhkShape.h"
 #include "obj\bhkSphereShape.h"
 #include "obj\bhkCapsuleShape.h"
+#include "obj\bhkConvexVerticesShape.h"
 #include "NifPlugins.h"
 
 using namespace Niflib;
@@ -39,8 +40,9 @@ struct CollisionImport
    bool ImportRigidBody(bhkRigidBodyRef rb, INode* node);
 
    bool ImportShape(bhkRigidBodyRef body, bhkShapeRef shape, INode* parent);
-   bool ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent);
-   bool ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent);
+   INode* ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent);
+   INode* ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent);
+   INode* ImportConvexVertices(bhkRigidBodyRef body, bhkConvexVerticesShapeRef shape, INode *parent);
 };
 
 bool NifImporter::ImportCollision(NiNodeRef node)
@@ -108,22 +110,46 @@ bool CollisionImport::ImportRigidBody(bhkRigidBodyRef body, INode* node)
    return true;
 }
 
-bool CollisionImport::ImportShape(bhkRigidBodyRef body, bhkShapeRef shape, INode* node)
+bool CollisionImport::ImportShape(bhkRigidBodyRef body, bhkShapeRef shape, INode* parent)
 {
+   INode *shapeNode = NULL;
    if (shape->IsDerivedType(bhkCapsuleShape::TypeConst()))
    {
-      return ImportCapsule(body, bhkCapsuleShapeRef(shape), node);
+      shapeNode = ImportCapsule(body, bhkCapsuleShapeRef(shape), parent);
    }
    else if (shape->IsDerivedType(bhkSphereShape::TypeConst()))
    {
-      return ImportSphere(body, bhkSphereShapeRef(shape), node);
+      shapeNode = ImportSphere(body, bhkSphereShapeRef(shape), parent);
+   }
+   else if (shape->IsDerivedType(bhkConvexVerticesShape::TypeConst()))
+   {
+      shapeNode = ImportConvexVertices(body, bhkConvexVerticesShapeRef(shape), parent);
+   }
+
+   // Now do common post processing for the node
+   if (shapeNode != NULL)
+   {
+      TSTR name = "bhk";
+      name.Append(parent->GetName());
+      shapeNode->SetName(name);
+
+      Point3 pos = TOPOINT3(body->GetTranslation()) * ni.bhkScaleFactor;
+      Quat rot = TOQUAT(body->GetRotation());
+      PosRotScaleNode(shapeNode, pos, rot, ni.bhkScaleFactor, prsDefault);
+
+      shapeNode->SetPrimaryVisibility(FALSE);
+      shapeNode->SetSecondaryVisibility(FALSE);
+      shapeNode->BoneAsLine(TRUE);
+      shapeNode->SetRenderable(FALSE);
+      shapeNode->XRayMtl(TRUE);
+      parent->AttachChild(shapeNode);
+      return true;
    }
    return false;
 }
 
-bool CollisionImport::ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent)
+INode* CollisionImport::ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape, INode *parent)
 {
-   bool ok = false;
    if (SimpleObject *ob = (SimpleObject *)ni.gi->CreateInstance(GEOMOBJECT_CLASS_ID, Class_ID(SPHERE_CLASS_ID, 0))) {
       float radius = shape->GetRadius();
 
@@ -131,34 +157,16 @@ bool CollisionImport::ImportSphere(bhkRigidBodyRef body, bhkSphereShapeRef shape
       setMAXScriptValue(t, "radius", 0, radius);
 
       if (INode *n = ni.gi->CreateObjectNode(ob)) {
-         TSTR name = "bhk";
-         name.Append(parent->GetName());
-         n->SetName(name);
-
          // Need to "Affect Pivot Only" and "Center to Object" first
          n->CenterPivot(0, FALSE);
-
-         Point3 pos = TOPOINT3(body->GetTranslation()) * ni.bhkScaleFactor;
-         Point3 center = TOPOINT3(body->GetCenter());
-         Quat rot = TOQUAT(body->GetRotation());
-         PosRotScaleNode(n, pos, rot, ni.bhkScaleFactor, prsDefault);
-
-         n->SetPrimaryVisibility(FALSE);
-         n->SetSecondaryVisibility(FALSE);
-         n->BoneAsLine(TRUE);
-         n->SetRenderable(FALSE);
-         n->XRayMtl(TRUE);
-
-         parent->AttachChild(n);
-         ok = true;
+         return n;
       }
    }
-   return ok;
+   return NULL;
 }
 
-bool CollisionImport::ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent)
+INode* CollisionImport::ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef shape, INode *parent)
 {
-   bool ok = false;
    if (SimpleObject *ob = (SimpleObject *)ni.gi->CreateInstance(GEOMOBJECT_CLASS_ID, SCUBA_CLASS_ID)) {
       float radius = shape->GetRadius();
       float radius1 = shape->GetRadius1();
@@ -169,42 +177,24 @@ bool CollisionImport::ImportCapsule(bhkRigidBodyRef body, bhkCapsuleShapeRef sha
       int heighttype = 1;
 
       RefTargetHandle t = ob->GetReference(0);
-
       IParamArray *params = ob->GetParamBlock();
       params->SetValue(ob->GetParamBlockIndex(CAPSULE_RADIUS), 0, radius);
       params->SetValue(ob->GetParamBlockIndex(CAPSULE_HEIGHT), 0, height);
       params->SetValue(ob->GetParamBlockIndex(CAPSULE_CENTERS), 0, heighttype);
 
-      //setMAXScriptValue(t, "radius", 0, radius);
-      //setMAXScriptValue(t, "height", 0, height);
-      //setMAXScriptValue(t, "heighttype", 0, heighttype);
-
       if (INode *n = ni.gi->CreateObjectNode(ob)) {
-         TSTR name = "bhk";
-         name.Append(parent->GetName());
-         n->SetName(name);
-
          // Need to "Affect Pivot Only" and "Center to Object" first
          //n->CenterPivot(0, FALSE);
 
-         Point3 pos = TOPOINT3(body->GetTranslation()) * ni.bhkScaleFactor;
-         Point3 center = TOPOINT3(body->GetCenter());
-         Quat rot = TOQUAT(body->GetRotation());
-         float ang[] = { 0.0f, TORAD(-90.0f), TORAD(180.0f) };
-         Quat q; EulerToQuat(ang, q);
-         rot *= q;
-
-         PosRotScaleNode(n, pos, rot, ni.bhkScaleFactor, prsDefault);
+         // Need to reposition the Capsule so that caps are rotated correctly for pts given
 
-         n->SetPrimaryVisibility(FALSE);
-         n->SetSecondaryVisibility(FALSE);
-         n->BoneAsLine(TRUE);
-         n->SetRenderable(FALSE);
-         n->XRayMtl(TRUE);
-
-         parent->AttachChild(n);
-         ok = true;
+         return n;
       }
    }
-   return ok;
+   return NULL;
+}
+
+INode* CollisionImport::ImportConvexVertices(bhkRigidBodyRef body, bhkConvexVerticesShapeRef shape, INode *parent)
+{
+   return NULL;
 }
\ No newline at end of file
diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp
index 6ed67028aec1652d746fdcb800b47d9051915916..4cfdc3f3d44b7c48092a1a4ede2cab63b641f06b 100644
--- a/NifImport/ImportMeshAndSkin.cpp
+++ b/NifImport/ImportMeshAndSkin.cpp
@@ -12,58 +12,10 @@ HISTORY:
 **********************************************************************/
 #include "stdafx.h"
 #include "MaxNifImport.h"
+#include "istdplug.h"
 
 using namespace Niflib;
 
-// Locate a TriObject in an Object if it exists
-TriObject* GetTriObject(Object *o)
-{
-   if (o && o->CanConvertToType(triObjectClassID))
-      return (TriObject *)o->ConvertToType(0, triObjectClassID);
-   while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o)
-   {
-      IDerivedObject* dobj = (IDerivedObject *)(o);
-      o = dobj->GetObjRef();
-      if (o && o->CanConvertToType(triObjectClassID))
-         return (TriObject *)o->ConvertToType(0, triObjectClassID);
-   }
-   return NULL;
-}
-
-// Get or Create the Skin Modifier
-Modifier *GetSkin(INode *node)
-{
-   Object* pObj = node->GetObjectRef();
-   if (!pObj) return NULL;
-   while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
-   {
-      IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
-      int Idx = 0;
-      while (Idx < pDerObj->NumModifiers())
-      {
-         // Get the modifier. 
-         Modifier* mod = pDerObj->GetModifier(Idx);
-         if (mod->ClassID() == SKIN_CLASSID)
-         {
-            // is this the correct Physique Modifier based on index?
-            return mod;
-         }
-         Idx++;
-      }
-      pObj = pDerObj->GetObjRef();
-   }
-
-   IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
-
-   //create a skin modifier and add it
-   Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
-   dobj->SetAFlag(A_LOCK_TARGET);
-   dobj->AddModifier(skinMod);
-   dobj->ClearAFlag(A_LOCK_TARGET);
-   node->SetObjectRef(dobj);
-   return skinMod;
-}
-
 bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject)
 {
    node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform()));   
@@ -101,7 +53,7 @@ bool NifImporter::ImportMeshes(NiNodeRef node)
    return ok;
 }
 
-bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture)
+bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, vector<Triangle>& tris)
 {
    Mesh& mesh = o->GetMesh();
 
@@ -148,36 +100,48 @@ bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triG
          mesh.setNormal(i, Point3(v.x, v.y, v.z));
       }
    }
-   // vertex coloring
-   {
-      bool hasAlpha = false;
-      vector<Color4> cv = triGeomData->GetColors();
-      mesh.setNumVertCol(cv.size());
-      for (int i=0; i<cv.size(); i++){
-         Color4& c = cv[i];
-         mesh.vertCol[i].Set(c.r, c.g, c.b);
-         hasAlpha |= (c.a != 0.0);
-      }
+
+   // Triangles and texture vertices
+   SetTrangles(mesh, tris);
+
+   ImportVertexColor(node, o, triGeom, triGeomData, tris);
+
+   ImportMaterialAndTextures(node, triGeom);
+
+   if (enableSkinSupport)
+      ImportSkin(node, triGeom);
+
+   i->AddNodeToScene(node);   
+
+   INode *inode = node->GetINode();
+   inode->EvalWorldState(0);
+
+   // attach child
+   if (INode *parent = GetNode(triGeom->GetParent()))
+      parent->AttachChild(inode, 1);
+
+   inode->Hide(triGeom->GetHidden() ? TRUE : FALSE);
+
+   if (enableAutoSmooth){
+      mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
    }
+
    return true;
 }
 
-void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture)
+void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v)
 {
    int n = v.size();
    mesh.setNumFaces(n);
-   //if (hasTexture)
-      mesh.setNumTVFaces(n);
+   mesh.setNumTVFaces(n);
    for (int i=0; i<n; ++i) {
       Triangle& t = v[i];
       Face& f = mesh.faces[i];
       f.setVerts(t.v1, t.v2, t.v3);
       f.Show();
       f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);
-      //if (hasTexture) {
-         TVFace& tf = mesh.tvFace[i];
-         tf.setTVerts(t.v1, t.v2, t.v3);
-      //}
+      TVFace& tf = mesh.tvFace[i];
+      tf.setTVerts(t.v1, t.v2, t.v3);
    }
 }
 
@@ -195,37 +159,13 @@ bool NifImporter::ImportMesh(NiTriShapeRef triShape)
    INode *inode = node->GetINode();
 
    // Texture
-   bool hasTexture = ImportMaterialAndTextures(node, triShape);
    Mesh& mesh = triObject->GetMesh();
    NiTriShapeDataRef triShapeData = DynamicCast<NiTriShapeData>(triShape->GetData());
    if (triShapeData == NULL)
       return false;
 
-   ok |= ImportMesh(node, triObject, triShape, triShapeData, hasTexture);
-
-   // Triangles and texture vertices
-   if (ok)
-   {
-      vector<Triangle> v = triShapeData->GetTriangles();
-      SetTrangles(mesh, v, hasTexture);
-   }
-   if (enableSkinSupport)
-      ImportSkin(node, triShape);
-
-   i->AddNodeToScene(node);   
-
-   // attach child
-   if (INode *parent = GetNode(triShape->GetParent()))
-      parent->AttachChild(inode, 1);
-
-   inode->Hide(triShape->GetHidden() ? TRUE : FALSE);
-   
-   if (enableAutoSmooth){
-      if (TriObject *tri = GetTriObject(inode->GetObjectRef())){
-         tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
-      }
-   }
-
+   vector<Triangle> tris = triShapeData->GetTriangles();
+   ok |= ImportMesh(node, triObject, triShape, triShapeData, tris);
    return ok;
 }
 
@@ -242,43 +182,164 @@ bool NifImporter::ImportMesh(NiTriStripsRef triStrips)
    node->SetName(name.c_str());
 
    // Texture
-   bool hasTexture = ImportMaterialAndTextures(node, triStrips);
-
    Mesh& mesh = triObject->GetMesh();
    NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData());
    if (triStripsData == NULL)
       return false;
 
-   ok |= ImportMesh(node, triObject, triStrips, triStripsData, hasTexture);
+   vector<Triangle> tris = triStripsData->GetTriangles();
+   ok |= ImportMesh(node, triObject, triStrips, triStripsData, tris);
+   return ok;
+}
 
-   // Triangles and texture vertices
-   if (ok)
+// vertex coloring
+bool NifImporter::ImportVertexColor(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, vector<Triangle>& tris)
+{
+   bool hasAlpha = false;
+   bool hasColor = false;
+   Mesh& mesh = o->GetMesh();
+
+   if (vertexColorMode == 1) // Bake into mesh (no Modifier)
    {
-      vector<Triangle> v = triStripsData->GetTriangles();
-      SetTrangles(mesh, v, hasTexture);
-   }
-   if (enableSkinSupport)
-      ImportSkin(node, triStrips);
+      vector<Color4> cv = triGeomData->GetColors();
+      int n = cv.size();
+      if (n > 0)
+      {
+         INode *tnode = node->GetINode();
+
+         vector<TVFace> vcFace;
+         int nt = tris.size();
+         vcFace.resize(nt);
+         mesh.setNumVCFaces(nt);
+         for (int i=0; i<nt; ++i) {
+            Triangle& t = tris[i];
+            TVFace& vcf = vcFace[i];
+            vcf.setTVerts(t.v1, t.v2, t.v3);
+            mesh.vcFace[i].setTVerts(t.v1, t.v2, t.v3);
+         }
 
-   i->AddNodeToScene(node);   
+         vector<VertColor> vertColors, vertAlpha;
+         vertColors.resize(n);
+         vertAlpha.resize(n);
+         mesh.setNumVertCol(cv.size());
+         
+         for (int i=0; i<n; i++){
+            Color4& c = cv[i];
+            hasColor |= (c.r != 1.0f && c.g != 1.0f && c.b != 1.0f);
+            vertColors[i] = Color(c.r,c.g,c.b);
+
+            hasAlpha |= (c.a != 1.0f);
+            vertAlpha[i] = Color(c.a,c.a,c.a);
+         }
 
-   // attach child
-   if (INode *parent = GetNode(triStrips->GetParent())) {
-      parent->AttachChild(inode, 0);
+         // Add the Vertex Paint Alpha modifier now
+         if (hasAlpha)
+         {
+            mesh.setMapSupport(MAP_ALPHA, TRUE);
+            mesh.setNumMapVerts(MAP_ALPHA, n, FALSE);
+            mesh.setNumMapFaces(MAP_ALPHA, nt, FALSE);
+            mesh.setVCDisplayData(MAP_ALPHA, 0, 0);
+            for (int i=0; i<nt; ++i)
+               mesh.vcFaceData[i] = vcFace[i];
+            for (int i=0; i<n; ++i)
+               mesh.vertColArray[i] = vertAlpha[i];
+         }
+         // Add the Vertex Paint Color modifier now
+         if (hasAlpha || hasColor)
+         {
+            mesh.setMapSupport(0, TRUE);
+            mesh.setNumMapVerts(0, n, TRUE);
+            mesh.setNumMapFaces(0, nt, FALSE);
+            mesh.setVCDisplayData(0, NULL, NULL);
+            for (int i=0; i<nt; ++i)
+               mesh.vcFaceData[i] = vcFace[i];
+            for (int i=0; i<n; ++i)
+               mesh.vertColArray[i] = vertColors[i];
+         }
+      }
    }
+   else if (vertexColorMode == 2)
+   {
+      vector<Color4> cv = triGeomData->GetColors();
+      int n = cv.size();
+      if (n > 0)
+      {
 
-   inode->Hide(triStrips->GetHidden() ? TRUE : FALSE);
+         INode *tnode = node->GetINode();
 
-   // apply autosmooth after object creation for it to take hold
-   if (enableAutoSmooth){
-      if (TriObject *tri = GetTriObject(inode->GetObjectRef())){
-         tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
+         vector<Color> colorMap, alphaMap;
+         IVertexPaint::VertColorTab vertColors, vertAlpha;
+         vertColors.SetCount(n, TRUE);
+         vertAlpha.SetCount(n, TRUE);
+         colorMap.resize(n);
+         alphaMap.resize(n);
+         mesh.setNumVertCol(cv.size());
+
+         for (int i=0; i<n; i++){
+            Color4& c = cv[i];
+            mesh.vertCol[i].Set(c.r, c.g, c.b);
+
+            hasColor |= (c.r != 1.0f && c.g != 1.0f && c.b != 1.0f);
+            colorMap[i] = Color(c.r,c.g,c.b);
+            vertColors[i] = &colorMap[i];
+
+            hasAlpha |= (c.a != 1.0f);
+            alphaMap[i] = Color(c.a,c.a,c.a);
+            vertAlpha[i] = &alphaMap[i];
+         }
+         // Civ4 assumes that vcFace is filled in even if only alpha is given via color modifier
+         if (hasColor || hasAlpha)
+         {
+            int n = tris.size();
+            mesh.setNumVCFaces(n);
+            for (int i=0; i<n; ++i) {
+               Triangle& t = tris[i];
+               TVFace& vcf = mesh.vcFace[i];
+               vcf.setTVerts(t.v1, t.v2, t.v3);
+            }
+         }
+         // Add the Vertex Paint Color modifier now
+         if (hasColor)
+         {
+            IDerivedObject *dobj = CreateDerivedObject(tnode->GetObjectRef());
+            Modifier * mod = (Modifier*)CreateInstance(OSM_CLASS_ID, PAINTLAYERMOD_CLASS_ID);
+            dobj->AddModifier(mod);
+            tnode->SetObjectRef(dobj);
+            IVertexPaint* ivertexPaint = (IVertexPaint*)mod->GetInterface(IVERTEXPAINT_INTERFACE_ID);
+            ObjectState os = tnode->EvalWorldState(0);
+            IAssignVertexColors::Options o;
+            ivertexPaint->GetOptions(o);
+            o.mapChannel = 0;
+            o.mixVertColors = true;
+            ivertexPaint->SetOptions(o);
+            ivertexPaint->SetColors(tnode, vertColors);
+            //mod->DisableModInViews();
+            //mod->EnableModInViews();
+         }
+         // Add the Vertex Paint Alpha modifier now
+         if (hasAlpha)
+         {
+            IDerivedObject *dobj = CreateDerivedObject(tnode->GetObjectRef());
+            Modifier * mod = (Modifier*)CreateInstance(OSM_CLASS_ID, PAINTLAYERMOD_CLASS_ID);
+            dobj->AddModifier(mod);
+            tnode->SetObjectRef(dobj);
+            IVertexPaint* ivertexPaint = (IVertexPaint*)mod->GetInterface(IVERTEXPAINT_INTERFACE_ID);
+            ObjectState os = tnode->EvalWorldState(0);
+            IAssignVertexColors::Options o;
+            ivertexPaint->GetOptions(o);
+            o.mapChannel = -2;
+            o.mixVertColors = true;
+            ivertexPaint->SetOptions(o);
+            ivertexPaint->SetColors(tnode, vertAlpha);
+            //mod->DisableModInViews();
+            //mod->EnableModInViews();
+         }
       }
    }
-
-   return ok;
+   return (hasAlpha || hasColor);
 }
 
+
 struct VertexHolder
 {
    VertexHolder() : vertIndex(0), count(0) {}
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
index 69038d7c47da7c42e814c2cc48e744d0a53afa9a..885d9307f21f757fe5c7491ac6dfe76bc6b1a99a 100644
--- a/NifImport/ImportSkeleton.cpp
+++ b/NifImport/ImportSkeleton.cpp
@@ -12,7 +12,9 @@ HISTORY:
 **********************************************************************/
 #include "stdafx.h"
 #include "MaxNifImport.h"
+#ifdef USE_BIPED
 #include <cs/BipedApi.h>
+#endif
 #include <obj/NiTriBasedGeom.h>
 #include <obj/NiTriBasedGeomData.h>
 #include <obj/NiTimeController.h>
@@ -486,6 +488,7 @@ INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPo
                setMAXScriptValue(o->GetReference(0), "width", 0, width);
                setMAXScriptValue(o->GetReference(0), "height", 0, width);
             }
+            n->BoneAsLine(1);
             n->ShowBone(2);
          }
          return result.n;
diff --git a/NifImport/MaxNifImport_VC80.vcproj b/NifImport/MaxNifImport_VC80.vcproj
index 204c390433eda2e6a4621b0f44e44eefe845fa1b..d05d46e9cfaefeadbf4298674f94c3230d2a2f02 100644
--- a/NifImport/MaxNifImport_VC80.vcproj
+++ b/NifImport/MaxNifImport_VC80.vcproj
@@ -797,10 +797,6 @@
 				>
 			</File>
 		</Filter>
-		<File
-			RelativePath="..\MaxNifPlugins_Readme.txt"
-			>
-		</File>
 	</Files>
 	<Globals>
 	</Globals>
diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp
index f02484a96bd5e64b62047ea0c1425b273f52d7fa..b8f9dc9f34800d879348115b1076b5c9f19c17b2 100644
--- a/NifImport/NIFImport.cpp
+++ b/NifImport/NIFImport.cpp
@@ -116,40 +116,41 @@ void NifImporter::LoadIniSettings()
    }
 
    // General System level
-   useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false);
+   useBiped = GetIniValue(NifImportSection, "UseBiped", false);
    skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*");
-   showTextures = GetIniValue<bool>(NifImportSection, "ShowTextures", true);
-   removeIllegalFaces = GetIniValue<bool>(NifImportSection, "RemoveIllegalFaces", true);
-   removeDegenerateFaces = GetIniValue<bool>(NifImportSection, "RemoveDegenerateFaces", true);
-   enableAutoSmooth = GetIniValue<bool>(NifImportSection, "EnableAutoSmooth", true);
-   autoSmoothAngle = GetIniValue<float>(NifImportSection, "AutoSmoothAngle", 30.0f);
-   flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true);
-   enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true);
-   enableCollision = GetIniValue<bool>(NifImportSection, "EnableCollision", true);
+   showTextures = GetIniValue(NifImportSection, "ShowTextures", true);
+   removeIllegalFaces = GetIniValue(NifImportSection, "RemoveIllegalFaces", true);
+   removeDegenerateFaces = GetIniValue(NifImportSection, "RemoveDegenerateFaces", true);
+   enableAutoSmooth = GetIniValue(NifImportSection, "EnableAutoSmooth", true);
+   autoSmoothAngle = GetIniValue(NifImportSection, "AutoSmoothAngle", 30.0f);
+   flipUVTextures = GetIniValue(NifImportSection, "FlipUVTextures", true);
+   enableSkinSupport = GetIniValue(NifImportSection, "EnableSkinSupport", true);
+   enableCollision = GetIniValue(NifImportSection, "EnableCollision", true);
+   vertexColorMode = GetIniValue<int>(NifImportSection, "VertexColorMode", 1);
 
    // Biped
-   importBones = GetIniValue<bool>(BipedImportSection, "ImportBones", true);
-   bipedHeight = GetIniValue<float>(BipedImportSection, "BipedHeight", 131.90f);
-   bipedAngle = GetIniValue<float>(BipedImportSection, "BipedAngle", 90.0f);
-   bipedAnkleAttach = GetIniValue<float>(BipedImportSection, "BipedAnkleAttach", 0.2f);
-   bipedTrianglePelvis = GetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", false);
-   removeUnusedImportedBones = GetIniValue<bool>(BipedImportSection, "RemoveUnusedImportedBones", false);
-   forceRotation = GetIniValue<bool>(BipedImportSection, "ForceRotation", true);
-   browseForSkeleton = GetIniValue<bool>(BipedImportSection, "BrowseForSkeleton", true);
+   importBones = GetIniValue(BipedImportSection, "ImportBones", true);
+   bipedHeight = GetIniValue(BipedImportSection, "BipedHeight", 131.90f);
+   bipedAngle = GetIniValue(BipedImportSection, "BipedAngle", 90.0f);
+   bipedAnkleAttach = GetIniValue(BipedImportSection, "BipedAnkleAttach", 0.2f);
+   bipedTrianglePelvis = GetIniValue(BipedImportSection, "BipedTrianglePelvis", false);
+   removeUnusedImportedBones = GetIniValue(BipedImportSection, "RemoveUnusedImportedBones", false);
+   forceRotation = GetIniValue(BipedImportSection, "ForceRotation", true);
+   browseForSkeleton = GetIniValue(BipedImportSection, "BrowseForSkeleton", true);
    defaultSkeletonName = GetIniValue<string>(BipedImportSection, "DefaultSkeletonName", "Skeleton.Nif");
-   minBoneWidth = GetIniValue<float>(BipedImportSection, "MinBoneWidth", 0.5f);
-   maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f);
-   boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f);
-   createNubsForBones = GetIniValue<bool>(BipedImportSection, "CreateNubsForBones", true);
+   minBoneWidth = GetIniValue(BipedImportSection, "MinBoneWidth", 0.5f);
+   maxBoneWidth = GetIniValue(BipedImportSection, "MaxBoneWidth", 3.0f);
+   boneWidthToLengthRatio = GetIniValue(BipedImportSection, "BoneWidthToLengthRatio", 0.25f);
+   createNubsForBones = GetIniValue(BipedImportSection, "CreateNubsForBones", true);
    dummyNodeMatches = TokenizeString(GetIniValue<string>(BipedImportSection, "DummyNodeMatches", "").c_str(), ";");
-   convertBillboardsToDummyNodes = GetIniValue<bool>(BipedImportSection, "ConvertBillboardsToDummyNodes", true);
-   uncontrolledDummies = GetIniValue<bool>(BipedImportSection, "UncontrolledDummies", true);
+   convertBillboardsToDummyNodes = GetIniValue(BipedImportSection, "ConvertBillboardsToDummyNodes", true);
+   uncontrolledDummies = GetIniValue(BipedImportSection, "UncontrolledDummies", true);
 
    // Animation
-   replaceTCBRotationWithBezier = GetIniValue<bool>(AnimImportSection, "ReplaceTCBRotationWithBezier", true);
-   enableAnimations = GetIniValue<bool>(AnimImportSection, "EnableAnimations", true);
-   requireMultipleKeys = GetIniValue<bool>(AnimImportSection, "RequireMultipleKeys", true);
-   applyOverallTransformToSkinAndBones = GetIniValue<bool>(AnimImportSection, "ApplyOverallTransformToSkinAndBones", true);
+   replaceTCBRotationWithBezier = GetIniValue(AnimImportSection, "ReplaceTCBRotationWithBezier", true);
+   enableAnimations = GetIniValue(AnimImportSection, "EnableAnimations", true);
+   requireMultipleKeys = GetIniValue(AnimImportSection, "RequireMultipleKeys", true);
+   applyOverallTransformToSkinAndBones = GetIniValue(AnimImportSection, "ApplyOverallTransformToSkinAndBones", true);
 
    // Collision
    bhkScaleFactor = GetIniValue<float>(CollisionSection, "bhkScaleFactor", 7.0f);
@@ -169,14 +170,14 @@ void NifImporter::LoadIniSettings()
 
 void NifImporter::SaveIniSettings()
 {
-   SetIniValue<bool>(NifImportSection, "UseBiped", useBiped);
-   SetIniValue<string>(NifImportSection, "Skeleton", skeleton);
-   SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck);
-
-   SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight);
-   SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle);
-   SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach);
-   SetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis);
+   //SetIniValue(NifImportSection, "UseBiped", useBiped);
+   //SetIniValue<string>(NifImportSection, "Skeleton", skeleton);
+   //SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck);
+
+   //SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight);
+   //SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle);
+   //SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach);
+   //SetIniValue(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis);
 }
 
 INode *NifImporter::GetNode(Niflib::NiNodeRef node)
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
index 4f543ed198395d5597f17b50f420bc5f95e93293..c15cbf1d72103962de741a3174e6e1ba589f41d5 100644
--- a/NifImport/NIFImporter.h
+++ b/NifImport/NIFImporter.h
@@ -29,6 +29,7 @@ public:
    bool enableSkinSupport;
    bool goToSkeletonBindPosition;
    bool enableCollision;
+   int vertexColorMode;
 
    // Biped/Bones related settings
    bool importBones;
@@ -87,12 +88,13 @@ public:
 
    bool ImportUPB(INode *node, Niflib::NiNodeRef block);
 
-   void SetTrangles(Mesh& mesh, vector<Niflib::Triangle>& v, bool hasTexture);
+   void SetTrangles(Mesh& mesh, vector<Niflib::Triangle>& v);
    bool ImportMesh(Niflib::NiTriShapeRef triShape);
    bool ImportMesh(Niflib::NiTriStripsRef triStrips);
    bool ImportMaterialAndTextures(ImpNode *node, Niflib::NiAVObjectRef avObject);
    bool ImportTransform(ImpNode *node, Niflib::NiAVObjectRef avObject);
-   bool ImportMesh(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, bool hasTexture);
+   bool ImportMesh(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, vector<Niflib::Triangle>& tris);
+   bool ImportVertexColor(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, vector<Niflib::Triangle>& tris);
 
    bool ImportSkin(ImpNode *node, Niflib::NiTriBasedGeomRef triGeom);
    Texmap* CreateTexture(Niflib::TexDesc& desc);