<NME> common.def.NOTUSED
<BEF> ADDFILE
<MSG> exploring alternative modifier formulations.

<DFF> @@ -0,0 +1,9 @@
+EXPORTS
+   LibDescription       @1       PRIVATE
+   LibNumberClasses     @2       PRIVATE
+   LibClassDesc         @3       PRIVATE
+   LibVersion           @4       PRIVATE
+   LibInitialize		@5       PRIVATE
+   LibShutdown			@6       PRIVATE
+SECTIONS
+   .data READ WRITE

{'commit': 'diff --git a/2011/CMakeLists.txt b/2011/CMakeLists.txt\nindex c8919a5a..fe8ee5a8 100644\n--- a/2011/CMakeLists.txt\n+++ b/2011/CMakeLists.txt\n@@ -1,4 +1,4 @@\n cmake_minimum_required (VERSION 2.6) \n \n-set( Max_VERSION "2011" )\n+set( Max_VERSION "2011" ) \n include( ../CMakeLists.txt )\n\\ No newline at end of file\ndiff --git a/2012/CMakeLists.txt b/2012/CMakeLists.txt\nindex eeb24f4f..1ca61a3c 100644\n--- a/2012/CMakeLists.txt\n+++ b/2012/CMakeLists.txt\n@@ -1,4 +1,4 @@\n cmake_minimum_required (VERSION 2.6) \n \n-set( Max_VERSION "2012" )\n+set( Max_VERSION "2012" ) \n include( ../CMakeLists.txt )\n\\ No newline at end of file\ndiff --git a/Alembic.aps b/Alembic.aps\nnew file mode 100644\nindex 00000000..bf3a059b\nBinary files /dev/null and b/Alembic.aps differ\ndiff --git a/AlembicMesh2Deformer.h b/AlembicMesh2Deformer.h\nnew file mode 100644\nindex 00000000..83b19503\n--- /dev/null\n+++ b/AlembicMesh2Deformer.h\n@@ -0,0 +1,157 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/**\n+ * Manages the deformation of control points for the BendModifier. \n+ */\n+class AlembicMesh2Deformer\n+    : public Deformer \n+{\n+\tpublic:\n+\t\tMatrix3 tm,invtm, tmAbove, tmBelow;\n+\t\tBox3 bbox;\n+\t\tTimeValue time;\n+\t\tfloat r, from, to;\n+\t\tint doRegion;\n+\t\t\n+        AlembicMesh2Deformer()\n+            : tm(1), invtm(1), tmAbove(1), tmBelow(1), time(0)\n+        { }\n+\n+\t\tAlembicMesh2Deformer(TimeValue t, ModContext &mc,\n+\t\t\tfloat angle, float dir, int naxis, \n+\t\t\tfloat from, float to, int doRegion, \n+\t\t\tMatrix3& modmat, Matrix3& modinv)\n+        {\n+\t        this->doRegion = doRegion;\n+\t        this->from = from;\n+\t        this->to   = to;\n+\t        Matrix3 mat;\n+\t        Interval valid;\t\n+\t        time   = t;\t\n+\n+\t        tm = modmat;\n+\t        invtm = modinv;\n+\t        mat.IdentityMatrix();\n+        \t\n+\t        switch (naxis) \n+            {\n+\t\t        case 0: mat.RotateY( -HalfPi() );\t break; //X\n+\t\t        case 1: mat.RotateX( HalfPi() );  break; //Y\n+\t\t        case 2: break;  //Z\n+\t\t    }\n+\t        mat.RotateZ(DegToRad(dir));\t\n+\t        SetAxis(mat);\t\n+\t        assert (mc.box);\n+\t        bbox = *mc.box;\n+\t        CalcR(naxis,DegToRad(angle));\n+        \t\n+\t        // Turn this off for a sec.\n+\t        this->doRegion = FALSE;\n+        \t\t\n+\t        float len  = to-from;\n+\t        float rat1, rat2;\n+\t        if (len==0.0f) {\n+\t\t        rat1 = rat2 = 1.0f;\n+\t        } else {\n+\t\t        rat1 = to/len;\n+\t\t        rat2 = from/len;\n+\t\t    }\n+\t        Point3 pt;\n+\t        tmAbove.IdentityMatrix();\n+\t        tmAbove.Translate(Point3(0.0f,0.0f,-to));\n+\t        tmAbove.RotateY(DegToRad(angle * rat1));\n+\t        tmAbove.Translate(Point3(0.0f,0.0f,to));\n+\t        pt = Point3(0.0f,0.0f,to);\n+\t        tmAbove.Translate((Map(0,pt*invtm)*tm)-pt);\n+\n+\t        tmBelow.IdentityMatrix();\n+\t        tmBelow.Translate(Point3(0.0f,0.0f,-from));\n+\t        tmBelow.RotateY(DegToRad(angle * rat2));\t\n+\t        tmBelow.Translate(Point3(0.0f,0.0f,from));\n+\t        pt = Point3(0.0f,0.0f,from);\n+\t        tmBelow.Translate((Map(0,pt*invtm)*tm)-pt);\t\n+        \t\n+\t        this->doRegion = doRegion;\n+        }\n+\t\t\n+        void SetAxis(Matrix3 &tmAxis)\n+        {\n+\t        Matrix3 itm = Inverse(tmAxis);\n+\t        tm = tm * tmAxis;\n+\t        invtm =\titm * invtm;\n+        }\n+\n+\t\tvoid CalcR(int axis, float angle)\n+        {\n+\t        float len = float(0);\n+\t        if (!doRegion) \n+            {\n+\t\t        switch (axis) \n+                {\n+\t\t\t        case 0: \n+                        len = bbox.pmax.x - bbox.pmin.x; \n+                        break;\n+\t\t\t        case 1:\t\n+                        len = bbox.pmax.y - bbox.pmin.y; \n+                        break;\n+\t\t\t        case 2: \n+                        len = bbox.pmax.z - bbox.pmin.z; \n+                        break;\n+                }\n+\t\t    }\n+\t        else \n+            {\n+\t\t        len = to-from;\n+\t\t    }\n+\n+            r = SafeDivide(len, angle);\n+    \t}\n+\t\t\n+        // This method gets called for each point. \n+\t\tPoint3 Map(int i, Point3 p)\n+        {\n+\t        float x, y, c, s, yr;\n+\t        if (r==0 && !doRegion) return p;\n+\t        \n+            // The point is first transformed by the tm.  This is typical\n+\t        // for all modifiers. \n+\t        p = p * tm;\n+\t        if (doRegion) {\n+\t\t        if (p.z<from) {\n+\t\t\t        return tmBelow * p * invtm;\t\t\t\n+\t\t        } else \n+\t\t        if (p.z>to) {\n+\t\t\t        return tmAbove * p * invtm;\n+\t\t\t    }\n+\t\t    }\t\n+\t        \n+            if (r==0) \n+                return p * invtm;\n+\t        \n+            x = p.x;\n+\t        y = p.z;\n+\t        yr = y/r;\n+\t        \n+            c = cos(Pi() - yr);\n+\t        s = sin(Pi() - yr);\n+\t        \n+            p.x = r*c + r - x*c;\n+\t        p.z = r*s - x*s;\n+\n+\t        // The point is finally transformed by the inverse of the tm.\n+\t        p = p * invtm;\n+\t        return p;\n+\t    }\n+};\n+\ndiff --git a/AlembicMesh2Modifier.cpp b/AlembicMesh2Modifier.cpp\nnew file mode 100644\nindex 00000000..c5b246cd\n--- /dev/null\n+++ b/AlembicMesh2Modifier.cpp\n@@ -0,0 +1,281 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#include "AlembicMesh2Modifier.h"\n+\n+// Static DLL handle\n+static HINSTANCE hDllInstance = 0; \n+\n+//==========================================================================\n+// Class descriptor implementation\n+\n+int AlembicMesh2ModifierClassDesc::IsPublic() \n+{ \n+    return 1; \n+} \n+void* AlembicMesh2ModifierClassDesc::Create(BOOL loading) \n+{ \n+    return new AlembicMesh2Modifier(); \n+} \n+const MCHAR* AlembicMesh2ModifierClassDesc::ClassName() \n+{ \n+    return _M("Modifier tutorial"); \n+} \n+SClass_ID AlembicMesh2ModifierClassDesc::SuperClassID() \n+{ \n+    return OSM_CLASS_ID;\n+} \n+Class_ID AlembicMesh2ModifierClassDesc::ClassID() \n+{ \n+    return MODIFIER_TUTORIAL_CLASS_ID; \n+} \n+const MCHAR* AlembicMesh2ModifierClassDesc::Category() \n+{ \n+    return SDK_TUTORIALS_CATEGORY; \n+}  \n+const MCHAR* AlembicMesh2ModifierClassDesc::GetInternalName() \n+{ \n+    return _M("AlembicMesh2Modifier"); \n+} \n+HINSTANCE AlembicMesh2ModifierClassDesc::HInstance() \n+{ \n+    return hDllInstance; \n+} \n+ClassDesc2* AlembicMesh2ModifierClassDesc::GetInstance() \n+{\n+    static AlembicMesh2ModifierClassDesc desc;\n+    return &desc;\n+}\n+\n+//============================================================\n+// Define the static parameter block descriptor\n+\n+static float BIGFLOAT = 999999.0f;\n+\n+// This class derives from ParamBlockDesc2, but adds a number of convenience functions \n+// for constructing parameter block descriptors incrementally.\n+// The ParamBlockDescUtil assumes the flags "P_AUTO_UI" and "P_AUTO_CREATE"\n+\n+class AlembicMesh2ModifierParams\n+    : public ParamBlockDescUtil\n+{\n+private:\n+    //============================================================\n+    // Allows for a call back mechanism on GetValue / SetValue calls. You can check the value\n+    // being passed by PB2Value and to manipulate it as necessary.\n+    // Implement the check here for from/to crossover.  \n+\n+    class BendPBAccessor : public PBAccessor\n+    {\n+    public:\n+\t\tvoid Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int /*tabIndex*/, TimeValue /*t*/)    // set from v\n+\t\t{\n+\t\t\tAlembicMesh2Modifier* u = dynamic_cast<AlembicMesh2Modifier*>(owner);\n+\t\t\tif (u == NULL)\n+\t\t\t\treturn;\n+\n+\t\t\tfloat from;\n+\t\t\tfloat to;\n+\n+\t\t\tswitch (id)\n+\t\t\t{\n+\t\t\t\tcase AlembicMesh2Modifier::FROM_PARAM_ID:\t\t\t\t\n+\t\t\t\t\tto = u->GetTo();\n+\t\t\t\t\tfrom = v.f;\n+\t\t\t\t\tif (from > to) \n+\t\t\t\t\t\tu->SetTo(from);\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\tcase AlembicMesh2Modifier::TO_PARAM_ID:\n+\t\t\t\t\tfrom = u->GetFrom();\n+\t\t\t\t\tto = v.f;\n+\t\t\t\t\tif (from > to) \n+\t\t\t\t\t\tu->SetFrom(to);\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t};\n+\n+    BendPBAccessor  bendPBAccessor;\n+\n+public:        \n+\n+    AlembicMesh2ModifierParams()\n+        : ParamBlockDescUtil(\n+\t\t\t\t  AlembicMesh2Modifier::PBLOCK_ID\n+\t\t\t\t, _M("AlembicMesh2ModifierParameters")\n+\t\t\t\t, IDS_RB_PARAMETERS\n+\t\t\t\t, AlembicMesh2ModifierClassDesc::GetInstance()\n+\t\t\t\t, 0\n+\t\t\t\t, 0 // Reference System ID for the Parameter Block\n+\t\t\t\t, IDD_BENDPARAM\n+\t\t\t\t, IDS_RB_PARAMETERS\n+\t\t\t\t, NULL\n+\t\t\t\t)\n+    {\n+        int id = AlembicMesh2Modifier::ANGLE_PARAM_ID;\n+        AddParam(id, _T("Angle"),\tTYPE_FLOAT,\tP_RESET_DEFAULT|P_ANIMATABLE, IDS_ANGLE, end);\n+\t\tParamOption(id, p_default,  0.0f, end);\n+\t\tParamOption(id, p_range,    -BIGFLOAT, BIGFLOAT, end);\n+\t\tParamOption(id, p_ui,       TYPE_SPINNER, EDITTYPE_FLOAT, IDC_ANGLE, IDC_ANGLESPINNER, 0.5f, end);\n+\n+        id = AlembicMesh2Modifier::DIR_PARAM_ID;\n+        AddParam(id, _T("Direction"),\tTYPE_FLOAT,\tP_RESET_DEFAULT|P_ANIMATABLE, IDS_DIR, end);\n+\t\tParamOption(id, p_default,  0.0f, end);\n+\t\tParamOption(id, p_range,    -BIGFLOAT, BIGFLOAT, end);\n+\t\tParamOption(id, p_ui,       TYPE_SPINNER, EDITTYPE_FLOAT, IDC_DIR, IDC_DIRSPINNER, 0.5f, end);\n+\n+        id = AlembicMesh2Modifier::AXIS_PARAM_ID;\n+        AddParam(id, _T("Axis"), TYPE_INT, P_RESET_DEFAULT,\tIDS_AXIS, end);\n+\t\tParamOption(id, p_default,  2, end);\n+\t\tParamOption(id, p_ui,       TYPE_RADIO, 3,IDC_X,IDC_Y,IDC_Z, \n+\t\t\t\t\t\tp_vals,     0,1,2, end); // Notice here how p_vals is passed in with p_ui to ParamOption. \n+\n+\t\tid = AlembicMesh2Modifier::FROM_TO_PARAM_ID;\n+        AddParam(id, _T("FromTo"), TYPE_BOOL, P_RESET_DEFAULT, IDS_FROMTO, end);\n+        ParamOption(id, p_default,  FALSE, end);\n+\t\tParamOption(id, p_ui,       TYPE_SINGLECHEKBOX, IDC_BEND_AFFECTREGION, end);\n+\n+        id = AlembicMesh2Modifier::FROM_PARAM_ID;\n+        AddParam(id, _T("From"), TYPE_FLOAT, P_RESET_DEFAULT|P_ANIMATABLE, IDS_FROM, end);\n+\t\tParamOption(id, p_default,  0.0f, end);\n+\t\tParamOption(id, p_range,    -BIGFLOAT, 0.0f, end);\n+\t\tParamOption(id, p_ui,       TYPE_SPINNER, EDITTYPE_UNIVERSE, IDC_BEND_FROM, IDC_BEND_FROMSPIN, SPIN_AUTOSCALE, end);\n+\t\tParamOption(id, p_accessor, &bendPBAccessor, end);\n+\n+        id = AlembicMesh2Modifier::TO_PARAM_ID;\n+        AddParam(id, _T("To"), TYPE_FLOAT, P_RESET_DEFAULT|P_ANIMATABLE, IDS_TO, end);\n+\t\tParamOption(id, p_default,\t0.0f, end);\n+\t\tParamOption(id, p_range, \t0.0f, BIGFLOAT, end);\n+\t\tParamOption(id, p_ui,\t\tTYPE_SPINNER, EDITTYPE_UNIVERSE, IDC_BEND_TO, IDC_BEND_TOSPIN, SPIN_AUTOSCALE, end);\n+\t\tParamOption(id, p_accessor, &bendPBAccessor, end);\n+\n+        // Prevent further changes to the parameter block descriptor\n+        SetFinished(); \n+    }\n+\n+    // Create a parameter block descriptor\n+    static AlembicMesh2ModifierParams* GetInstance()\n+    {\n+        static AlembicMesh2ModifierParams desc;\n+        return &desc;\n+    }\n+};\n+\n+//============================================================\n+// DLL Main functions \n+\n+BOOL WINAPI DllMain(HINSTANCE hinstDLL, ULONG fdwReason, LPVOID lpvReserved) { \n+    // NOTE: Do not call managed code from here.\n+    // You should do any initialization in LibInitialize\n+\thDllInstance = hinstDLL; \n+\tswitch(fdwReason) { \n+\t\tcase DLL_PROCESS_ATTACH: break; \n+\t\tcase DLL_THREAD_ATTACH: break; \n+\t\tcase DLL_THREAD_DETACH: break; \n+\t\tcase DLL_PROCESS_DETACH: break; \n+\t} \n+\treturn(TRUE); \n+} \n+__declspec( dllexport ) const TCHAR * LibDescription() \n+{ \n+\treturn _T("Modifier tutorial plug-in project."); \n+} \n+__declspec( dllexport ) int LibNumberClasses() \n+{ \n+\treturn 1;  \n+} \n+__declspec( dllexport ) ClassDesc2* LibClassDesc(int i) \n+{ \n+    return AlembicMesh2ModifierClassDesc::GetInstance();\n+} \n+__declspec( dllexport ) ULONG LibVersion() \n+{ \n+\treturn VERSION_3DSMAX; \n+} \n+__declspec( dllexport ) ULONG CanAutoDefer() \n+{ \n+\treturn 1; \n+} \n+__declspec( dllexport ) ULONG LibInitialize() \n+{ \n+    AlembicMesh2ModifierParams::GetInstance();\n+    return 1;\n+}\n+__declspec( dllexport ) ULONG LibShutdown() \n+{ \n+    return 1;\n+}\n+\n+//==========================================================================================================\n+// AlembicMesh2Modifier implementations\n+\n+AlembicMesh2Modifier::AlembicMesh2Modifier() : ModifierPlugin<Modifier>(AlembicMesh2ModifierClassDesc::GetInstance())\n+{\n+    // Ask the class descriptor to make the parameter blocks\n+    // This will trigger 3ds Max to call ReplaceReference with the \n+    // constructed parameter block \n+    AlembicMesh2ModifierClassDesc::GetInstance()->MakeAutoParamBlocks(this);\n+}\n+\n+RefTargetHandle AlembicMesh2Modifier::Clone(RemapDir &remap) \n+{        \n+    AlembicMesh2Modifier* r = new AlembicMesh2Modifier();\n+    BaseClone(this, r, remap);\n+    return r;\n+}\n+\n+Deformer* AlembicMesh2Modifier::GetDeformer(TimeValue t, ModContext &mc, Matrix3& mat, Matrix3& invmat) \n+{\n+    deformer = AlembicMesh2Deformer(t, mc, GetAngle(t), GetDirection(t),\n+        GetAxis(t), GetFrom(t), GetTo(t), GetFromTo(t), mat, invmat);\n+    return &deformer;\n+}\n+\n+float AlembicMesh2Modifier::GetAngle(TimeValue t)\n+{\n+    return GetParameter<float>(ANGLE_PARAM_ID, t);\n+}\n+\n+float AlembicMesh2Modifier::GetDirection(TimeValue t)\n+{\n+    return GetParameter<float>(DIR_PARAM_ID, t);\n+}\n+\n+int AlembicMesh2Modifier::GetAxis(TimeValue t)\n+{\n+    return GetParameter<int>(AXIS_PARAM_ID, t);\n+}\n+\n+float AlembicMesh2Modifier::GetFrom(TimeValue t)\n+{\n+    return GetParameter<float>(FROM_PARAM_ID, t);\n+}\n+\n+float AlembicMesh2Modifier::GetTo(TimeValue t)\n+{\n+    return GetParameter<float>(TO_PARAM_ID, t);\n+}\n+\n+void AlembicMesh2Modifier::SetFrom(float x, TimeValue t)\n+{\n+    SetParameter(FROM_PARAM_ID, x, t);\n+}\n+\n+void AlembicMesh2Modifier::SetTo(float x, TimeValue t)\n+{\n+    SetParameter(TO_PARAM_ID, x, t);\n+}\n+\n+BOOL AlembicMesh2Modifier::GetFromTo(TimeValue t)\n+{\n+    return GetParameter<BOOL>(FROM_TO_PARAM_ID, t);\n+}\ndiff --git a/AlembicMesh2Modifier.h b/AlembicMesh2Modifier.h\nnew file mode 100644\nindex 00000000..7a69a0d3\n--- /dev/null\n+++ b/AlembicMesh2Modifier.h\n@@ -0,0 +1,109 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "resource.h"\n+#include "Shared/Common.h"\n+#include "macrorec.h"\n+#include "Shared/ModifierPlugin.h"\n+#include "AlembicMesh2Deformer.h"\n+\n+const Class_ID MODIFIER_TUTORIAL_CLASS_ID = Class_ID(0x25725b80, 0x251b3ee9);\n+\n+//============================================================\n+// Class descriptor declaration\n+\n+/**\n+ * The class descriptor for AlembicMesh2Modifier.\n+ */\n+class AlembicMesh2ModifierClassDesc\n+    : public ClassDesc2 \n+{\n+public: \n+    //---------------------------------------\n+    // ClassDesc2 overrides \n+\n+    virtual int IsPublic();\n+    virtual void* Create(BOOL loading = FALSE);\n+    virtual const MCHAR* ClassName();\n+    virtual SClass_ID SuperClassID();\n+    virtual Class_ID ClassID();\n+    virtual const MCHAR* Category();\n+    virtual const MCHAR* GetInternalName();\n+    virtual HINSTANCE HInstance();\n+\n+    //---------------------------------------\n+    // Returns a singleton instance of the class descriptor\n+    static ClassDesc2* GetInstance();\n+}; \n+\n+//============================================================\n+// The plug-in definition\n+\n+/**\n+ * This plug-in demonstrates a bend modifier which is a re-implementation\n+ * of the standard 3ds Max bend modifier. The following concepts are \n+ * demonstrated: \n+ * \\li Implementing a plug-in derived from Modifier\n+ * \\li Using a Deformer to implement a plug-in.\n+ */\n+class AlembicMesh2Modifier \n+    : public ModifierPlugin<Modifier>\n+{\n+private:\n+\n+\t//============================================================\n+\t// Member fields\n+\n+\tAlembicMesh2Deformer deformer;\n+\n+public:\n+\t//============================================================\n+\t// Parameter Block Identifier\n+\tstatic const BlockID        PBLOCK_ID = 0;\n+\n+    //============================================================\n+    // Parameter identifiers \n+    \n+    static const ParamID ANGLE_PARAM_ID = 0;\n+    static const ParamID DIR_PARAM_ID = 1;\n+    static const ParamID AXIS_PARAM_ID = 2;\n+    static const ParamID FROM_TO_PARAM_ID = 3;\n+    static const ParamID FROM_PARAM_ID = 4;\n+    static const ParamID TO_PARAM_ID = 5;\n+\n+\tAlembicMesh2Modifier();\n+\n+\t//============================================================\n+    // RefMaker overrides \n+\n+\tvirtual RefTargetHandle Clone(RemapDir &remap);\n+\n+    //============================================================\n+    // ModifierPlugin overrides \n+\n+    virtual Deformer* GetDeformer(TimeValue t, ModContext &mc, Matrix3& mat, Matrix3& invmat);\n+\n+    //=========================================================================\n+    // BendModifier specific functions for accessing the parameters.\n+\n+    float GetAngle(TimeValue t);\n+    float GetDirection(TimeValue t = Now());\n+    int GetAxis(TimeValue t = Now());\n+    float GetFrom(TimeValue t = Now());\n+    float GetTo(TimeValue t = Now());\n+    void SetFrom(float x, TimeValue t = Now());\n+    void SetTo(float x, TimeValue t = Now());\n+    BOOL GetFromTo(TimeValue t = Now());\n+};\n+\n+//======================================================================\ndiff --git a/AlembicMeshModifier.cpp b/AlembicMeshModifier.cpp\nnew file mode 100644\nindex 00000000..47d2db05\n--- /dev/null\n+++ b/AlembicMeshModifier.cpp\n@@ -0,0 +1,329 @@\n+#include "Alembic.h"\n+#include "AlembicDefinitions.h"\n+#include "AlembicMeshModifier.h"\n+#include "AlembicArchiveStorage.h"\n+#include "utility.h"\n+#include <iparamb2.h>\n+#include <MeshNormalSpec.h>\n+#include <Assetmanagement\\AssetType.h>\n+#include "alembic.h"\n+#include "AlembicXForm.h"\n+#include "AlembicVisCtrl.h"\n+\n+using namespace MaxSDK::AssetManagement;\n+const int POLYMESHMOD_MAX_PARAM_BLOCKS = 1;\n+\n+class AlembicMeshModifier : public Modifier {\n+public:\n+\tstatic const BlockID PBLOCK_ID = 0;\n+\t\n+\t// Parameters in first block:\n+\tenum \n+\t{ \n+\t\tID_FILENAME,\n+\t\tID_DATAPATH,\n+\t\tID_CURRENTTIMEHIDDEN,\n+\t\tID_TIMEOFFSET,\n+\t\tID_TIMESCALE,\n+\t\tID_FACSET,\n+\t\tID_VERTICES,\n+\t\tID_NORMALS,\n+\t\tID_UVS,\n+\t\tID_CLUSTERS,\n+\t\tID_MUTE\n+\t};\n+\n+\tstatic IObjParam *ip;\n+\tstatic AlembicMeshModifier *editMod;\n+\n+\tAlembicMeshModifier();\n+\n+\t// From Animatable\n+\tvoid DeleteThis() { delete this; }\n+\tvoid GetClassName(TSTR& s) { s = _T("Alembic Mesh Modifier"); }  \n+\tvirtual Class_ID ClassID() { return EXOCORTEX_ALEMBIC_POLYMESH_MODIFIER_ID; }\t\t\n+\tRefTargetHandle Clone(RemapDir& remap);\n+\tTCHAR *GetObjectName() { return _T("Alembic Mesh Modifier"); }\n+\n+\t// From modifier\n+\tChannelMask ChannelsUsed()  { return TOPO_CHANNEL|GEOM_CHANNEL|TEXMAP_CHANNEL; }\n+\tChannelMask ChannelsChanged() { return TOPO_CHANNEL|GEOM_CHANNEL|TEXMAP_CHANNEL; }\n+\tClass_ID InputType() { return polyObjectClassID; }\n+\tvoid ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node);\n+\tInterval LocalValidity(TimeValue t) { return GetValidity(t); }\n+\tInterval GetValidity (TimeValue t);\n+\tBOOL DependOnTopology(ModContext &mc) { return TRUE; }\n+\n+\t// From BaseObject\n+\tCreateMouseCallBack* GetCreateMouseCallBack() {return NULL;} \n+\tvoid BeginEditParams(IObjParam  *ip, ULONG flags,Animatable *prev);\n+\tvoid EndEditParams(IObjParam *ip,ULONG flags,Animatable *next);\t\t\n+\n+\tint NumParamBlocks () { return POLYMESHMOD_MAX_PARAM_BLOCKS; }\n+\tIParamBlock2 *GetParamBlock (int i) { return pblock[i]; }\n+\tIParamBlock2 *GetParamBlockByID (short id);\n+\n+\tint NumRefs() { return POLYMESHMOD_MAX_PARAM_BLOCKS; }\n+\tRefTargetHandle GetReference(int i) { return pblock[i]; }\n+private:\n+\tvirtual void SetReference(int i, RefTargetHandle rtarg) { pblock[i] = (IParamBlock2 *) rtarg; }\n+public:\n+\n+\tint NumSubs() {return POLYMESHMOD_MAX_PARAM_BLOCKS;}\n+\tAnimatable* SubAnim(int i) { return GetReference(i); }\n+\tTSTR SubAnimName(int i);\n+\n+\tRefResult NotifyRefChanged( Interval changeInt,RefTargetHandle hTarget, \n+\t\tPartID& partID, RefMessage message);\n+private:\n+    alembic_nodeprops m_AlembicNodeProps;\n+public:\n+\tvoid SetAlembicId(const std::string &file, const std::string &identifier);\n+    void SetAlembicUpdateDataFillFlags(unsigned int nFlags) { m_AlembicNodeProps.m_UpdateDataFillFlags = nFlags; }\n+    const std::string &GetAlembicArchive() { return m_AlembicNodeProps.m_File; }\n+    const std::string &GetAlembicObjectId() { return m_AlembicNodeProps.m_Identifier; }\n+};\n+//--- ClassDescriptor and class vars ---------------------------------\n+\n+IObjParam *AlembicMeshModifier::ip              = NULL;\n+AlembicMeshModifier *AlembicMeshModifier::editMod         = NULL;\n+\n+class AlembicPolyMeshModifierClassDesc : public ClassDesc2 {\n+\tpublic:\n+\tint \t\t\tIsPublic() { return 1; }\n+\tvoid *\t\t\tCreate(BOOL loading = FALSE) { return new AlembicMeshModifier; }\n+\tconst TCHAR *\tClassName() { return _T("Alembic Mesh Modifier"); }\n+\tSClass_ID\t\tSuperClassID() { return OSM_CLASS_ID; }\n+\tClass_ID\t\tClassID() { return EXOCORTEX_ALEMBIC_POLYMESH_MODIFIER_ID; }\n+\tconst TCHAR* \tCategory() { return _T("Alembic"); }\n+\tconst TCHAR*\tInternalName() { return _T("AlembicMeshModifier"); }\t// returns fixed parsable name (scripter-visible name)\n+\tHINSTANCE\t\tHInstance() { return hInstance; }\t\t\t// returns owning module handle\n+};\n+\n+static AlembicPolyMeshModifierClassDesc AlembicPolyMeshModifierDesc;\n+ClassDesc* GetAlembicPolyMeshModifierDesc() {return &AlembicPolyMeshModifierDesc;}\n+\n+//--- Properties block -------------------------------\n+\n+class AlembicMeshModifierParams\n+    : public ParamBlockDescUtil\n+{\n+public:        \n+    \n+    AlembicMeshModifierParams()\n+\t\t: ParamBlockDescUtil(\n+\t\t\tAlembicMeshModifier::PBLOCK_ID,\n+\t\t\t_T("AlembicMeshModifierParams"), \n+\t\t\tIDS_PARAMS, \n+\t\t\tAlembicPolyMeshModifierClassDesc::GetInstance(),\n+\t\t\tBEGIN_EDIT_MOTION, \n+\t\t\t// ----------------------\n+\t\t\t0, // Reference System Index\n+\t\t\tIDD_EMPTY,\n+\t\t\tIDS_PARAMS, \n+\t\t\tNULL\n+\t\t\t)\n+    {\n+\t\tAddFilenameParam(AlembicMeshModifier::ID_FILENAME, _T("filename"), IDS_FILENAME );\n+\t\tAddStringParam(AlembicMeshModifier::ID_DATAPATH, _T("dataPath"), IDS_DATAPATH );\n+\t\tAddFloatParam(AlembicMeshModifier::ID_CURRENTTIMEHIDDEN, _T("currentTimeHidden"), ID_CURRENTTIMEHIDDEN, 0);\n+        AddFloatParam(AlembicMeshModifier::ID_TIMEOFFSET, _T("timeOffset"), IDS_TIMEOFFSET, P_ANIMATABLE);\n+        AddFloatParam(AlembicMeshModifier::ID_TIMESCALE, _T("timeScale"), IDS_TIMESCALE, P_ANIMATABLE);\n+        AddBooleanParam(AlembicMeshModifier::ID_FACESET, _T("faceSet"), IDS_FACESET, 0);\n+        AddBooleanParam(AlembicMeshModifier::ID_VERTICES, _T("vertices"), IDS_VERTICES, 0);\n+        AddBooleanParam(AlembicMeshModifier::ID_NORMALS, _T("normals"), IDS_NORMALS, 0);\n+        AddBooleanParam(AlembicMeshModifier::ID_UVS, _T("uvs"), IDS_UVS, 0);\n+        AddBooleanParam(AlembicMeshModifier::ID_CLUSTERS, _T("clusters"), IDS_CLUSTERS, 0);\n+        AddBooleanParam(AlembicMeshModifier::ID_MUTE, _T("mute"), IDS_MUTE, P_ANIMATABLE);\n+    }\n+\n+    // Creates a static instance of the ControllerTutorialParams\n+    static AlembicMeshModifierParams* GetInstance()\n+    {\n+        static AlembicMeshModifierParams params;\n+        return &params;\n+    }\n+};\n+\n+\n+//--- Modifier methods -------------------------------\n+\n+AlembicMeshModifier::AlembicMeshModifier() \n+{\n+    for (int i = 0; i < POLYMESHMOD_MAX_PARAM_BLOCKS; i += 1)\n+\t    pblock[i] = NULL;\n+\n+\tAlembicPolyMeshModifierClassDesc::GetInstance()->MakeAutoParamBlocks(this);\n+}\n+\n+RefTargetHandle AlembicMeshModifier::Clone(RemapDir& remap) \n+{\n+\tAlembicMeshModifier *mod = new AlembicMeshModifier();\n+\n+    for (int i = 0; i < POLYMESHMOD_MAX_PARAM_BLOCKS; i += 1)\n+\t    mod->ReplaceReference (i, remap.CloneRef(pblock[i]));\n+\t\n+    BaseClone(this, mod, remap);\n+\treturn mod;\n+}\n+\n+IParamBlock2 *AlembicMeshModifier::GetParamBlockByID (short id) \n+{\n+    for (int i = 0; i < POLYMESHMOD_MAX_PARAM_BLOCKS; i += 1)\n+    {\n+        if (pblock[i] && pblock[i]->ID() == id)\n+            return pblock[i];\n+    }\n+\n+    return NULL;\n+}\n+\n+Interval AlembicMeshModifier::GetValidity (TimeValue t) {\n+\t// Interval ret = FOREVER;\n+\t// pblock->GetValidity (t, ret);\n+\n+    // PeterM this will need to be rethought out\n+    Interval ret(t,t);\n+\treturn ret;\n+}\n+\n+void AlembicMeshModifier::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node) \n+{\n+\tESS_CPP_EXCEPTION_REPORTING_START\n+\n+   Interval ivalid=os->obj->ObjectValidity(t);\n+\n+   Alembic::AbcGeom::IObject iObj = getObjectFromArchive(m_AlembicNodeProps.m_File, m_AlembicNodeProps.m_Identifier);\n+   if(!iObj.valid())\n+      return;\n+\n+   alembic_fillmesh_options options;\n+   options.pIObj = &iObj;\n+   options.dTicks = t;\n+   options.nDataFillFlags = m_AlembicNodeProps.m_UpdateDataFillFlags;\n+\n+   // Find out if we are modifying a poly object or a tri object\n+   if (os->obj->CanConvertToType(Class_ID(POLYOBJ_CLASS_ID, 0)))\n+   {\n+\t   PolyObject *pPolyObj = reinterpret_cast<PolyObject *>(os->obj->ConvertToType(t, Class_ID(POLYOBJ_CLASS_ID, 0)));\n+\n+\t   options.pMNMesh = &( pPolyObj->GetMesh() );\n+    \n+\t   if (os->obj != pPolyObj) {\n+          os->obj = pPolyObj;\n+\t   }\n+\n+   }\n+   else if (os->obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))\n+   {\n+      TriObject *pTriObj = reinterpret_cast<TriObject *>(os->obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0)));\n+\t\toptions.pMesh = &( pTriObj->GetMesh() );\n+\n+\t\tif (os->obj != pTriObj) {\n+          os->obj = pTriObj;\n+\t\t}\n+   }\n+   else\n+   {\n+       return;\n+   }\n+\n+   AlembicImport_FillInPolyMesh(options);\n+\n+   Interval alembicValid(t, t); \n+   ivalid = alembicValid;\n+\n+   // update the validity channel\n+   os->obj->UpdateValidity(TOPO_CHAN_NUM, ivalid);\n+   os->obj->UpdateValidity(GEOM_CHAN_NUM, ivalid);\n+   os->obj->UpdateValidity(TEXMAP_CHAN_NUM, ivalid);\n+\n+   \tESS_CPP_EXCEPTION_REPORTING_END\n+}\n+\n+void AlembicMeshModifier::BeginEditParams (IObjParam  *ip, ULONG flags, Animatable *prev) {\n+\tESS_CPP_EXCEPTION_REPORTING_START\n+\n+\tthis->ip = ip;\t\n+\teditMod  = this;\n+\n+\t// throw up all the appropriate auto-rollouts\n+\tAlembicPolyMeshModifierDesc.BeginEditParams(ip, this, flags, prev);\n+\n+    /*TimeValue t = GetCOREInterface()->GetTime();\n+    const char *strArchive = GetAlembicArchive().c_str();\n+    GetParamBlock(polymesh_preview)->SetValue(polymesh_preview_abc_archive, t, strArchive);\n+\n+    const char *strObjectId = GetAlembicObjectId().c_str();\n+    GetParamBlock(polymesh_preview)->SetValue(polymesh_preview_abc_archive, t, strObjectId);\n+    */\n+\n+\t// Necessary?\n+\tNotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);\n+\n+\tESS_CPP_EXCEPTION_REPORTING_END\n+}\n+\n+void AlembicMeshModifier::EndEditParams (IObjParam *ip,ULONG flags,Animatable *next) \n+{\n+\tESS_CPP_EXCEPTION_REPORTING_START\n+\n+\tAlembicPolyMeshModifierDesc.EndEditParams(ip, this, flags, next);\n+\tthis->ip = NULL;\n+\teditMod  = NULL;\n+\n+\tESS_CPP_EXCEPTION_REPORTING_END\n+}\n+\n+RefResult AlembicMeshModifier::NotifyRefChanged (Interval changeInt, RefTargetHandle hTarget,\n+\t\t\t\t\t\t\t\t\t\t   PartID& partID, RefMessage message) {\n+\tESS_CPP_EXCEPTION_REPORTING_START\n+\n+\tswitch (message) \n+    {\n+\tcase REFMSG_CHANGE:\n+\t\tif (editMod!=this) break;\n+\t\t// if this was caused by a NotifyDependents from pblock, LastNotifyParamID()\n+\t\t// will contain ID to update, else it will be -1 => inval whole rollout\n+\t\t/*if (pblock->LastNotifyParamID() == turn_sel_level) {\n+\t\t\t// Notify stack that subobject info has changed:\n+\t\t\tNotifyDependents(changeInt, partID, message);\n+\t\t\tNotifyDependents(FOREVER, 0, REFMSG_NUM_SUBOBJECTTYPES_CHANGED);\n+\t\t\treturn REF_STOP;\n+\t\t}\n+        */\n+        for (int i = 0; i < POLYMESHMOD_MAX_PARAM_BLOCKS; i += 1)\n+        {\n+\t\t    polymesh_props_desc.InvalidateUI(pblock[i]->LastNotifyParamID());\n+            polymesh_preview_desc.InvalidateUI(pblock[i]->LastNotifyParamID());\n+            // polymesh_render_desc.InvalidateUI(pblock[i]->LastNotifyParamID());\n+        }\n+\t\tbreak;\n+\n+    case REFMSG_WANT_SHOWPARAMLEVEL:\n+\n+        break;\n+\t}\n+\n+\tESS_CPP_EXCEPTION_REPORTING_END\n+\n+\treturn REF_SUCCEED;\n+}\n+\n+void AlembicMeshModifier::SetAlembicId(const std::string &file, const std::string &identifier)\n+{\n+    m_AlembicNodeProps.m_File = file;\n+    m_AlembicNodeProps.m_Identifier = identifier;\n+}\n+\n+TSTR AlembicMeshModifier::SubAnimName(int i)\n+{\n+    if ( i == 0)\n+        return _T("IDS_PROPS");\n+    else if (i == 1)\n+        return _T("IDS_PREVIEW");\n+    else if (i == 2)\n+        return _T("IDS_RENDER");\n+    else\n+        return "";\n+}\ndiff --git a/CMakeLists.txt b/CMakeLists.txt\nindex 41fc85f1..f2aa466f 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -30,17 +30,18 @@ endif()\n get_filename_component( BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PATH )\n set( LOCAL_CMAKE_FILE_NAME "${BASE_DIR}/CMakeLists.txt" )\n \n-include_directories( ${BASE_DIR}/include )\n-include_directories( ${BASE_DIR}/src )\n+include_directories( ${BASE_DIR}/Shared )\n \n-file(GLOB_RECURSE Sources ${BASE_DIR}/*.cpp)\n-file(GLOB_RECURSE Includes ${BASE_DIR}/*.h)\n+file(GLOB_RECURSE Shared ${BASE_DIR}/Shared/*.*)\n+file(GLOB Sources ${BASE_DIR}/*.cpp)\n+file(GLOB Includes ${BASE_DIR}/*.h)\n \n-file(GLOB_RECURSE MaxScripts ${BASE_DIR}/MaxScripts/*.*)\n+file(GLOB_RECURSE Scripts ${BASE_DIR}/MaxScripts/*.*)\n \n SOURCE_GROUP("Source Files" FILES ${Sources})   \n SOURCE_GROUP("Header Files" FILES ${Includes})\n-SOURCE_GROUP("MaxScripts" FILES ${MaxScripts})\n+SOURCE_GROUP("_Shared" FILES ${Shared})\n+SOURCE_GROUP("Maxscripts" FILES ${Scripts})\n \n if( Exocortex_SERVICES OR Exocortex_RLM_ONLY )\n \tinclude_directories( ${Rlm_INCLUDE_DIR} )\n@@ -73,7 +74,7 @@ if( Exocortex_RLM_ONLY )\n \tlink_directories( ${Rlm_LIBRARYDIR} )\n endif()\n \n-add_library( ${PROJECT_NAME} SHARED ${Sources} ${Includes}  ${LOCAL_CMAKE_FILE_NAME}  ${MaxScripts} )\n+add_library( ${PROJECT_NAME} SHARED ${Sources} ${Includes}  ${LOCAL_CMAKE_FILE_NAME}  ${Scripts} ${Shared} )\n \n TARGET_LINK_LIBRARIES( ${PROJECT_NAME} ${ALEMBIC_ILMBASE_LIBS}\n \t${ALEMBIC_ZLIB_LIBS} ${ALEMBIC_HDF5_LIBS} ${ALEMBIC_CORE_LIBS}\ndiff --git a/MaxScripts/Exocortex-AlembicModifier.mcr b/MaxScripts/Exocortex-AlembicModifier.mcr\nnew file mode 100644\nindex 00000000..4906698d\n--- /dev/null\n+++ b/MaxScripts/Exocortex-AlembicModifier.mcr\n@@ -0,0 +1,99 @@\n+plugin modifier AlembicMeshModifier\n+\tname:"Alembic Mesh Modifier"\n+\tclassID:#(0x20730623, 0x5d650bdb)\n+\tcategory:"Alembic" \n+\tversion:1\n+\tinvisible:false\n+(\n+\tparameters pblock rollout:params\n+\t(\n+\t\tfileName type:#string ui:fileName default:"filename"\n+\t\tdataPath type:#string ui:dataPath default:"path"\n+\t\tcurrentTimeHidden type:#float default:0\n+\t\ttimeOffset type:#float ui:timeOffset animatable:true default:0\n+\t\ttimeScale type:#float ui:timeScale animatable:true default:0\n+\t\tfaceSet type:#boolean ui:faceSet default:true\n+\t\tvertices type:#boolean ui:vertices default:true\n+\t\tnormals type:#boolean ui:normals default:true\n+\t\tuvs type:#boolean ui:uvs default:true\n+\t\tclusters type:#boolean ui:clusters default:true\n+\t\tmute type:#boolean ui:mute animatable:true default:false\n+\t)\n+\n+\tlocal update_currentTimeHidden_registered = false\n+\t\n+\tfn update_currentTimeHidden =\n+\t(\n+\t\tcurrentTimeHidden = currentTime / frameRate\n+\t)\n+\t\t\n+\ton create do\n+\t(\n+\t\tformat "on create: %\\n" this\n+\t\tformat "  current obj: %\\n" $\n+\t)\n+\n+\ton postCreate do \n+\t(\n+\t\tformat "on postCreate : %\\n" this\n+\t\tformat "  current obj: %\\n" $\n+\t\tapplied = isGameFlowModifierAlreadyApplied $\n+\t\tformat "  already applied: %\\n" applied\n+\t)\n+\n+\ton load do\n+\t(\n+\t\tformat "on load : %\\n" this\n+\t\tformat "current obj: %\\n" $\n+\t)\n+\n+\ton postLoad do \n+\t(\n+\t\tformat "on postLoad : %\\n" this\n+\t\tformat "current obj: %\\n" $\n+\t)\n+\t\n+\ton clone orig do \n+\t(\n+\t\tformat "on clone: % : % : % : %\\n" this orig\n+\t\tformat "current obj: %\\n" $\n+\t)\n+\t\n+\ton update do \n+\t(\t\t\n+\t\tformat "on update : %\\n" this\n+\t\tformat "current obj: %\\n" $\n+\t\t\n+\t\tif( not update_currentTimeHidden_registered ) do\n+\t\t(\n+\t\t\tcurrentTimeHidden = currentTime / frameRate\n+\t\t\tregisterTimeCallback update_currentTimeHidden\n+\t\t\tupdate_currentTimeHidden_registered = true\n+\t\t)\n+\t\t\n+\t\tdataTime = timeOffset + currentTimeHidden  * timeScale\n+\t\ttempMesh = mesh\n+\t\tif( not mute ) do\n+\t\t(\n+\t\t\ttempMesh = ExocortexAlembic.importMesh tempMesh fileName dataPath dataTime faceSet vertices normals uvs clusters\n+\t\t)\n+\t\tmesh = tempMesh;\n+\t\t\n+\t)\n+\ton attachedToNode node do\n+\t(\n+\t\tformat "on attachedToNode: % %\\n" this node\n+\t\tformat "current obj: %\\n" $\n+\t)\n+\ton detachedFromNode node do\n+\t(\n+\t\tformat "on detachedFromNode : % %\\n" this node\n+\t\tformat "current obj: %\\n" $\n+\t)\n+\n+\ton deleted do \n+\t(\n+\t\tformat "on deleted : % \\n" this\n+\t\tformat "current obj: %\\n" $\n+\t)\n+)\n\\ No newline at end of file\ndiff --git a/ParamBlockUtility.h b/ParamBlockUtility.h\nnew file mode 100644\nindex 00000000..8ebce20e\n--- /dev/null\n+++ b/ParamBlockUtility.h\n@@ -0,0 +1,278 @@\n+//\n+// Copyright 2010 Autodesk, Inc.  All rights reserved.\n+//\n+// Use of this software is subject to the terms of the Autodesk license\n+// agreement provided at the time of installation or download, or which\n+// otherwise accompanies this software in either electronic or hard copy form.  \n+//\n+\n+#pragma once\n+\n+/**\n+ * This class is for easier and safer descriptions of parameter blocks. \n+ * ParamBlockDescUtil is based on ParamBlockDesc2, and is intended to be used created incrementally \n+ * using a series of function calls. This is different from the traditional approach of passing \n+ * a massive variable length list of arguments to the constructor of ParamBlockDesc2. \n+ * The advantages are additional type safety, and explicit support from the IDE (e.g. code completion).\n+ * \n+ * \\par This class assumes the parameter block is auto-constructed by 3ds Max (meaning it \n+ * assigns the parameter block as a reference to your plug-in) and has an auto-constructed UI. \n+ * You would use it normally it by deriving a class which has an empty constructors \n+ * and calls the incremental construction functions from its own constructor\n+ * Once constructed, the parameter block descriptor must never be modified.\n+ *\n+ * \\note: Pass the flag BEGIN_EDIT_MOTION to the constructor \\c flags argument \n+ * if using this for a controller.\n+ */\n+class ParamBlockDescUtil\n+    : protected ParamBlockDesc2 \n+{\n+private:\n+\n+    BOOL finished;\n+\n+public:\n+\t///* From the header:\n+\tParamBlockDescUtil(  ClassDesc2* classDesc  ,\n+\t\tMCHAR* internalName, \n+\t\tStringResID localNameID, \n+\t\tBlockID id, \n+\t\tint flagMask       )        : ParamBlockDesc2(\n+\t\tid, \n+\t\tinternalName, \n+\t\tlocalNameID, \n+\t\tclassDesc, \n+\t\tP_AUTO_CONSTRUCT + P_AUTO_UI,\n+\t\t// [<auto_construct_block_refno>]\n+\t\t// [<auto_ui_parammap_specs>]\n+\t\tflagMask,\n+\t\t0, // could be APPENDROLL_CLOSED if the roll-up should be closed by default\n+\t\tdlgProc,\n+\t\tend),\n+\t\tfinished(FALSE){};  \n+\t//*/\n+    ParamBlockDescUtil\n+        (\n+\t\t\tBlockID     id              ,\n+            MCHAR* internalName, \n+            StringResID localNameID, \n+            ClassDesc2* classDesc       , \n+            int         flagMask        , // Set to BEGIN_EDIT_MOTION for controllers.\n+            // ---------------------\n+\t\t\tint         refNumber       ,\n+            ResID       dialogTemplateID, \n+            StringResID panelTitleID, \n+            ParamMap2UserDlgProc* dlgProc = NULL\n+        )\n+        : ParamBlockDesc2(\n+            id, \n+            internalName, \n+            localNameID, \n+            classDesc, \n+            P_AUTO_CONSTRUCT + P_AUTO_UI,\n+                // [<auto_construct_block_refno>]\n+                refNumber,\n+                // [<auto_ui_parammap_specs>]\n+                dialogTemplateID,\n+                panelTitleID,\n+                flagMask,\n+                0, // could be APPENDROLL_CLOSED if the roll-up should be closed by default\n+                dlgProc,\n+            end),\n+        finished(FALSE)\n+    {\n+    }\n+\n+    /**\n+     * After the last parameter is added, call this function. \n+     * This is used to assure internally that no one tries to add additional parameters.\n+     */\n+    void SetFinished()\n+    {\n+        DbgAssert(!finished);\n+        finished = TRUE;\n+    }\n+\n+    /**\n+     * Adds an angle parameter with an associated spinner controller.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     * \\param  min An argument of type float.\n+     * \\param  max An argument of type float.\n+     * \\param  def An argument of type float.\n+     * \\param  editID An argument of type ResID.\n+     * \\param  spinnerID An argument of type ResID.\n+     * \\param  incAmount An argument of type float.\n+     */\n+    void AddAngleSpinnerParam(ParamID id, MCHAR* internalName, StringResID localNameID, float min, float max, float def, ResID editID, ResID spinnerID, float incAmount = SPIN_AUTOSCALE)\n+    {\n+        DbgAssert(!finished);\n+        AddAngleParam(id, internalName, localNameID);\n+        SetRangeAndDefault(id, min, max, def);\n+        AddEditAndSpinner(id, editID, spinnerID, incAmount);\n+    }\n+\n+    \n+    /**\n+     * Adds a float parameter with an associated spinner controller.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     * \\param  min An argument of type float.\n+     * \\param  max An argument of type float.\n+     * \\param  def An argument of type float.\n+     * \\param  editID An argument of type ResID.\n+     * \\param  spinnerID An argument of type ResID.\n+     * \\param  incAmount An argument of type float.\n+     */\n+    void AddFloatSpinnerParam(ParamID id, MCHAR* internalName, StringResID localNameID, float min, float max, float def, ResID editID, ResID spinnerID, float incAmount = SPIN_AUTOSCALE)\n+    {\n+        DbgAssert(!finished);\n+        AddFloatParam(id, internalName, localNameID);\n+        SetRangeAndDefault(id, min, max, def);\n+        AddEditAndSpinner(id, editID, spinnerID, incAmount);\n+    }\n+        \n+    /**\n+     * Adds a node parameter, with a node pick button. \n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     * \\param  buttonID An argument of type ResID.\n+     */\n+    void AddNodePickParam(ParamID id, MCHAR* internalName, StringResID localNameID, ResID buttonID)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_INODE, 0, localNameID, end);\n+        ParamOption(id, p_ui, TYPE_PICKNODEBUTTON, buttonID, end);\n+    }\n+\n+    /**\n+     * Sets the range and default values of the specified float parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  min An argument of type float.\n+     * \\param  max An argument of type float.\n+     * \\param  def An argument of type float.\n+     */\n+    void SetRangeAndDefault(ParamID id, float min, float max, float def)\n+    {\n+        DbgAssert(!finished);\n+        DbgAssert(min <= max);\n+        DbgAssert(def >= min);\n+        DbgAssert(def <= max);\n+        ParamOption(id, p_range, min, max, end);\n+        ParamOption(id, p_default, def, end);\n+    }\n+\n+    /**\n+     * Adds an angle parameter. \n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddAngleParam(ParamID id, MCHAR* internalName, StringResID localNameID, int flags = P_ANIMATABLE )\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_ANGLE, flags, localNameID, end);        \n+    }\n+\n+   /**\n+     * Adds a int parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddIntParam(ParamID id, MCHAR* internalName, StringResID localNameID, int flags = P_ANIMATABLE)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_INT, flags, localNameID, end);        \n+    }\n+\n+     /**\n+     * Adds a filename parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddFilenameParam(ParamID id, MCHAR* internalName, StringResID localNameID, int flags = 0 )\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_FILENAME, flags, localNameID, end);        \n+    }\n+\t\n+\t/**\n+     * Adds a float parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddFloatParam(ParamID id, MCHAR* internalName, StringResID localNameID, int flags = P_ANIMATABLE)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_FLOAT, flags, localNameID, end);        \n+    }\n+\n+\t\n+    /**\n+     * Adds a bool parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddBooleanParam(ParamID id, MCHAR* internalName, StringResID localNameID, int flags = P_ANIMATABLE)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_BOOL, flags, localNameID, end);        \n+    }\n+\n+    /**\n+     * Adds a bool parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddStringParam(ParamID id, MCHAR* internalName, StringResID localNameID)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_STRING, 0, localNameID, end);        \n+    }\n+\n+\t/**\n+     * Adds an edit control and and a spinner control to the specified parameter. \n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  editID An argument of type ResID.\n+     * \\param  spinnerID An argument of type ResID.\n+     * \\param  incAmount An argument of type float.\n+     */\n+    void AddEditAndSpinner(ParamID id, ResID editID, ResID spinnerID, float incAmount = SPIN_AUTOSCALE)\n+    {\n+        DbgAssert(!finished);\n+        ParamOption(id, p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, editID, spinnerID, incAmount);\n+    }\n+\n+    \n+    /**\n+     * Associates a button with a parameter, which become a pick-node button.\n+     * This means when the user clicks on it, 3ds Max goes into node picking mode.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  buttonID An argument of type ResID.\n+     */\n+    void AddPickNodeButton(ParamID id, ResID buttonID)\n+    {\n+        DbgAssert(!finished);\n+        ParamOption(id, p_ui, TYPE_PICKNODEBUTTON, buttonID, end);\n+    }\n+};\n\\ No newline at end of file\ndiff --git a/Shared/AnimatablePlugin.h b/Shared/AnimatablePlugin.h\nnew file mode 100644\nindex 00000000..864c6cfc\n--- /dev/null\n+++ b/Shared/AnimatablePlugin.h\n@@ -0,0 +1,375 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "SubAnimManager.h"\n+\n+/**\n+ * A class template for use as a base class for plug-ins that derive from \n+ * ReferenceTarget. An instantiation of this\n+ * class template will derive ultimately from the the template parameter Base_T. \n+ * It is required that Base_T is a class that derives from ReferenceTarget. \n+ * This class assumes that the plug-in has a single parameter block as reference 0 \n+ * which is created automatically by the ClassDescriptor. This is done by \n+ * passing the flag P_AUTO_CONSTRUCT to the parameter block descriptor (ParamBlockDesc2)\n+ * or by constructing the parameter blocks using ParamBlockDesc2.\n+ * This class is similar to SimpleMod, SimpleMod2, SimpleObj, and SimpleObj2. \n+ */\n+template<typename Base_T>\n+class AnimatablePlugin\n+    : public SubAnimManager<Base_T>    \n+{\n+private:\n+\n+    /// Identifies the main parameter block sub-anim\n+    static const int PARAM_BLOCK_SUBANIM_INDEX = 0;\n+\n+    /// A cached pointer to class descriptor \n+\tClassDesc2* m_classDesc;\n+\n+\t/// Default construction is not allowed. \n+\tAnimatablePlugin()\n+\t{ }\n+\n+public:\n+\n+    /**\n+     * Creates an animatable plug-in which is associated with a particular class descriptor. \n+     * \n+     * \\param  classDesc An argument of type ClassDesc2 *.\n+     * \\see        Related API elements\n+     */\n+    AnimatablePlugin(ClassDesc2* classDesc)\n+        : m_classDesc(classDesc)\n+    {\n+\t\tassert(m_classDesc);\n+\n+        // Register a NULL parameter-block reference with the ReferenceManager base class\n+        RegisterReference(GetParamBlockRefIndex(), NULL);\n+\n+        // Register the parameter block as a sub-animatable with the SubAnimManager base\n+        // class\n+        RegisterSubAnim(PARAM_BLOCK_SUBANIM_INDEX, GetParamBlockRefIndex(), "Parameters");\n+    }\n+\n+    /// \\name Reference Maker Overrides\n+    //@{\n+    /**\n+     * The override of NotifyRefChanged handles parameter changes by calling the virtual \n+     * function OnParamChangeMsg() and handles deletions by calling OnRefDeleted(). \n+     * REFMSG_GET_PARAM_DIM, and REFMSG_GET_PARAM_NAME. \n+     * \n+     * \\param  changeInt An argument of type Interval.\n+     * \\param  hTarget An argument of type RefTargetHandle.\n+     * \\param  partID An argument of type PartID &.\n+     * \\param  message An argument of type RefMessage.\n+     * \\return     Returns a value of type RefResult.\n+     * \\see        ReferenceMaker::NotifyRefChanged()\n+     */\n+    virtual RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ) \n+   \t{\n+        RefResult result = REF_DONTCARE;\n+\n+\t    switch (message) \n+        {\n+            // Reference message representing a change in one of the targets \n+            case REFMSG_CHANGE: \n+            {   \n+                // If the target is a parameter block, then we do handle it \n+                IParamBlock2* pb = dynamic_cast<IParamBlock2*>(hTarget);\n+                if (pb != NULL)\n+                    OnParamChangeMsg(pb->LastNotifyParamID());\n+\t\t\t    result = REF_SUCCEED; \n+                break;\n+            }\n+            // Reference message representing a request for the dimension of a parameter\n+\t\t    case REFMSG_GET_PARAM_DIM: \n+            {\n+\t\t\t    GetParamDim *gpd = (GetParamDim*)partID;\n+\t\t\t    gpd->dim = GetMainParamBlock()->GetParamDimension(GetMainParamBlock()->IndextoID(gpd->index));\t\t\t\n+\t\t\t    result = REF_HALT; \n+                break;\n+            }\n+            // Reference message representing a request for the name of a parameter\n+            case REFMSG_GET_PARAM_NAME: \n+            {\n+                ::GetParamName *gpn = (::GetParamName*)partID;                \n+\t\t\t    gpn->name = GetMainParamBlock()->GetLocalName(GetMainParamBlock()->IndextoID(gpn->index));\t\t\t\n+\t\t\t    result = REF_HALT; \n+                break;\n+            }\n+            // Reference message informing us of a deletion. \n+            case REFMSG_TARGET_DELETED:\n+            {\n+                OnRefDeleted(hTarget);\n+\t\t\t    result = ReferenceManager::NotifyRefChanged(changeInt, hTarget, partID, message);\n+                break;\n+            }\n+            default: \n+                result = ReferenceManager::NotifyRefChanged(changeInt, hTarget, partID, message);\n+                break;\n+\t\t}\n+\n+        return result;\n+\t}\n+    //@}\n+\n+    /// \\name Animatable Overrides\n+    //@{    \n+    /**\n+     * Returns the number of parameter blocks. \n+     * \n+     * \\return     Returns a value of type int.\n+     * \\see        Animatable::NumParamBlocks()\n+     */\n+    virtual int NumParamBlocks() \n+    {\n+        return 1;\n+    }\n+\n+    /**\n+     * Returns the nth parameter block. \n+     *\n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type IParamBlock2 *.\n+     * \\see        Animatable::GetParamBlock()\n+     */\n+    virtual IParamBlock2* GetParamBlock(int n) \n+    {\n+        if (n != 0) return NULL;\n+        return GetMainParamBlock();\n+    }    \n+\n+    /**\n+     * Returns a parameter block with the specified id.  \n+     * \n+     * \\param  id An argument of type short.\n+     * \\return     Returns a value of type IParamBlock2 *.\n+     * \\see        Animatable::GetParamBlockByID()\n+     */\n+    virtual IParamBlock2* GetParamBlockByID(short id) \n+    {\n+        if (id != 0) return NULL;\n+        return GetMainParamBlock();\n+    }\n+\n+    /**\n+     * Called when the roll-out panel is about to be shown.\n+     * \n+     * \\param  ip An argument of type IObjParam *.\n+     * \\param  flags An argument of type ULONG.\n+     * \\param  prev An argument of type Animatable *.\n+     * \\see        Animatable::BeginEditParams()\n+     */\n+    virtual void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev) \n+    {\n+        GetClassDesc()->BeginEditParams(ip, this, flags, prev);\n+    }\n+\n+    /**\n+     * Called when the roll-out panel is about to be closed. \n+     * \n+     * \\param  ip An argument of type IObjParam *.\n+     * \\param  flags An argument of type ULONG.\n+     * \\param  next An argument of type Animatable *.\n+     * \\see        Animatable::EndEditParams()\n+     */\n+    virtual void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) \n+    {\n+        ip->ClearPickMode(); // prevents crash on undo, while pickmode is active.\n+        GetClassDesc()->EndEditParams(ip, this, flags, next);\n+    }   \n+\n+    /**\n+     * Returns the class ID.\n+     * \n+     * \\return     Returns a value of type Class_ID.\n+     * \\see        Animatable::ClassID()\n+     */\n+    virtual Class_ID ClassID() \n+    { \n+        return GetClassDesc()->ClassID(); \n+    }\t\t\n+\n+\t/**\n+\t * Returns the super-class name.\n+\t * \n+\t * \\return     Returns a value of type SClass_ID.\n+\t * \\see        Animatable::SuperClassID()\n+\t */\n+\tvirtual SClass_ID SuperClassID() \n+    { \n+        return GetClassDesc()->SuperClassID(); \n+    }\n+\n+\t/**\n+\t * Returns the class name.\n+\t * \n+\t * \\param  s An argument of type MSTR &.\n+\t * \\see        Animatable::GetClassName()\n+\t */\n+\tvirtual void GetClassName(MSTR& s) \n+    {\n+        s = GetClassDesc()->ClassName();\n+    }    \n+    //@}\n+   \n+    /// \\name BaseObject Overrides\n+    //@{\n+    /**\n+     * An animatable plug-in may or may not derive from a BaseObject, \n+     * but if it does it must override this function. It is placed \n+     * \n+     * \\param  id An argument of type int.\n+     * \\return     Returns a value of type int.\n+     * \\see        BaseObject::GetParamBlockIndex()\n+     */\n+    virtual int GetParamBlockIndex(int id)\n+\t{\n+        return id == 0 ? 0 : -1;\n+\t}\n+    //@}\n+\n+    /// \\name Utility Functions\n+    //@{\n+    /**\n+     * Returns a parameter block.     \n+     * \n+     * \\return     Returns a value of type IParamBlock2 *.\n+     */\n+    IParamBlock2* GetMainParamBlock() \n+    {\n+        IParamBlock2* r = GetReferenceAs<IParamBlock2*>(GetParamBlockRefIndex());\n+        \n+        // The pblock variable is expected to be set by 3ds Max. This will happen through\n+        // a call to the ReferenceMaker::SetReference() method on the plug-in.        \n+        // The class descriptor will tell 3ds Max to create the parameter blocks \n+        // automatically. \n+        DbgAssert(r != NULL && "Parameter block was not set correctly");\n+\n+        return r; \n+    }\n+    \n+    \n+    /**\n+     * Retrieves parameters. \n+     * \n+     * \\tparam     T the type of the parameter to retrieve.\n+     * \\param  id An argument of type ParamID.\n+     * \\param  t An argument of type TimeValue.\n+     * \\return     Returns a value of type T.\n+     */\n+    template<typename T>\n+    T GetParameter(ParamID id, TimeValue t = Now())\n+    {\n+        T r = T(); // Initialized, but should always work.\n+\t\tIParamBlock2* pb = GetMainParamBlock();\n+\t\t// Should never ever be NULL\n+\t\tif (pb)\n+\t\t{\t\t\t\n+\t\t\tpb->GetValue(id, t, r, FOREVER);\n+\t\t}\n+        return r;\n+    }\n+    \n+    /**\n+     * Sets the value of a particular parameter.\n+     * \n+     * \\tparam     T the type of the parameter to set.\n+     * \\param  id An argument of type ParamID.\n+     * \\param  x An argument of type T.\n+     * \\param  t An argument of type TimeValue.\n+     */\n+    template<typename T>\n+    void SetParameter(ParamID id, T x, TimeValue t = Now())\n+    {\n+        GetMainParamBlock()->SetValue(id, t, x);\n+\t\tInvalidateUI();\n+    }\n+    \n+    /**\n+     * Forces the parameter block descriptor to update the roll-out UI.\n+     */\n+    void InvalidateUI() \n+    {\n+    \tGetParamBlockDesc()->InvalidateUI();\n+    }\n+\n+    /**\n+     * Returns the parameter block descriptor.\n+     * \n+     * \\return     Returns a value of type ParamBlockDesc2 *.\n+     */\n+    ParamBlockDesc2* GetParamBlockDesc()\n+    {\n+        return GetMainParamBlock()->GetDesc();\n+    }\n+    \n+    /**\n+     * Resets all parameters to their default values.\n+     * \n+     * \\param  updateUI An argument of type BOOL.\n+     * \\param  callSetHandlers An argument of type BOOL.\n+     */\n+    void ResetParams(BOOL updateUI = TRUE, BOOL callSetHandlers = TRUE)\n+    {\n+        GetClassDesc()->Reset(this, updateUI, callSetHandlers);\n+    }\n+\n+\t/**\n+\t * Provides access to the class descriptor passed to the constructor.\n+\t * \n+\t * \\return     Returns a value of type ClassDesc2 *.\n+\t */\n+\tClassDesc2* GetClassDesc() \n+\t{\n+\t\tassert(m_classDesc != NULL);\n+\t\treturn m_classDesc;\n+\t}\n+\n+\t/**\n+\t * Returns the reference index of the parameter block. \n+\t * \n+\t * \\return     Returns a value of type int.\n+\t */\n+\tint GetParamBlockRefIndex()\n+\t{\n+\t\treturn GetClassDesc()->GetParamBlockDesc(0)->ref_no;\n+\t}\n+    //@}\n+\n+    /// \\name Virtual Functions\n+    //@{\n+    /**\n+     * Override if you want to respond to reference messages notifying the plug-in of changes to\n+     * parameters.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     */\n+    virtual void OnParamChangeMsg(ParamID id) \n+    {\n+        // Does nothing by default.\n+    }\n+\n+    /**\n+     * Override if you want to respond to reference messages notifying the plug-in of deletions\n+     * to observed plug-ins.\n+     * \n+     * \\param  ref An argument of type ReferenceTarget*.\n+     */\n+    virtual void OnRefDeleted(ReferenceTarget* ref) \n+    {\n+        // Does nothing by default.\n+    }\n+    //@}\n+};\n+\ndiff --git a/Shared/Common.h b/Shared/Common.h\nnew file mode 100644\nindex 00000000..3e399c91\n--- /dev/null\n+++ b/Shared/Common.h\n@@ -0,0 +1,32 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#include <istdplug.h>\n+#include <iparamb2.h> \n+#include <iparamm2.h> \n+#include <decomp.h>\n+#include <animtbl.h> \n+#include <gfx.h>\n+\n+const MSTR SDK_TUTORIALS_CATEGORY = _M("SDK Tutorials");\n+\n+// Disable the "unreferenced formal parameter" warning\n+#pragma warning(disable:4100) \n+\n+// Disable the argument conversion to reference warning\n+#pragma warning(disable:4239)\n+\n+#include "Macros.h"\n+#include "UtilityPlugin.h"\n+#include "AnimatablePlugin.h"\n+#include "MathUtility.h"\n+#include "Utility.h"\n+#include "ParamBlockUtility.h"\ndiff --git a/Shared/ControlPlugin.h b/Shared/ControlPlugin.h\nnew file mode 100644\nindex 00000000..804f9538\n--- /dev/null\n+++ b/Shared/ControlPlugin.h\n@@ -0,0 +1,346 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "ControlUtility.h"\n+\n+/**\n+ * Provides default implementations of some of the basic Control\n+ * functions. \n+ */\n+class ControlPlugin\n+    : public AnimatablePlugin<Control>\n+{\n+public:\n+\n+\tControlPlugin(ClassDesc2* classDesc)\n+\t\t: AnimatablePlugin(classDesc)\n+\t{ }\n+\n+    //================================================================================\n+    // Animatable overrides\n+\n+    /// \\name Animatable Overrides\n+    //@{\n+    /**\n+     * Calls the default AnimatablePlugin::BeginEditParams() implementation.\n+     * \n+     * \\see        Animatable::BeginEditParams\n+     * \n+     * \\param  ip An argument of type IObjParam *.\n+     * \\param  flags An argument of type ULONG.\n+     * \\param  prev An argument of type Animatable *.\n+     */\n+    virtual void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev = NULL) \n+    {\n+        AnimatablePlugin::BeginEditParams(ip, flags, prev); \n+    }\n+\n+    /**\n+     * Calls the default AnimatablePlugin::EndEditParams() implementation,\n+     * but first assures that \\c END_EDIT_REMOVEUI is or\'d with the \n+     * \\c flags argument. \n+     * \n+     * \\see        Animatable::EndEditParams()\n+     * \n+     * \\param  ip An argument of type IObjParam *.\n+     * \\param  flags An argument of type ULONG.\n+     * \\param  next An argument of type Animatable *.\n+     */\n+    virtual void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) \n+    {\n+        // Assure that we set the parameter editing UI is removed\n+        // when no longer focusing on the object. \n+        AnimatablePlugin::EndEditParams(ip, flags | END_EDIT_REMOVEUI, next); \n+    }       \n+    //@}\n+\n+    //================================================================================\n+    // Control overrides\n+\n+\t/// \\name Control Overrides\n+\t//@{\n+\t/**\n+\t * Provides a default implementation of Control::Copy() which does nothing. \n+\t * \n+\t * \\param  from An argument of type Control *.\n+\t */\n+\tvirtual void Copy(Control *from)\n+    {\n+    }\n+\n+    /**\n+     * Provides a default implementation of Control::IsKeyable() that returns \n+     * FALSE, indicating that the controller is not a key-frame controller. \n+     * \n+     * \\return     Returns a value of type BOOL.\n+     */\n+    virtual BOOL IsKeyable() \n+    {\n+        return FALSE;\n+    }\n+\t//@}\n+};\n+\n+//================================================================================\n+// A parameter base class for all parametric controllers\n+\n+/**\n+ * This template class is used to create new base classes for different kinds \n+ * of controller plug-ins based on the kinds of values that they manage. This is done\n+ * to provide type-safety and make it easier to implement the different kinds of plug-ins.\n+ */\n+template<typename GetAbsVal_T, typename GetRelVal_T, typename SetAbsVal_T, typename SetRelVal_T>\n+class ControlPluginTemplate : public ControlPlugin\n+{\n+public:\n+\n+\tControlPluginTemplate(ClassDesc2* classDesc)\n+\t\t: ControlPlugin(classDesc)\n+\t{ }\n+\n+    //============================================================================================\n+    // Control plug-in overrides \n+\n+    /// \\name Control Overrides\n+    //@{\n+    /**\n+     * Provides a default implementation for Control::GetValue() based on the \n+     * results of the pure virtual functions GetAbsoluteValue() and GetRelativeValue().\n+     * \n+     * \\param  t An argument of type TimeValue.\n+     * \\param  val An argument of type void *.\n+     * \\param  valid An argument of type Interval &.\n+     * \\param  method An argument of type GetSetMethod.\n+     */\n+    virtual void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE)\n+    {\n+        if (method == CTRL_ABSOLUTE)\n+            GetAbsoluteValue(t, *reinterpret_cast<GetAbsVal_T*>(val), valid); else\n+            GetRelativeValue(t, *reinterpret_cast<GetRelVal_T*>(val), valid);\n+    }\n+\n+    /**\n+     * Provides a default implementation for Control::SetValue() based on the \n+     * results of the virtual functions SetAbsoluteValue() and SetRelativeValue().\n+     * \n+     * \\param  t An argument of type TimeValue.\n+     * \\param  val An argument of type void *.\n+     * \\param  commit An argument of type int.\n+     * \\param  method An argument of type GetSetMethod.\n+     */\n+    virtual void SetValue(TimeValue t, void *val, int commit=1, GetSetMethod method=CTRL_ABSOLUTE)\n+    {\n+        if (method == CTRL_ABSOLUTE)\n+            SetAbsoluteValue(t, *reinterpret_cast<SetAbsVal_T*>(val), commit); else\n+            SetRelativeValue(t, *reinterpret_cast<SetRelVal_T*>(val), commit);\n+    }\n+    //@}\n+\n+    //============================================================================================\n+    // New pure-virtual functions to override\n+    \n+    /// \\name Pure Virtual Functions for All Controllers\n+    //@{\n+    /**\n+     * Classes that derive from ControlPluginTemplate can override this \n+     * function to generate absolute values instead of having to implement\n+     * Control::GetValue() which is not type-safe. \n+     *\n+     * \\see        GetValue()\n+     * \n+     * \\param  t An argument of type TimeValue.\n+     * \\param  val An argument of type GetAbsVal_T &.\n+     * \\param  valid An argument of type Interval &.\n+     */\n+    virtual void GetAbsoluteValue(TimeValue t, GetAbsVal_T& val, Interval& valid) = 0;\n+\n+    /**\n+     * Classes that derive from ControlPluginTemplate can override this \n+     * function to generate relative values instead of having to implement\n+     * Control::GetValue() which is not type-safe. \n+     *\n+     * \\see        GetValue()\n+     * \n+     * \\param  t An argument of type TimeValue.\n+     * \\param  val An argument of type GetRelVal_T &.\n+     * \\param  valid An argument of type Interval &.\n+     */\n+    virtual void GetRelativeValue(TimeValue t, GetRelVal_T& val, Interval& valid) = 0;\n+    //@}\n+\n+    /// \\name Virtual Functions for Key-frame Controllers\n+    //@{\n+    /**\n+     * Allows the absolute value to be set at a specific time for a keyframe controller.\n+     * Does nothing by default. If you support this function, you should override\n+     * ControlPlugin::IsKeyable() to return TRUE.\n+     *\n+     * \\see        SetValue()\n+     * \n+     * \\param  t An argument of type TimeValue.\n+     * \\param  val An argument of type SetAbsVal_T &.\n+     * \\param  commit An argument of type int.\n+     */\n+    virtual void SetAbsoluteValue(TimeValue t, SetAbsVal_T& val, int commit) { };\n+    /**\n+     * Allows the relative value to be set at a specific time for a keyframe controller.\n+     * Does nothing by default. If you support this function, you should override\n+     * ControlPlugin::IsKeyable() to return TRUE.\n+     *\n+     * \\see        SetValue()\n+     * \n+     * \\param  t An argument of type TimeValue.\n+     * \\param  val An argument of type SetRelVal_T &.\n+     * \\param  commit An argument of type int.\n+     */\n+    virtual void SetRelativeValue(TimeValue t, SetRelVal_T& val, int commit) { };\n+    //@}\n+};\n+\n+/**\n+ * A base class for controller plug-ins that manage float values.\n+ */\n+class FloatControlPlugin\n+    : public ControlPluginTemplate<float, float, float, float> \n+{ \n+public:\n+\tFloatControlPlugin(ClassDesc2* classDesc)\n+\t\t: ControlPluginTemplate(classDesc)\n+\t{ \n+\t}\n+\n+    /**\n+     * Identifies the plug-in as a float controller plug-in.\n+     * \n+     * \\return     Returns a value of type SClass_ID.\n+     */\n+    virtual SClass_ID SuperClassID()\n+    {\n+        return CTRL_FLOAT_CLASS_ID;\n+    }\n+};\n+\n+/**\n+ * A base class for controller plug-ins that manage Point3 values.\n+ */\n+class Point3ControlPlugin \n+    : public ControlPluginTemplate<Point3, Point3, Point3, Point3> \n+{ \n+public:\n+\tPoint3ControlPlugin(ClassDesc2* classDesc)\n+\t\t: ControlPluginTemplate(classDesc)\n+\t{ \n+\t}\n+\n+    /**\n+     * Identifies the plug-in as a point controller plug-in.\n+     * \n+     * \\return     Returns a value of type SClass_ID.\n+     */\n+    virtual SClass_ID SuperClassID()\n+    {\n+        return CTRL_POINT3_CLASS_ID;\n+    }\n+};\n+\n+/**\n+ * A base class for controller plug-ins that manage Matrix3 values.\n+ */\n+class Matrix3ControlPlugin\n+    : public ControlPluginTemplate<Matrix3, Matrix3, SetXFormPacket, SetXFormPacket> \n+{ \n+public:\n+\tMatrix3ControlPlugin(ClassDesc2* classDesc)\n+\t\t: ControlPluginTemplate(classDesc)\n+\t{ \n+\t}\n+\n+    /**\n+     * Identifies the plug-in as a matrix controller plug-in.\n+     * \n+     * \\return     Returns a value of type SClass_ID.\n+     */\n+    virtual SClass_ID SuperClassID()\n+    {\n+        return CTRL_MATRIX3_CLASS_ID;\n+    }\n+};\n+\n+/**\n+ * A base class for controller plug-ins that manage position.\n+ */\n+class PositionControlPlugin\n+    : public ControlPluginTemplate<Point3, Matrix3, Point3, Matrix3> \n+{ \n+public:\n+\tPositionControlPlugin(ClassDesc2* classDesc)\n+\t\t: ControlPluginTemplate(classDesc)\n+\t{ \n+\t}\n+\n+    /**\n+     * Identifies the plug-in as a position controller plug-in.\n+     * \n+     * \\return     Returns a value of type SClass_ID.\n+     */\n+    virtual SClass_ID SuperClassID()\n+    {\n+        return CTRL_POSITION_CLASS_ID;\n+    }\n+};\n+\n+/**\n+ * A base class for controller plug-ins that manage rotation.\n+ */\n+class RotationControlPlugin \n+    : public ControlPluginTemplate<Quat, Matrix3, Quat, Matrix3> \n+{ \n+public:\n+\tRotationControlPlugin(ClassDesc2* classDesc)\n+\t\t: ControlPluginTemplate(classDesc)\n+\t{ \n+\t}\n+\n+    /**\n+     * Identifies the plug-in as a rotation controller plug-in.\n+     * \n+     * \\return     Returns a value of type SClass_ID.\n+     */\n+    virtual SClass_ID SuperClassID()\n+    {\n+        return CTRL_ROTATION_CLASS_ID;\n+    }\n+};\n+\n+/**\n+ * A base class for controller plug-ins that manage scale.\n+ */\n+class ScaleControlPlugin\n+    : public ControlPluginTemplate<ScaleValue, Matrix3, ScaleValue, Matrix3> \n+{ \n+public:\n+\tScaleControlPlugin(ClassDesc2* classDesc)\n+\t\t: ControlPluginTemplate(classDesc)\n+\t{ \n+\t}\n+\n+    /**\n+     * Identifies the plug-in as a scale controller plug-in.\n+     * \n+     * \\return     Returns a value of type SClass_ID.\n+     */\n+    virtual SClass_ID SuperClassID()\n+    {\n+        return CTRL_SCALE_CLASS_ID;\n+    }\n+};\ndiff --git a/Shared/ControlUtility.h b/Shared/ControlUtility.h\nnew file mode 100644\nindex 00000000..581ec0ba\n--- /dev/null\n+++ b/Shared/ControlUtility.h\n@@ -0,0 +1,228 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+// A utility template class, used for the construction of wrappers around \n+// controls for type safe GetValue and SetValue functions. Helps with error \n+// detection at compile-time and code completion. You shouldn\'t use this class directly\n+// but instead one of the classes derived from it below, such as FloatControlWrapper, etc.\n+template<typename GetAbs_T, typename GetRel_T, typename SetAbs_T, typename SetRel_T>\n+class ControlWrapper\n+{\n+private:\n+\n+    Control* control;\n+\n+public:\n+    \n+    ControlWrapper(Control* ctrl)\n+        : control(ctrl)\n+    { }\n+\n+    Control* GetCtrl()\n+    {\n+        return ctrl;\n+    }\n+\n+    void GetAbsoluteValue(GetAbs_T& val, TimeValue t = GetCOREInterface()->GetTime(), Interval& valid = FOREVER)\n+    {\n+        return control->GetValue(t, &val, valid, CTRL_ABSOLUTE);\n+    }\n+\n+    void GetRelativeValue(GetRel_T& val, TimeValue t = GetCOREInterface()->GetTime(), Interval& valid = FOREVER)\n+    {\n+        return control->GetValue(t, &val, valid, CTRL_RELATIVE);\n+    }\n+\n+    void SetAbsoluteValue(const SetAbs_T& val, int commit = 1, TimeValue t = GetCOREInterface()->GetTime())\n+    {\n+        return control->SetValue(t, const_cast<void*>(&val), commit, CTRL_ABSOLUTE);\n+    }\n+\n+    void SetRelativeValue(const SetRel_T& val, int commit = 1, TimeValue t = GetCOREInterface()->GetTime())\n+    {\n+        return control->SetValue(t, const_cast<void*>(&val), commit, CTRL_RELATIVE);\n+    }\n+};\n+    \n+//=========================================================================================\n+// These functions are useful mnemonics for checking what kind of control\n+// we are looking at, without having to remember the class ID\'s \n+\n+BOOL IsFloatController(Control* ctrl) { \n+    return ctrl->SuperClassID() == CTRL_FLOAT_CLASS_ID; \n+} \n+\n+BOOL IsPoint3Controller(Control* ctrl) { \n+    return ctrl->SuperClassID() == CTRL_POINT3_CLASS_ID; \n+} \n+\n+BOOL IsMatrix3Controller(Control* ctrl) { \n+    return ctrl->SuperClassID() == CTRL_MATRIX3_CLASS_ID; \n+} \n+\n+BOOL IsPositionController(Control* ctrl) { \n+    return ctrl->SuperClassID() == CTRL_POSITION_CLASS_ID; \n+} \n+\n+BOOL IsRotationController(Control* ctrl) { \n+    return ctrl->SuperClassID() == CTRL_ROTATION_CLASS_ID; \n+} \n+\n+BOOL IsScaleController(Control* ctrl) { \n+    return ctrl->SuperClassID() == CTRL_SCALE_CLASS_ID; \n+} \n+\n+//===============================================================================\n+// The following are used as wrappers around controllers for \n+// type-safe getting and setting of values. \n+\n+struct FloatControlWrapper \n+    : public ControlWrapper<float, float, float, float> \n+{ \n+    FloatControlWrapper(Control* ctrl)\n+        : ControlWrapper(ctrl)\n+    { \n+        DbgAssert(IsFloatController(ctrl));\n+    }\n+};\n+\n+struct Point3ControlWrapper \n+    : public ControlWrapper<Point3, Point3, Point3, Point3> \n+{ \n+    Point3ControlWrapper(Control* ctrl)\n+        : ControlWrapper(ctrl)\n+    { \n+        DbgAssert(IsPoint3Controller(ctrl));\n+    }\n+};\n+\n+struct Matrix3ControlWrapper \n+    : public ControlWrapper<Matrix3, Matrix3, SetXFormPacket, SetXFormPacket> \n+{ \n+    Matrix3ControlWrapper(Control* ctrl)\n+        : ControlWrapper(ctrl)\n+    { \n+        DbgAssert(IsMatrix3Controller(ctrl));\n+    }\n+};\n+\n+struct PositionControlWrapper \n+    : public ControlWrapper<Point3, Matrix3, Point3, Matrix3> \n+{ \n+    PositionControlWrapper(Control* ctrl)\n+        : ControlWrapper(ctrl)\n+    { \n+        DbgAssert(IsPositionController(ctrl));\n+    }\n+};\n+\n+struct RotationControlWrapper \n+    : public ControlWrapper<Quat, Matrix3, Quat, Matrix3> \n+{ \n+    RotationControlWrapper(Control* ctrl)\n+        : ControlWrapper(ctrl)\n+    { \n+        DbgAssert(IsRotationController(ctrl));\n+    }\n+};\n+\n+struct ScaleControlWrapper \n+    : public ControlWrapper<ScaleValue, Matrix3, ScaleValue, Matrix3> \n+{ \n+    ScaleControlWrapper(Control* ctrl)\n+        : ControlWrapper(ctrl)\n+    { \n+        DbgAssert(IsScaleController(ctrl));\n+    }\n+};\n+\n+//====================================================================================\n+// Sub-controller querying and replacement utility functions\n+\n+// Finds a sub-controller\n+ISubTargetCtrl* FindSubCtrl(Control* ctrl, Control*& subCtrl)\n+{\n+\tISubTargetCtrl* assign = NULL;\n+    Control* child = NULL;\n+\tsubCtrl = NULL;\n+\tfor ( ISubTargetCtrl* next = GetSubTargetInterface(ctrl); next != NULL; next = GetSubTargetInterface(child)) {\n+\t\tchild = next->GetTMController();\n+\t\tif (child == NULL)\n+\t\t\tbreak;\n+\t\tif (next->CanAssignTMController()) {\n+\t\t\tassign = next;\n+\t\t\tsubCtrl = child;\n+\t\t}\n+\t}\n+\n+\treturn assign;\n+}\n+\n+// Replaces a lookat controller\n+bool ReplaceSubLookatController(Control* old)\n+{\n+\tControl* child = NULL;\n+\tISubTargetCtrl* assign = FindSubCtrl(old, child);\n+\tif (assign == NULL)\n+\t\treturn false;\n+\tDbgAssert(assign->CanAssignTMController()); \n+    DbgAssert(child != NULL);\n+\n+\tControl *tmc = NewDefaultMatrix3Controller();\n+\ttmc->Copy(child); // doesn\'t copy rotation, only scale and position.\n+\tassign->AssignTMController(tmc);\n+\n+\treturn true;\n+}\n+\n+// Removes the target controller\n+void ClearTargetController(INode* node)\n+{\n+\tControl* old = node->GetTMController();\n+\n+\tif (!ReplaceSubLookatController(old)) {\n+\t\tControl *tmc = NewDefaultMatrix3Controller();\n+\t\ttmc->Copy(old); // doesn\'t copy rotation, only scale and position.\n+\t\tnode->SetTMController(tmc);\n+\t}\n+}\n+\n+// Replaces the sub PRS-controller\n+bool ReplaceSubPRSController(Control* old, INode* targNode)\n+{\n+\tControl* child = NULL;\n+\tISubTargetCtrl* assign = FindSubCtrl(old, child);\n+\tif (assign == NULL)\n+\t\treturn false;\n+\tDbgAssert(assign->CanAssignTMController() && child != NULL);\n+\n+\tControl *laControl = CreateLookatControl();\n+\tlaControl->SetTarget(targNode);\n+\tlaControl->Copy(child);\n+\tassign->AssignTMController(laControl);\n+\n+\treturn true;\n+}\n+\n+// Sets the target controller\n+void SetTargetController(INode* node, INode* targNode)\n+{\n+\tControl* old = node->GetTMController();\n+\tif (!ReplaceSubPRSController(old, targNode)) {\n+\t\t// assign lookat controller\n+\t\tControl *laControl= CreateLookatControl();\n+\t\tlaControl->SetTarget(targNode);\n+\t\tlaControl->Copy(old);\n+\t\tnode->SetTMController(laControl);\n+\t}\n+}\n\\ No newline at end of file\ndiff --git a/Shared/FXPlugin.h b/Shared/FXPlugin.h\nnew file mode 100644\nindex 00000000..082563a1\n--- /dev/null\n+++ b/Shared/FXPlugin.h\n@@ -0,0 +1,27 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/**\n+ * An instantiation of this template class is used when a plug-in\n+ * derives from SpecialFX.\n+ */\n+template<typename Base_T>\n+class FXPlugin\n+    : public AnimatablePlugin<Base_T>\n+{\n+public:\n+    FXPlugin(ClassDesc2* classDesc)\n+        : AnimatablePlugin(classDesc)        \n+    { \n+    }\n+};\ndiff --git a/Shared/Macros.h b/Shared/Macros.h\nnew file mode 100644\nindex 00000000..c7afdfc5\n--- /dev/null\n+++ b/Shared/Macros.h\n@@ -0,0 +1,25 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+//========================================================\n+// Utility macros\n+\n+/// Used for constructing strings at compile time.\n+#define STRING2(X) #X\n+/// Used for constructing strings at compile time.\n+#define STRING(X) STRING2(X)\n+/// Creates a message pragma with specific instructions that appears in the output pane.\n+#define TODO(MSG) __pragma(message("TODO, at line " STRING(__LINE__) " in file : " __FILE__ " : " MSG))\n+\n+// Forces an assertion to occur\n+#define DbgError(X) DbgAssert(false && (X))\ndiff --git a/Shared/ManipulatorPlugin.h b/Shared/ManipulatorPlugin.h\nnew file mode 100644\nindex 00000000..7a5f74fc\n--- /dev/null\n+++ b/Shared/ManipulatorPlugin.h\n@@ -0,0 +1,45 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include <manipulator.h>\n+\n+/**\n+ * Base class for manipulators. Derives from SimpleManipulator \n+ * which manages references and parameter block. \n+ */\n+class ManipulatorPlugin\n+    : public SimpleManipulator\n+{\n+\n+public:\n+\n+    ManipulatorPlugin() \n+        : SimpleManipulator()\n+    {\n+    }\n+\n+    ManipulatorPlugin(INode* node)\n+        : SimpleManipulator(node)\n+    { \n+    }\n+\n+    /**\n+     * Default implementation of Object::GetCreateMouseCallBack()\n+     * \n+     * \\return     Returns a value of type CreateMouseCallBack *.\n+     */\n+    virtual CreateMouseCallBack* GetCreateMouseCallBack()\n+    {\n+        return NULL;\n+    }\n+};\n\\ No newline at end of file\ndiff --git a/Shared/MaterialPlugin.h b/Shared/MaterialPlugin.h\nnew file mode 100644\nindex 00000000..4ff2b08e\n--- /dev/null\n+++ b/Shared/MaterialPlugin.h\n@@ -0,0 +1,268 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "MtlBasePlugin.h"\n+\n+//=========================================================================\n+// Provides default empty implementations of the Mtl functions\n+\n+/** \n+ * Base class for material plug-ins (derived from Mtl) which provides default \n+ * implementations of the Mtl function.\n+ */\n+class MaterialPlugin\n+    : public MtlBasePlugin<Mtl>\n+{\n+\n+public:\n+\n+    //===========================================================================\n+    // Constructor\n+\n+    MaterialPlugin(ClassDesc2* classDesc) \n+        : MtlBasePlugin(classDesc)\n+    {\n+    }\n+\n+    //===========================================================================\n+    // Mtl overrides\n+   \n+    /**\n+     * \\see         Mtl::NumSubMtls() \n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int NumSubMtls()\n+    {\n+        return 0;\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetSubMtl() \n+     * \\param       i An argument of type int.\n+     * \\return      Returns a value of type Mtl *.\n+     */\n+    virtual Mtl* GetSubMtl(int i)\n+    {\n+        // Does nothing. \n+        DbgError(_T("There is no sub-material"));\n+        return NULL;\n+    }\n+    \n+    /**\n+     * \\see         Mtl::SetSubMtl()\n+     * \\param       i An argument of type int.\n+     * \\param       m An argument of type Mtl *.\n+     */\n+    virtual void SetSubMtl(int i, Mtl *m)\n+    {\n+        DbgError(_T("There are no sub-materials"));       \n+    }\n+\n+    /**\n+     * \\see         Mtl::GetSubMtlSlotName()\n+     * \n+     * \\param       i An argument of type int.\n+     * \\return      Returns a value of type TSTR.\n+     */\n+    virtual TSTR GetSubMtlSlotName(int i)\n+    {\n+        DbgError(_T("There are no sub-materials"));       \n+\t    return _T(""); \n+    }\n+\n+    /**\n+     * \\see         Mtl::GetSubMtlTVName()\n+     * \n+     * \\param       i An argument of type int.\n+     * \\return      Returns a value of type TSTR.\n+     */\n+    virtual TSTR GetSubMtlTVName(int i)\n+    {\n+        DbgError(_T("There are no sub-materials"));       \n+\t    return GetSubMtlSlotName(i);\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetSubTexmapTVName()\n+     * \n+     * \\param       i An argument of type int.\n+     * \\return      Returns a value of type TSTR.\n+     */\n+    virtual TSTR GetSubTexmapTVName(int i)\n+    {\n+\t    return GetSubTexmapSlotName(i);\n+    }\n+\n+    /**\n+     * \\see         Mtl::SetAmbient()\n+     * \n+     * \\param       c An argument of type Color.\n+     * \\param       t An argument of type TimeValue.\n+     */\n+    virtual void SetAmbient(Color c, TimeValue t) \n+    {\n+        // Does nothing by default        \n+    }\t\t\n+\n+    /**\n+     * \\see         Mtl::SetDiffuse()\n+     * \n+     * \\param       c An argument of type Color.\n+     * \\param       t An argument of type TimeValue.\n+     */\n+    virtual void SetDiffuse(Color c, TimeValue t) \n+    {\n+        // Does nothing by default\n+    }\t\t\n+    \n+    /**\n+     * \\see         Mtl::SetSpecular()\n+     * \n+     * \\param       c An argument of type Color.\n+     * \\param       t An argument of type TimeValue.\n+     */\n+    virtual void SetSpecular(Color c, TimeValue t) \n+    {\n+        // Does nothing by default\n+    }\n+\n+    /**\n+     * \\see         Mtl::SetShininess()\n+     * \n+     * \\param       v An argument of type float.\n+     * \\param       t An argument of type TimeValue.\n+     */\n+    virtual void SetShininess(float v, TimeValue t) \n+    {\n+        // Does nothing by default\n+    }\n+    \t\t\t\t\n+    /**\n+     * \\see         Mtl::GetAmbient()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type Color.\n+     */\n+    virtual Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return Color(0,0,0);\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetDiffuse()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type Color.\n+     */\n+    virtual Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return Color(0,0,0);\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetSpecular()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type Color.\n+     */\n+    virtual Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return Color(0,0,0);\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetXParency()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type float.\n+     */\n+    virtual float GetXParency(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return 0.0f;\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetShininess()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type float.\n+     */\n+    virtual float GetShininess(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return 0.0f;\n+    }\n+\n+    /**\n+     * \\see         Mtl::GetShinStr()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type float.\n+     */\n+    virtual float GetShinStr(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return 0.0f;\n+    }\n+\n+    /**\n+     * \\see         Mtl::WireSize()\n+     * \n+     * \\param       mtlNum An argument of type int.\n+     * \\param       backFace An argument of type BOOL.\n+     * \\return      Returns a value of type float.\n+     */\n+    virtual float WireSize(int mtlNum=0, BOOL backFace=FALSE)\n+    {\n+\t    return 0.0f;\n+    }\n+\n+    /**\n+     * \\see         Mtl::Shade()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     */\n+    virtual void Shade(ShadeContext& sc)\n+    {\n+    \tif (gbufID) sc.SetGBufferID(gbufID);\n+    }\n+  \t\t\n+    /**\n+     * \\see         Mtl::EvalDisplacement()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type float.\n+     */\n+    virtual float EvalDisplacement(ShadeContext& sc)\n+    {\n+        // Do nothing by default\n+        return 0.0f;\n+    }\n+\n+    /**\n+     * \\see         Mtl::DisplacementValidity()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type Interval.\n+     */\n+    virtual Interval DisplacementValidity(TimeValue t)\n+    {\n+        // Do nothing by default\n+        return Interval();\n+    }\n+};\n+    \ndiff --git a/Shared/MathUtility.h b/Shared/MathUtility.h\nnew file mode 100644\nindex 00000000..45a0d1d6\n--- /dev/null\n+++ b/Shared/MathUtility.h\n@@ -0,0 +1,256 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/// The value of the constant pi as a float.\n+float Pi()\n+{\n+    return 3.141592653589f;\n+}\n+\n+/// The value of the pi divided by two.\n+float HalfPi()\n+{\n+    return Pi() / 2;\n+}\n+\n+/// A small value, used for approximate comparisons\n+float Epsilon() \n+{\n+    return 0.00000001f;\n+}\n+\n+/// Returns a value just under 1.\n+float OneMinusEpsilon(float e = Epsilon()) \n+{\n+    return 1.0f - e;\n+}\n+\n+/// Compares two values to see if they are equal to some degree of error\n+template<typename T>\n+BOOL NearlyEquals(T x, T y, T e = Epsilon())\n+{\n+    return (x >= y - e) && (x <= y + e);\n+}\n+\n+/// Compares a value to see if it is equal to +/- Epsilon\n+template<typename T>\n+BOOL NearZero(T x, T e = Epsilon())\n+{\n+    return fabs(x) <= e;\n+}\n+\n+/// Returns the sign of a value, or zero if it is equal (or near) to zero\n+int Sign(float f, float e = Epsilon()) {\n+    return NearZero(f, e)\n+        ? 0 \n+        : ( f > 0.0f ? 1 : -1);\n+}\n+\n+/// Compares two matrices. \n+bool EqualMatrices(const Matrix3& m1,const Matrix3& m2) \n+{\n+\treturn m1.GetRow(0)==m2.GetRow(0) && \n+\t\t     m1.GetRow(1)==m2.GetRow(1) && \n+\t\t     m1.GetRow(2)==m2.GetRow(2) && \n+\t\t     m1.GetRow(3)==m2.GetRow(3);\n+}\n+\n+/* \n+ * Checks that the two points cross the XY plane.  \n+ * If so, computes the crossing point.\n+ *\n+ * \\param pt1 First point.\n+ * \\param pt2 Second point.\n+ * \\param[out] Point of intersection.\n+ * \n+ * \\return An integer indicating the kind of intersection. \n+ * - 2  both points are on the plane (cross_pt is not changed)\n+ * - 1  cross_pt contains the one point of intersection\n+ * - 0  no points of intersection (cross_pt not changed)\n+ */\n+int CrossCheck(const Point3& pt1,const Point3& pt2,Point3* cross_pt)\n+{\n+\tconst float\tz1 = pt1.z;\n+\tconst float\tz2 = pt2.z;\n+\tconst int\tsign_z1 = Sign(z1);\n+\tconst int\tsign_z2 = Sign(z2);\n+\n+\t// check for corners straddling the plane\n+\tif(sign_z1 == 0 && sign_z2 == 0)\n+\t\treturn 2;\n+\telse if(sign_z1 != sign_z2)\n+\t{\n+\t\t// straddle!  compute intersection pt\n+\t\tif(cross_pt)\n+\t\t{\n+\t\t\tfloat dd = z1 / (z1 - z2);\n+\t\t\tcross_pt->x = pt1.x + dd * (pt2.x - pt1.x);\n+\t\t\tcross_pt->y = pt1.y + dd * (pt2.y - pt1.y);\n+\t\t\tcross_pt->z = 0.0f;\n+\t\t}\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+\n+/// An approximate method of comparing Point3\'s.\n+bool EqualPoint3(const Point3& p1, const Point3& p2, float e = Epsilon())\n+{\n+    return NearlyEquals(p1.x, p2.x, e) \n+        && NearlyEquals(p1.y, p2.y, e)\n+        && NearlyEquals(p1.z, p2.z, e);\n+}\n+\n+/// Removes scaling from a matrix\n+void RemoveScaling(Matrix3 &tm) \n+{\n+\tAffineParts ap;\n+\tdecomp_affine(tm, &ap);\n+\ttm.IdentityMatrix();\n+\ttm.SetRotate(ap.q);\n+\ttm.SetTrans(ap.t);\n+}\n+\n+/// Creates a matrix from AffineParts\n+void ComposeFromAffine(const AffineParts &ap, Matrix3 &mat)\n+{\n+\tMatrix3 tm;\n+\t\n+\tmat.IdentityMatrix();\n+\tmat.SetTrans( ap.t );\n+\n+\tif ( ap.f != 1.0f ) {\t\t\t\t// has f component\n+\t\ttm.SetScale( Point3( ap.f, ap.f, ap.f ) );\n+\t\tmat = tm * mat;\n+\t}\n+\n+\tif ( !ap.q.IsIdentity() ) {\t\t\t// has q rotation component\n+\t\tap.q.MakeMatrix( tm );\n+\t\tmat = tm * mat;\n+\t}\n+\t\n+\tif ( ap.k.x != 1.0f || ap.k.y != 1.0f || ap.k.z != 1.0f ) {\t\t// has k scale component\n+\t\ttm.SetScale( ap.k );\n+\t\tif ( !ap.u.IsIdentity() ) {\t\t\t// has u rotation component\n+\t\t\tMatrix3 utm;\n+\t\t\tap.u.MakeMatrix( utm );\n+\t\t\tmat = Inverse( utm ) * tm * utm * mat;\n+\t\t} else {\n+\t\t\tmat = tm * mat;\n+\t\t}\n+\t}\n+}\n+\n+/// Computes the distance from a matrix representation of location to a point.\n+float GetDistanceTo(const Matrix3& m, const Point3& pt)\n+{\n+    return Length(m.GetTrans() - pt) / Length(m.GetRow(2));\n+}\n+\n+/// Swaps two values\n+template<typename T>\n+void Swap(T& x, T& y)\n+{\n+    T temp = x;\n+    x = y;\n+    y = temp;\n+}\n+\n+/// Swaps two values, if the first is greater than the second.\n+template<typename T>\n+void SwapIfGreater(T& x, T& y)\n+{\n+    if (x > y)\n+        Swap(x, y);\n+}\n+\n+/// Returns TRUE if a value x is between the values a and b.\n+template<typename T>\n+BOOL IsBetween(T x, T a, T b) \n+{\n+    SwapIfGreater(a, b);\n+    return ((x >= a) && (x <= b));\n+}\n+\n+/// Interpolates a value m by applying a function represented by \n+/// an array (ntab) depending on which interval it appears in in mtab\n+float InterpolateValues(float m, float *mtab, float *ntab, int n) \n+{\n+\tfloat frac;\n+\tfor (int i=1; i<n; i++) {\n+\t\tif (IsBetween(m,mtab[i-1],mtab[i])) {\n+\t\t\tfrac = (m - mtab[i-1])/(mtab[i]-mtab[i-1]);\n+\t\t\treturn((1.0f-frac)*ntab[i-1] + frac*ntab[i]);\n+\t\t}\n+\t}\n+\treturn 0.0f;\n+}\n+\n+/// Computes the projection of a point on a perpendicular place\n+Point3 ProjectionOnPerpPlane(const Point3& Vector_ProjectionOf, const Point3& Vector_OnPlanePerpTo)\n+{\n+\tPoint3 xAxis = CrossProd(Vector_OnPlanePerpTo, Vector_ProjectionOf);\n+\n+\tif (FLength(xAxis) < Epsilon())\n+    {\n+\t\tint ix = Vector_OnPlanePerpTo.MinComponent();\n+\t\txAxis = Point3(0,0,0); \n+\t\txAxis[ix] = 1;\n+\t\tPoint3 cp = CrossProd(xAxis, Vector_OnPlanePerpTo);\n+\t\txAxis = Normalize(CrossProd(Vector_OnPlanePerpTo, cp));\n+\t}\n+\n+\tPoint3 yAxis = CrossProd(xAxis, Vector_OnPlanePerpTo);\n+\treturn Normalize(yAxis);\n+}\n+\n+/// Return the identity matrix\n+const Matrix3& Identity()\n+{\n+    static const Matrix3 r(1);\n+    return r;\n+}\n+\n+/// Given a point and target, computes a quaternion that represents the rotation\n+/// to look towards the target.\n+Quat ComputeLookAtQuat(const Point3& source, const Point3& target)\n+{\n+\tPoint3 source_target_vector = target - source;\n+\tPoint3 source_target_unit_vector = Normalize(source_target_vector);\t\t\n+    Point3 source_to_upnode_vector = Normalize(-source);\n+\t\n+\tPoint3 zdir = source_target_unit_vector;\n+\tPoint3 ydir = -Normalize(ProjectionOnPerpPlane(source_to_upnode_vector, zdir));\n+\tPoint3 xdir = Normalize(CrossProd(ydir, zdir));\n+\n+    Matrix3 mat(xdir, ydir, zdir, Point3::Origin);\t\n+    Quat quat(mat);\n+\tquat.MakeClosest(IdentQuat());\n+\tquat.Normalize();\n+\n+\treturn quat;\n+}\n+\n+/// Converts degrees to radians.\n+float DegToRad(float deg)\n+{\n+    return (Pi() * deg) / 180.0f;\n+}\n+\n+/// Returns x divided by y, unless y is nearly zero in which \n+/// case it returns 0.\n+float SafeDivide(float x, float y, float e = Epsilon())\n+{\n+    return NearZero(y, e) ? 0.0f : x / y;\n+}\n\\ No newline at end of file\ndiff --git a/Shared/ModifierPlugin.h b/Shared/ModifierPlugin.h\nnew file mode 100644\nindex 00000000..b9b34d5b\n--- /dev/null\n+++ b/Shared/ModifierPlugin.h\n@@ -0,0 +1,610 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include <objmode.h>\n+\n+/**\n+ * This class is a base class for modifier plug-ins which is a modern \n+ * implementation from SimpleMod and SimpleMod2. One feature that is \n+ * missing from SimpleMod is support for ModifierLimits. There \n+ * is no support for parameter block 1. \n+ *\n+ * \\par Like SimpleMod this provides automatic management of modes, and \n+ * the the gizmo and center sub-object. To use this class you will \n+ * have to implement the pure virtual function GetDeformer(). \n+ *\n+ * \\see         Modifier, SimpleMod, SimpleMod2, AnimatablePlugin\n+ */\n+template<typename Base_T>\n+class ModifierPlugin\n+    : public AnimatablePlugin<Base_T>\n+{   \n+private:\n+\n+    MoveModBoxCMode*    moveMode;\n+    RotateModBoxCMode*  rotMode;\n+    UScaleModBoxCMode*  uscaleMode;\n+    NUScaleModBoxCMode* nuscaleMode;\n+    SquashModBoxCMode*  squashMode;\n+\n+    GenSubObjType GizmoSubObject;\n+    GenSubObjType CenterSubObject; \n+\n+    static const int GIZMO_ICON_INDEX = 14;\n+    static const int CENTER_ICON_INDEX = 15;\n+\n+public:\n+\n+    ModifierPlugin(ClassDesc2* classDesc)\n+        : AnimatablePlugin(classDesc), GizmoSubObject(GIZMO_ICON_INDEX), CenterSubObject(CENTER_ICON_INDEX)\n+\t{\n+\t    moveMode    = NULL;\n+        rotMode \t= NULL;\n+        uscaleMode  = NULL;\n+        nuscaleMode = NULL;\n+        squashMode  = NULL;\n+\n+\t\tGizmoSubObject.SetName(_T("Gizmo"));\n+\t\tCenterSubObject.SetName(_T("Center"));\n+\n+        RegisterReference(1, NewDefaultMatrix3Controller(), false);\n+        RegisterReference(2, NewDefaultPositionController(), false);            \n+\n+        RegisterSubAnim(1, 1, "Gizmo");\n+        RegisterSubAnim(2, 2, "Center");\n+\t}\n+\n+\n+    ~ModifierPlugin()\n+    {\n+        DeleteModes();\n+    }\n+\n+\n+    /// \\name Animatable Overrides\n+    //@{\n+    /**\n+     * \\see         Animatable::BeginEditParams()\n+     * \n+     * \\param       ip An argument of type IObjParam *.\n+     * \\param       flags An argument of type ULONG.\n+     * \\param       prev An argument of type Animatable *.\n+     */\n+    virtual void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev )\n+\t{\n+        DeleteModes();\n+\n+        moveMode    = new MoveModBoxCMode(this,ip);\n+\t    rotMode     = new RotateModBoxCMode(this,ip);\n+\t    uscaleMode  = new UScaleModBoxCMode(this,ip);\n+\t    nuscaleMode = new NUScaleModBoxCMode(this,ip);\n+\t    squashMode  = new SquashModBoxCMode(this,ip);\t\n+    \t\n+\t    TimeValue t = Now();\n+\t    NotifyDependents(Interval(t,t), (PartID)PART_ALL, REFMSG_BEGIN_EDIT);\n+\t    NotifyDependents(Interval(t,t), (PartID)PART_ALL, REFMSG_MOD_DISPLAY_ON);\n+\n+        SetAFlag(A_MOD_BEING_EDITED);\n+\n+        AnimatablePlugin::BeginEditParams(ip, flags, prev);\n+\t}\n+\n+    /**\n+     * \\see         Animatable::EndEditParams()\n+     * \n+     * \\param       ip An argument of type IObjParam *.\n+     * \\param       flags An argument of type ULONG.\n+     * \\param       next An argument of type Animatable *.\n+     */\n+    virtual void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next)\n+\t{\n+        AnimatablePlugin::EndEditParams(ip, flags, next);\n+\n+        // NOTE: This flag must be cleared before sending the REFMSG_END_EDIT\n+\t    ClearAFlag(A_MOD_BEING_EDITED);\n+\n+\t    TimeValue t = Now();\n+\t    NotifyDependents(Interval(t,t), (PartID)PART_ALL, REFMSG_END_EDIT);\n+\t    NotifyDependents(Interval(t,t), (PartID)PART_ALL, REFMSG_MOD_DISPLAY_OFF);\n+\n+        DeleteModes();\n+\t}\n+    //@}\n+\n+    \n+    /// \\name BaseObject Overrides\n+    //@{\n+    /**\n+     * \\see         BaseObject::GetCreateMouseCallBack()\n+     * \n+     * \\return      Returns a value of type CreateMouseCallBack *.\n+     */\n+    virtual CreateMouseCallBack* GetCreateMouseCallBack() \n+    {\n+        return NULL;\n+    }\n+\n+    /**\n+     * \\see         BaseObject::ActivateSubobjSel()\n+     * \n+     * \\param       level An argument of type int.\n+     * \\param       modes An argument of type XFormModes &.\n+     */\n+    virtual void ActivateSubobjSel(int level, XFormModes& modes )\n+    {\t\n+\t    switch ( level ) \n+        {\n+\t\t    case 1: \n+                // Modifier box\n+\t\t\t    modes = XFormModes(moveMode, rotMode, nuscaleMode, uscaleMode, squashMode, NULL);\n+\t\t\t    break;\n+\t\t    case 2: \n+                // Modifier Center\n+\t\t\t    modes = XFormModes(moveMode, NULL, NULL, NULL, NULL, NULL);\n+\t\t\t    break;\n+\t\t}\n+\t    NotifyDependents(FOREVER,PART_DISPLAY,REFMSG_CHANGE);\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::GetSubObjectCenters()\n+     * \n+     * \\param       cb An argument of type SubObjAxisCallback *.\n+     * \\param       t An argument of type TimeValue.\n+     * \\param       node An argument of type INode *.\n+     * \\param       mc An argument of type ModContext *.\n+     */\n+    virtual void GetSubObjectCenters(SubObjAxisCallback *cb, TimeValue t, INode *node, ModContext *mc)\n+    {\n+        Interval valid;\n+        Matrix3 modmat, tm, ntm = node->GetObjTMBeforeWSM(t,&valid), off, invoff;\n+\n+        if (cb->Type() == SO_CENTER_PIVOT) {\n+\t        tm = CompMatrix(t,*mc,ntm,valid,TRUE);\n+\t        cb->Center(tm.GetTrans(),0);\n+        } else {\n+\t        modmat = CompMatrix(t,*mc,ntm,valid,FALSE);\n+\t        CompOffset(t,off,invoff);\n+\t        BoxLineProc bp1(&modmat);\t\n+\t        DoModifiedBox(*mc->box, *GetDeformer(t,*mc,invoff,off), bp1);\n+\t        cb->Center(bp1.Box().Center(),0);\n+\t    }\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::GetSubObjectTMs()\n+     * \n+     * \\param       cb An argument of type SubObjAxisCallback *.\n+     * \\param       t An argument of type TimeValue.\n+     * \\param       node An argument of type INode *.\n+     * \\param       mc An argument of type ModContext *.\n+     */\n+    virtual void GetSubObjectTMs(SubObjAxisCallback *cb,TimeValue t,INode *node, ModContext *mc)\n+    {\n+        Interval valid;\n+        Matrix3 ntm = node->GetObjTMBeforeWSM(t, &valid);\n+        Matrix3 tm = CompMatrix(t, *mc, ntm, valid, TRUE);\n+        cb->TM(tm,0);\n+    }\n+\n+    /**\n+     * \\see         BaseObject::Rotate()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       partm An argument of type Matrix3 &.\n+     * \\param       tmAxis An argument of type Matrix3 &.\n+     * \\param       val An argument of type Quat &.\n+     * \\param       localOrigin An argument of type BOOL.\n+     */\n+    virtual void Rotate( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Quat& val, BOOL localOrigin = FALSE) \n+    {\n+\t    SetXFormPacket pckt(val, localOrigin, partm, tmAxis);\n+\t    GetTMController()->SetValue(t, &pckt, TRUE, CTRL_RELATIVE);\t\t\n+\t    macroRecorder->OpAssign(_T("+="), mr_prop, _T("gizmo.rotation"), mr_reftarg, this, mr_quat, &val);\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::Scale()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       partm An argument of type Matrix3 &.\n+     * \\param       tmAxis An argument of type Matrix3 &.\n+     * \\param       val An argument of type Point3 &.\n+     * \\param       localOrigin An argument of type BOOL.\n+     */\n+    virtual void Scale(TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin = FALSE) \n+    {\n+\t    SetXFormPacket pckt(val, localOrigin, partm, tmAxis);\n+\t    GetTMController()->SetValue(t, &pckt, TRUE, CTRL_RELATIVE);\t\t\n+\t    macroRecorder->OpAssign(_T("*="), mr_prop, _T("gizmo.scale"), mr_reftarg, this, mr_point3, &val);\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::Display()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\param       flags An argument of type int.\n+     * \\param       mc An argument of type ModContext *.\n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags, ModContext *mc) \n+    {\n+        Interval valid;\n+        GraphicsWindow *gw = vpt->getGW();\n+        Matrix3 modmat, ntm = inode->GetObjTMBeforeWSM(t), off, invoff;\n+\n+        if (mc->box->IsEmpty()) return 0;\n+\n+        modmat = CompMatrix(t,*mc,ntm,valid,FALSE);\n+        CompOffset(t,off,invoff);\n+        gw->setTransform(modmat);\n+        if ( GetCOREInterface()->GetSubObjectLevel() == 1 ) {\t\t\n+\t        gw->setColor(LINE_COLOR,GetUIColor(COLOR_SEL_GIZMOS));\n+        } else {\n+\t        gw->setColor(LINE_COLOR,GetUIColor(COLOR_GIZMOS));\n+\t    }\t\n+        if (mc->box->pmin==mc->box->pmax) {\n+\t        gw->marker(&mc->box->pmin,ASTERISK_MRKR);\n+        } \n+        else {\n+\t        DoModifiedBox(*mc->box, *GetDeformer(t,*mc,invoff,off), DrawLineProc(gw));\n+\t    }\n+\n+        modmat = CompMatrix(t,*mc,ntm,valid,TRUE);\n+        gw->setTransform(modmat);\n+        if ( GetCOREInterface()->GetSubObjectLevel() == 1 || GetCOREInterface()->GetSubObjectLevel() == 2) \n+        {\t\t\n+\t        gw->setColor( LINE_COLOR, (float)1.0, (float)1.0, (float)0.0);\n+        } \n+        else \n+        {\n+\t        gw->setColor( LINE_COLOR, (float).85, (float).5, (float)0.0);\n+\t    }\t\n+        DrawCenterMark(DrawLineProc(gw),*mc->box);\t\n+\n+    \treturn 0;\t\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::GetWorldBoundBox()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\param       box An argument of type Box3 &.\n+     * \\param       mc An argument of type ModContext *.\n+     */\n+    virtual void GetWorldBoundBox(TimeValue t,INode* inode, ViewExp *vpt, Box3& box, ModContext *mc) \n+    {\n+\t    Interval valid;\n+\t    Matrix3 modmat, ntm = inode->GetObjTMBeforeWSM(t), off, invoff;\n+\n+\t    if (mc->box->IsEmpty()) \n+            return;\n+\n+\t    modmat = CompMatrix(t, *mc, ntm, valid, FALSE);\n+\t    CompOffset(t,off,invoff);\t\n+\t    BoxLineProc bp1(&modmat);\t\n+\t    DoModifiedBox(*mc->box, *GetDeformer(t, *mc, invoff, off), bp1);\n+\t    box = bp1.Box();\n+\n+\t    modmat = CompMatrix(t, *mc, ntm, valid, TRUE);\t\n+\t    BoxLineProc bp2(&modmat);\t\t\n+\t    DrawCenterMark(bp2,*mc->box);\n+\t    box += bp2.Box();\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::HitTest()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       type An argument of type int.\n+     * \\param       crossing An argument of type int.\n+     * \\param       flags An argument of type int.\n+     * \\param       p An argument of type IPoint2 *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\param       mc An argument of type ModContext *.\n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt, ModContext* mc) \n+    {\n+\t    if (mc->box->IsEmpty()) \n+            return 0;\n+\n+\t    Interval valid;\n+\t    GraphicsWindow *gw = vpt->getGW();\n+\t    HitRegion hr;\n+\t    MakeHitRegion(hr,type, crossing,4,p);\n+\t    gw->setHitRegion(&hr);\n+\t    Matrix3 modmat, ntm = inode->GetObjTMBeforeWSM(t);\n+\n+        HitTestModeRAII hitTestHelper(gw);\n+    \t\n+        Interface* ip = GetCOREInterface();\n+\t    if (ip->GetSubObjectLevel() == 1) {\n+\t\t    Matrix3 off, invoff;\n+\t\t    modmat = CompMatrix(t,*mc,ntm,valid,FALSE);\n+\t\t    CompOffset(t,off,invoff);\n+\t\t    gw->setTransform(modmat);\t\t\n+\t\t    if (mc->box->pmin==mc->box->pmax) {\n+\t\t\t    gw->marker(&mc->box->pmin,ASTERISK_MRKR);\n+\t\t    } \n+            else {\n+\t\t\t    DoModifiedBox(*mc->box,*GetDeformer(t,*mc,invoff,off), DrawLineProc(gw));\n+\t\t    }\n+\t    }\n+\n+\t    if (ip->GetSubObjectLevel() == 1 ||\n+\t              ip->GetSubObjectLevel() == 2 ) {\n+\t\t    modmat = CompMatrix(t,*mc,ntm,valid,TRUE);\n+\t\t    gw->setTransform(modmat);\n+\t\t    DrawCenterMark(DrawLineProc(gw),*mc->box);\n+\t    }\n+\n+\t    if (gw->checkHitCode()) {\n+\t\t    vpt->LogHit(inode, mc, gw->getHitDistance(), 0, NULL); \n+\t\t    return TRUE;\n+\t\t}\n+        else {\n+\t        return FALSE;\n+        }\n+\t}\n+\n+    /**\n+     * \\see         BaseObject::NumSubObjTypes()\n+     * \n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int NumSubObjTypes() \n+    { \n+\t    return 2;\n+    }\n+\t\t\n+    /**\n+     * \\see         BaseObject::GetSubObjType()\n+     * \n+     * \\param       i An argument of type int.\n+     * \\return      Returns a value of type ISubObjType *.\n+     */\n+    virtual ISubObjType* GetSubObjType(int i) \n+    {\n+        switch (i)\n+        {\n+        case -1:\n+\t\t    if(GetSubObjectLevel() > 0)\n+\t\t\t    return GetSubObjType(GetSubObjectLevel()-1);\n+            break;\n+        case 0:\n+\t\t    return &GizmoSubObject;\n+        case 1:\n+\t\t    return &CenterSubObject;\n+        }\n+    \t\n+\t    return NULL;\n+    }\n+\n+    /**\n+     * \\see         BaseObject::Move()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       partm An argument of type Matrix3 &.\n+     * \\param       tmAxis An argument of type Matrix3 &.\n+     * \\param       val An argument of type Point3 &.\n+     * \\param       localOrigin An argument of type BOOL.\n+     */\n+    virtual void Move( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin = FALSE) \n+    {\n+\t    if ( GetCOREInterface()->GetSubObjectLevel() == 1) \n+        {\n+\t\t    SetXFormPacket pckt(val, partm, tmAxis);\n+\t\t    GetTMController()->SetValue(t, &pckt, TRUE, CTRL_RELATIVE);\t\t\n+\t\t    macroRecorder->OpAssign(_T("+="), mr_prop, _T("gizmo.pos"), mr_reftarg, this, mr_point3, &val);\n+\t    } \n+        else \n+        {\t\t\n+            Interval valid = FOREVER;\n+\t\t    Matrix3 ptm = partm;\n+            GetTMController()->GetValue(t,&ptm,valid,CTRL_RELATIVE);\n+\t\t    GetPosController()->SetValue(t, VectorTransform(tmAxis*Inverse(ptm),val), TRUE, CTRL_RELATIVE);\n+\t\t    macroRecorder->OpAssign(_T("+="), mr_prop, _T("center"), mr_reftarg, this, mr_point3, &val);\n+\t\t}\n+\t}\n+    //@}\n+\n+\t/// \\name Modifier Overrides\n+\t//@{\n+\t/**\n+\t * \\see         Modifier::ChannelsUsed()\n+\t * \n+\t * \\return      Returns a value of type ChannelMask.\n+\t */\n+\tvirtual ChannelMask ChannelsUsed() \n+    { \n+        return PART_GEOM|PART_TOPO|SELECT_CHANNEL|SUBSEL_TYPE_CHANNEL; \n+    }\n+\t\n+    /**\n+     * \\see         Modifier::ChannelsChanged()\n+     * \n+     * \\return      Returns a value of type ChannelMask.\n+     */\n+    virtual ChannelMask ChannelsChanged() \n+    { \n+        return PART_GEOM; \n+    }\n+\n+    /**\n+     * \\see         Modifier::InputType()\n+     * \n+     * \\return      Returns a value of type Class_ID.\n+     */\n+    virtual Class_ID InputType() \n+    {\n+        return defObjectClassID;\n+    }\n+\n+    /**\n+     * \\see         Modifier::ModifyObject()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       mc An argument of type ModContext &.\n+     * \\param       os An argument of type ObjectState *.\n+     * \\param       node An argument of type INode *.\n+     */\n+    virtual void ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) \n+\t{\t\n+\t    Interval valid = LocalValidity(t);\n+    \t\n+\t    // These are inverted becuase that\'s what is usually needed for displaying/hit testing\n+\t    Matrix3 minv   = CompMatrix(t,mc,idTM,valid,TRUE);\n+\t    Matrix3 modmat = Inverse(minv);\n+    \t\n+        // This calls the deform function of the object, using the virtual function GetDeformer\n+        Deformer* deformer = GetDeformer(t, mc, modmat, minv);\n+        DbgAssert(deformer != NULL && "The function GetDeformer must return a non-null object");\n+\t    os->obj->Deform(deformer, TRUE);\n+\t    os->obj->UpdateValidity(GEOM_CHAN_NUM, valid);\t\n+\t}\n+\n+    /**\n+     * \\see         Modifier::LocalValidity()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type Interval.\n+     */\n+    virtual Interval LocalValidity(TimeValue t)\n+\t{\n+\t    // if being edited, return NEVER forces a cache to be built \n+\t    // after previous modifier.\n+\t    if (IsBeingEdited())\n+\t\t    return NEVER;  \n+\t    Interval valid = FOREVER;\n+        for (int i=0; i < NumParamBlocks(); ++i)\n+            GetParamBlock(i)->GetValidity(t, valid);\n+\t    Matrix3 mat(1);\n+        GetTMController()->GetValue(t, &mat, valid, CTRL_RELATIVE);\n+        mat.IdentityMatrix();\n+        GetPosController()->GetValue(t, &mat, valid, CTRL_RELATIVE);\n+\n+\t    return valid;\n+\t}\n+\t//@}\n+\n+    //============================================================\n+    // Virtual function introduced by ModifierPlugin\n+    \n+    /// \\name ModifierPlugin Virtual Functions\n+    //@{\n+    /**\n+     * Returns a Deformer instance that can be used to perform the \n+     * actual modification of points.\n+     * \n+     * \\see         SimpleMod::GetDeformer()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       mc An argument of type ModContext &.\n+     * \\param       mat An argument of type Matrix3 &.\n+     * \\param       invmat An argument of type Matrix3 &.\n+     * \\return      Returns a value of type Deformer *.\n+     */\n+    virtual Deformer* GetDeformer(TimeValue t,\n+        ModContext &mc, Matrix3& mat, Matrix3& invmat) = 0;\n+    //@}\n+\n+    //============================================================\n+    // Utility functions\n+\n+    /// \\name Utility Functions\n+    //@{\n+    /**\n+     * Returns TRUE if the modifier is in edit mode.\n+     * \n+     * \\return      Returns a value of type BOOL.\n+     */\n+    BOOL IsBeingEdited()\n+    {\n+        return TestAFlag(A_MOD_BEING_EDITED);\n+    }\n+\n+    /**\n+     * Returns the transform matrix controller.\n+     * \n+     * \\return      Returns a value of type Control *.\n+     */\n+    Control* GetTMController()\n+    {\n+        return dynamic_cast<Control*>(GetReference(1));\n+    }\n+\n+    /**\n+     * Returns the position controller.\n+     * \n+     * \\return      Returns a value of type Control *.\n+     */\n+    Control* GetPosController()\n+    {\n+        return dynamic_cast<Control*>(GetReference(2));\n+    }\t\n+    //@}\n+\n+private:\n+\n+    Matrix3 CompMatrix(TimeValue t, ModContext& mc, Matrix3& ntm, Interval& valid, BOOL needOffset) \n+    {\n+\t    Matrix3 tm;\n+    \t\n+\t    if (mc.tm) \n+\t\t    tm = *mc.tm; else \n+\t\t    tm.IdentityMatrix();\t\n+\n+\t    Matrix3 mat;\n+\t    mat.IdentityMatrix();\n+\t    GetTMController()->GetValue(t,&mat,valid,CTRL_RELATIVE);\n+\t    tm = tm*Inverse(mat);\t\t\n+\t    \n+\t    mat.IdentityMatrix();\t\t\n+\t    GetPosController()->GetValue(t,&mat,valid,CTRL_RELATIVE);\n+\t    tm = tm*Inverse(mat);\t\t\n+    \t\n+\t    return Inverse(tm) * ntm;\t\n+\t}\n+\n+    void CompOffset( TimeValue t, Matrix3& offset, Matrix3& invoffset)\n+    {\t\n+        Interval valid;\n+        offset.IdentityMatrix();\n+        invoffset.IdentityMatrix();\n+\t    GetPosController()->GetValue(t,&offset,valid,CTRL_RELATIVE);\n+\t    invoffset = Inverse(offset);\n+    }\n+\n+    void DeleteModes()\n+    {\n+        Interface* ip = GetCOREInterface();\n+\n+\t    ip->DeleteMode(moveMode);\n+\t    ip->DeleteMode(rotMode);\n+\t    ip->DeleteMode(uscaleMode);\n+\t    ip->DeleteMode(nuscaleMode);\n+\t    ip->DeleteMode(squashMode);\t\n+\n+\t    DeleteAndNullify(moveMode);\n+        DeleteAndNullify(rotMode);\n+\t    DeleteAndNullify(uscaleMode );\n+\t    DeleteAndNullify(nuscaleMode);\n+\t    DeleteAndNullify(squashMode);\n+    }\n+};\ndiff --git a/Shared/MtlBasePlugin.h b/Shared/MtlBasePlugin.h\nnew file mode 100644\nindex 00000000..9c296c59\n--- /dev/null\n+++ b/Shared/MtlBasePlugin.h\n@@ -0,0 +1,171 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+//=========================================================================\n+// Provides default empty implementations of the MtlBase functions\n+// This class might be the base class of a TexMap or a Mtl derived plug-in\n+\n+/**\n+ * A base-class for MtlBase derived plug-ins (e.g. TexMap or Mtl plug-ins). Provides \n+ * some default empty implementation of functions, and manages a validity \n+ * interval automatically. \n+ * \n+ * \\see         MtlBase\n+ */\n+template<typename Base_T>\n+class MtlBasePlugin\n+    : public AnimatablePlugin<Base_T>\n+{\n+private:\n+\n+    //===========================================================================\n+    // fields\n+\n+    Interval ivalid;\n+    \n+public:\n+\n+    //===========================================================================\n+    // Constructor / destructor\n+\n+    MtlBasePlugin(ClassDesc2* classDesc) \n+        : AnimatablePlugin(classDesc), ivalid()\n+    {\n+    }\n+\n+    /// \\name ReferenceTarget Overrides\n+    //@{\n+    /**\n+     * Copies the ivalid interval automatically. \n+     * \\note        Place important notes that you want emphasized here.\n+     * \\see         ReferenceTarget::BaseClone()\n+     * \n+     * \\param       from An argument of type ReferenceTarget *.\n+     * \\param       to An argument of type ReferenceTarget *.\n+     * \\param       remap An argument of type RemapDir &.\n+     */\n+    virtual void BaseClone(ReferenceTarget* from, ReferenceTarget* to, RemapDir& remap) \n+    {\n+        MtlBasePlugin* fromMtl = dynamic_cast<MtlBasePlugin*>(from);\n+        MtlBasePlugin* toMtl = dynamic_cast<MtlBasePlugin*>(to);\n+        if (fromMtl != NULL && toMtl != NULL)\n+            toMtl->ivalid = fromMtl->ivalid;\n+        AnimatablePlugin::BaseClone(from, to, remap);\n+    }\n+\n+    //@}\n+\n+    //===========================================================================\n+    // MtlBase overrides\n+\n+    /// \\name MtlBase Overrides\n+    //@{\n+    /**\n+     * Automatically creates a master parameter dialog.     \n+     * \n+     * \\see         Mtlbase::CreateParamDlg()\n+     * \n+     * \\param       hwMtlEdit An argument of type HWND.\n+     * \\param       imp An argument of type IMtlParams *.\n+     * \\return      Returns a value of type ParamDlg *.\n+     */\n+    virtual ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) \n+    {\n+        IAutoMParamDlg* masterDlg = GetClassDesc()->CreateParamDlgs(hwMtlEdit, imp, this);   \t\n+\t    return masterDlg;\n+    }  \n+\n+    /**\n+     * Computes the validity interval automatically based on all\n+     * references that are MtlBase and the parameter block.\n+     * \n+     * \\see         Mtlbase::Validity()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type Interval.\n+     */\n+    virtual Interval Validity(TimeValue t)\n+    {\n+        Interval r = FOREVER;\n+        \n+        // Compute the validity based on all references that are also MtlBase* \n+        // e.g. sub-materials and sub-texture maps\n+        for (int i=0; i < NumRefs(); ++i)\n+        {\n+            MtlBase* mtl = dynamic_cast<MtlBase*>(GetReference(i));            \n+            if (mtl != NULL && !IsWeakRef(i))\n+                r &= mtl->Validity(t);\n+        }\n+\n+        GetMainParamBlock()->GetValidity(t, r);\n+        return r;\n+    }   \n+\n+    /**\n+     * Updates the validity interval.\n+     * \n+     * \\see         MtlBase::Update()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       valid An argument of type Interval &.\n+     */\n+    virtual void Update(TimeValue t, Interval& valid) \n+    {\t\n+\t    if (!ivalid.InInterval(t)) \n+        {\n+\t\t    ivalid.SetInfinite();\n+\n+            GetMainParamBlock()->GetValidity(t, ivalid);\n+\n+            // Call update on all sub-texture maps and sub-materials \n+            for (int i=0; i < NumRefs(); ++i)\n+            {\n+                MtlBase* mtl = dynamic_cast<MtlBase*>(GetReference(i));            \n+                if (mtl != NULL && !IsWeakRef(i))\n+                    mtl->Update(t, ivalid);\n+            }\n+\t    }\n+\n+        valid &= ivalid;\n+    }\n+\n+    /**\n+     * Resets the validity interval, and calls reset on all references \n+     * which are derived from "MtlBase". \n+     * This will reset the UVGen and XYZGen because they would be \n+     * added as references. \n+     * \n+     * \\see         MtlBase::Reset()\n+     * \n+     */\n+    virtual void Reset()\n+    {\n+\t    ivalid.SetEmpty();\n+    \tResetParams();\n+    }\n+    //@}\n+\n+    /// \\name ReferenceManager Overrides\n+    //@{\n+    /**\n+     * Updates the validity interval.\n+     * \n+     * \\param       id An argument of type ParamID.\n+     */\n+    virtual void OnParamChanged(ParamID id)\n+    {\n+        ivalid.SetEmpty();\n+    }\n+    //@}\n+};\n+    \ndiff --git a/Shared/NodePicker.h b/Shared/NodePicker.h\nnew file mode 100644\nindex 00000000..46bdd94c\n--- /dev/null\n+++ b/Shared/NodePicker.h\n@@ -0,0 +1,78 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/**\n+ * Simplifies node picking by merging the responsibilities for both\n+ * PickNodeCallback and PickModeCallback, into a single virtual function \n+ * that must be implemented (OnObjectPicked).\n+ */\n+class SingleObjectNodePicker : \n+      public PickModeCallback,\n+      public PickNodeCallback \n+{\n+private: \n+\n+    INode* hit;\n+\n+public:     \n+\n+    SingleObjectNodePicker() \n+        : hit(NULL)\n+    { }\n+\n+    ~SingleObjectNodePicker()\n+    { }\n+\n+    BOOL HitTest(IObjParam *ip, HWND hWnd, ViewExp *vpt, IPoint2 m, int flags) { \n+        return ip->PickNode(hWnd, m, this) ? TRUE : FALSE;\n+    }\n+\n+    BOOL Pick(IObjParam *ip, ViewExp *vpt) {\n+        hit = vpt->GetClosestHit();\n+        OnObjectPicked(hit);\n+        return TRUE;\n+    }\n+\n+    void EnterMode(IObjParam *ip) {\n+        hit = NULL;\n+\t\tGetCOREInterface()->PushPrompt(_T("Select target"));\n+    }\n+\n+    void ExitMode(IObjParam *ip) {\n+\t\tGetCOREInterface()->PopPrompt();\n+    }\n+\n+    BOOL RightClick(IObjParam *ip,ViewExp *vpt)  {\n+        return TRUE;\n+    }\n+\n+    BOOL Filter(INode *node) {\n+        return node != NULL && node->GetObjectRef() != NULL;\n+    }\n+\n+    PickNodeCallback *GetFilter() {\n+        return this;\n+    }\n+\n+    BOOL AllowMultiSelect() {\n+        return FALSE;\n+    }\n+\n+    INode* GetHitNode() {\n+        return hit;\n+    }\n+\n+protected:\n+\n+    virtual void OnObjectPicked(INode* hit) = 0;\n+};\ndiff --git a/Shared/ObjectPlugin.h b/Shared/ObjectPlugin.h\nnew file mode 100644\nindex 00000000..bed2b5df\n--- /dev/null\n+++ b/Shared/ObjectPlugin.h\n@@ -0,0 +1,527 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include <object.h>\n+#include <triobj.h>\n+#include <patchobj.h>\n+\n+//=========================================================================\n+// Replaces SimpleObject2 and SimpleObject\n+//\n+// The assumption is that Base_T derives from "Object" class (directly or indirectly)\n+\n+template<typename Base_T>\n+class ObjectPlugin\n+    : public AnimatablePlugin<Base_T>\n+{\n+private:\n+\n+\tMesh mesh;\n+\tInterval ivalid;\n+    int extDispFlags;\n+\n+public:\n+\t\n+    // Constructor / Destructor \n+    ObjectPlugin(ClassDesc2* classDesc) \n+        : AnimatablePlugin(classDesc)\n+    { \n+        mesh.EnableEdgeList(1);\n+        ivalid.SetEmpty();\n+        extDispFlags = 0;\n+    }\n+\n+    ~ObjectPlugin()\n+    {\n+    }\n+\n+    /// \\name New ObjectPlugin Virtual Functions\n+    //@{\n+    /**\n+     * The implementation of this function should construct a mesh at a specific time. \n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       m An argument of type Mesh &.\n+     */\n+    virtual void BuildMesh(TimeValue t, Mesh& m) = 0;\n+\n+    /**\n+     * Override this function to return FALSE during any special degenerate cases when the \n+     * object should not be displayed (e.g. size == 0).\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type BOOL.\n+     */\n+    virtual BOOL OKtoDisplay(TimeValue t) \n+    {\n+        return TRUE;\n+    }\n+    //@}\n+\n+    /// \\name Utility Functions\n+    //@{\n+    /**\n+     * Triggers a rebuild of the mesh, if the time value falls outside of the validity interval.\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     */\n+    void UpdateMesh(TimeValue t)\n+    {\n+\t    if (!ivalid.InInterval(t) ) {\n+\t\t    BuildMesh(t, mesh);\n+            ivalid.SetInfinite();\n+\t\t}\n+    }\n+    /**\n+     * Returns true if in creation mode. \n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsCreating() \n+    {\n+        return TestAFlag(A_OBJ_CREATING) == TRUE;\n+    }\n+    /**\n+     * Returns the cached mesh. \n+     * \n+     * \\return      Returns a value of type Mesh &.\n+     */\n+    Mesh& GetMesh() \n+    {\n+        return mesh;\n+    }\n+    /**\n+     * Returns the validity reference.\n+     * \n+     * \\return      Returns a value of type Interval &.\n+     */\n+    Interval& GetValidityRef() \n+    {\n+        return ivalid;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_SELECTED flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsSelected() \n+    {\n+        return (extDispFlags & EXT_DISP_SELECTED) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_TARGET_SELECTED flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsTargetSelected()\n+    {\n+        return (extDispFlags & EXT_DISP_TARGET_SELECTED) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_TARGET_SELECTED flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsLookatSelected()\n+    {\n+        return (extDispFlags & EXT_DISP_LOOKAT_SELECTED) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_TARGET_SELECTED flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsOnlySelected()\n+    {\n+        return (extDispFlags & EXT_DISP_ONLY_SELECTED) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_DRAGGING flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsDragging()\n+    {\n+        return (extDispFlags & EXT_DISP_DRAGGING) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_ZOOM_EXT flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsZoomExtent()\n+    {\n+        return (extDispFlags & EXT_DISP_ZOOM_EXT) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_GROUP_EXT flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsGroupExtext()\n+    {\n+        return (extDispFlags & EXT_DISP_GROUP_EXT) != 0;\n+    }\n+    /**\n+     * Returns true if the EXT_DISP_ZOOMSEL_EXT flag is set.\n+     * \n+     * \\return      Returns a value of type bool.\n+     */\n+    bool IsZoomSelectedExtent()\n+    {\n+        return (extDispFlags & EXT_DISP_ZOOMSEL_EXT) != 0;\n+    }\n+    /**\n+     * Converts the object to a TriObject. \n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type TriObject *.\n+     */\n+    TriObject* ConvertToTriObject(TimeValue t) \n+    {\n+\t    TriObject* triob = new TriObject();\n+\t    triob->GetMesh() = mesh;\n+\t    triob->SetChannelValidity(TOPO_CHAN_NUM, ObjectValidity(t));\n+\t    triob->SetChannelValidity(GEOM_CHAN_NUM, ObjectValidity(t));\t\t\n+\t    return triob;\n+    }\n+    /**\n+     * Converts the object to a PatchObject. \n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type PatchObject *.\n+     */\n+    PatchObject* ConvertToPatchObject(TimeValue t) \n+    {\n+\t    PatchObject *patchob = new PatchObject();\n+\t    patchob->patch = mesh;\t\t\n+\t    patchob->SetChannelValidity(TOPO_CHAN_NUM, ObjectValidity(t));\n+\t    patchob->SetChannelValidity(GEOM_CHAN_NUM, ObjectValidity(t));\t\t\n+        return patchob;\n+    }\n+    /**\n+     * Returns the selected node that is associated with this object. \n+     * \n+     * \\return      Returns a value of type INode *.\n+     */\n+    INode* GetSelectedSelfNode()\n+    {\n+        for (int i=0; i < GetCOREInterface()->GetSelNodeCount(); ++i) {\n+            INode* node = GetCOREInterface()->GetSelNode(i);\n+            if (node->GetObjectRef() == this)\n+                return node;\n+        }\n+        return NULL;\n+    }\n+    //@}\n+\n+    /// \\name Animatable Overrides\n+    //@{\n+    /**\n+     * \\see         Animatable::EndEditParams.\n+     * \n+     * \\param       ip An argument of type IObjParam *.\n+     * \\param       flags An argument of type ULONG.\n+     * \\param       next An argument of type Animatable *.\n+     */\n+    virtual void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) \n+    {\n+        if (IsCreating()) \n+            ClearAFlag(A_OBJ_CREATING);\n+        AnimatablePlugin::EndEditParams(ip, flags, next);\n+    }   \n+    /**\n+     * Clears all caches, and resets the validity interval.\n+     * \n+     * \\see         Animatable::FreeCaches()\n+     * \n+     */\n+    virtual void FreeCaches() \n+\t{\n+\t    ivalid.SetEmpty();\n+\t    mesh.FreeAll();\n+\t} \n+    //@}\n+\n+    /// \\name BaseObject Overrides\n+    //@{\n+    /**\n+     * \\see         BaseObject::GetDeformBBox()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       box An argument of type Box3 &.\n+     * \\param       tm An argument of type Matrix3 *.\n+     * \\param       useSel An argument of type BOOL.\n+     */\n+    virtual void GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel )\n+\t{\n+    \tUpdateMesh(t);\n+\t    box = mesh.getBoundingBox(tm);\n+\t}\n+    /**\n+     * \\see         BaseObject::GetLocalBoundBox()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\param       box An argument of type Box3 &.\n+     */\n+    virtual void GetLocalBoundBox(TimeValue t, INode *inode,ViewExp* vpt,  Box3& box ) \n+\t{\n+\t    UpdateMesh(t);\n+\t    box = mesh.getBoundingBox();\n+\t}\n+    /**\n+     * \\see         BaseObject::GetWorldBoundBox()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\param       box An argument of type Box3 &.\n+     */\n+    virtual void GetWorldBoundBox(TimeValue t, INode *inode, ViewExp* vpt, Box3& box )\n+\t{\n+\t    Matrix3 mat = inode->GetObjectTM(t);\n+\t    UpdateMesh(t);\n+\t    box = mesh.getBoundingBox();\n+\t    box = box * mat;\n+\t}\n+    /**\n+     * \\see         BaseObject::HitTest()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       type An argument of type int.\n+     * \\param       crossing An argument of type int.\n+     * \\param       flags An argument of type int.\n+     * \\param       p An argument of type IPoint2 *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int HitTest(TimeValue t, INode *inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) \n+\t{\n+\t    Point2 pt( (float)p[0].x, (float)p[0].y );\n+\t    HitRegion hitRegion;\n+\t    GraphicsWindow *gw = vpt->getGW();\t\n+\t    UpdateMesh(t);\n+\t    gw->setTransform(inode->GetObjectTM(t));\n+\t    MakeHitRegion(hitRegion, type, crossing, 4, p);\n+\t    return mesh.select(gw, inode->Mtls(), &hitRegion, flags & HIT_ABORTONHIT, inode->NumMtls());\n+\t}\n+    /**\n+     * \\see         BaseObject::Snap()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       snap An argument of type SnapInfo *.\n+     * \\param       p An argument of type IPoint2 *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     */\n+    virtual void Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt) \n+\t{\n+        // No snap to ourself while creating!\n+\t    if (IsCreating())\t\n+\t\t    return;\n+\t    Matrix3 tm = inode->GetObjectTM(t);\n+\t    GraphicsWindow *gw = vpt->getGW();\n+\t    UpdateMesh(t);\n+\t    gw->setTransform(tm);\n+\t    mesh.snap(gw, snap, p, tm);\n+\t}\n+    /**\n+     * \\see         BaseObject::Display()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       vpt An argument of type ViewExp *.\n+     * \\param       flags An argument of type int.\n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags) \n+\t{\n+    \tif (!OKtoDisplay(t)) \n+            return 0;\n+\t    GraphicsWindow *gw = vpt->getGW();\n+\t    Matrix3 mat = inode->GetObjectTM(t);\t \n+\t    UpdateMesh(t);\t\t\n+\t    gw->setTransform(mat);\n+        if (flags & USE_DAMAGE_RECT)\n+        {\n+            RECT r = vpt->GetDammageRect();\n+\t        mesh.render(gw, inode->Mtls(), &r, COMP_ALL, inode->NumMtls());\n+        }\n+        else\n+        {\n+\t        mesh.render(gw, inode->Mtls(), NULL, COMP_ALL, inode->NumMtls());\n+        }\n+\t    return 0;\n+    }\n+    /**\n+     * \\see         BaseObject::GetCreateMouseCallBack()\n+     * \n+     * \\return      Returns a value of type CreateMouseCallBack *.\n+     */\n+    virtual CreateMouseCallBack* GetCreateMouseCallBack()\n+    {\n+        return NULL;\n+    }\n+    //@}\n+\n+    /// \\name Object Overrides\n+    //@{\n+    /**\n+     * \\see         Object::Eval()\n+     * \n+     * \\param       time An argument of type TimeValue.\n+     * \\return      Returns a value of type ObjectState.\n+     */\n+    virtual ObjectState Eval(TimeValue time) \n+\t{\n+        UpdateMesh(time);\n+\t    return ObjectState(this);\n+\t}\n+    /**\n+     * \\see         Object::SetExtendedDisplay()\n+     * \n+     * \\param       flags An argument of type int.\n+     */\n+    virtual void SetExtendedDisplay(int flags)\n+    {\n+\t    extDispFlags = flags;\n+    }\n+    /**\n+     * \\see         Object::ObjectValidity()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\return      Returns a value of type Interval.\n+     */\n+    virtual Interval ObjectValidity(TimeValue t) \n+\t{        \n+        return ivalid;\n+\t}\n+    /**    \n+     * \\see         Object::IntersectRay()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       ray An argument of type Ray &.\n+     * \\param       at An argument of type float &.\n+     * \\param       norm An argument of type Point3 &.\n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int IntersectRay(TimeValue t, Ray& ray, float& at, Point3& norm)\n+\t{\t\n+\t    UpdateMesh(t);\n+\t    return mesh.IntersectRay(ray,at,norm);\t\n+\t}\n+    /**    \n+     * \\see         Object::CanConvertToType()\n+     * \n+     * \\param       obtype An argument of type Class_ID.\n+     * \\return      Returns a value of type int.\n+     */\n+    virtual int CanConvertToType(Class_ID obtype) \n+\t{\n+\t    if (obtype==defObjectClassID) return TRUE;\n+\t    if (obtype==mapObjectClassID) return TRUE;\n+\t    if (obtype==triObjectClassID) return TRUE;\n+\t    if (obtype==patchObjectClassID) return TRUE;\n+\t    if (Object::CanConvertToType(obtype)) return TRUE;\n+\t    if (CanConvertTriObject(obtype)) return TRUE;\n+\t    return FALSE;\n+    }\n+    /**\n+     * \\see         Object::ConvertToType()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       obtype An argument of type Class_ID.\n+     * \\return      Returns a value of type Object *.\n+     */\n+    virtual Object* ConvertToType(TimeValue t, Class_ID obtype) \n+\t{\n+        if (CanConvertToType(obtype))\n+            return NULL;\n+\n+        UpdateMesh(t);\n+\n+\t    if (obtype==defObjectClassID || obtype==triObjectClassID || obtype==mapObjectClassID) {\n+            return ConvertToTriObject(t);\n+\t    }\n+\t    else if (obtype == patchObjectClassID) {\n+\t\t    return ConvertToPatchObject(t);\n+\t    }\n+\t    else if (Object::CanConvertToType (obtype)) {\n+\t\t    return Object::ConvertToType(t,obtype);\n+\t    }\n+\t    else if (CanConvertTriObject(obtype)) {\n+\t\t    TriObject *triob = ConvertToTriObject(t);\n+\t\t    Object *ob = triob->ConvertToType (t, obtype);\t\t    \n+            // ob should never = tob but just to be safe, we check before \n+            // deleting.\n+            if (ob != triob) \n+                triob->DeleteThis();\t\n+\t\t    return ob;\n+\t    }\n+\t    return NULL;\n+    }\n+    //@}\n+\n+    /// \\name GeomObject Overrides\n+    //@{\n+    /**\n+     * \\see         GeomObject::GetRenderMesh()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       inode An argument of type INode *.\n+     * \\param       view An argument of type View &.\n+     * \\param       needDelete An argument of type BOOL &.\n+     * \\return      Returns a value of type Mesh *.\n+     */\n+    virtual Mesh* GetRenderMesh(TimeValue t, INode *inode, View &view, BOOL& needDelete) \n+    {\n+\t    UpdateMesh(t);\n+\t    needDelete = FALSE;\n+\t    return &mesh;\n+\t}\n+    /**\n+     * \\see         GeomObject::PolygonCount()\n+     * \n+     * \\param       t An argument of type TimeValue.\n+     * \\param       numFaces An argument of type int &.\n+     * \\param       numVerts An argument of type int &.\n+     * \\return      Returns a value of type BOOL.\n+     */\n+    virtual BOOL PolygonCount(TimeValue t, int& numFaces, int& numVerts) \n+    {\n+        UpdateMesh(t);\n+        numFaces = mesh.getNumFaces();\n+        numVerts = mesh.getNumVerts();\n+        return TRUE;\n+    }\n+    //@}\n+\n+    \n+    /// \\name AnimatablePlugin Overrides\n+    //@{\n+    /**\n+     * Resets the validity interval whenever a parameter changes. \n+     * \n+     * \\param       id An argument of type ParamID.\n+     */\n+    virtual void OnParamChangeMsg(ParamID id) \n+    {\n+        ivalid.SetEmpty();\n+    }\n+    //@}\n+};\n+\n+\ndiff --git a/Shared/ParamBlockUtility.h b/Shared/ParamBlockUtility.h\nnew file mode 100644\nindex 00000000..6719e09d\n--- /dev/null\n+++ b/Shared/ParamBlockUtility.h\n@@ -0,0 +1,228 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/**\n+ * This class is for easier and safer descriptions of parameter blocks. \n+ * ParamBlockDescUtil is based on ParamBlockDesc2, and is intended to be used created incrementally \n+ * using a series of function calls. This is different from the traditional approach of passing \n+ * a massive variable length list of arguments to the constructor of ParamBlockDesc2. \n+ * The advantages are additional type safety, and explicit support from the IDE (e.g. code completion).\n+ * \n+ * \\par This class assumes the parameter block is auto-constructed by 3ds Max (meaning it \n+ * assigns the parameter block as a reference to your plug-in) and has an auto-constructed UI. \n+ * You would use it normally it by deriving a class which has an empty constructors \n+ * and calls the incremental construction functions from its own constructor\n+ * Once constructed, the parameter block descriptor must never be modified.\n+ *\n+ * \\note: Pass the flag BEGIN_EDIT_MOTION to the constructor \\c flags argument \n+ * if using this for a controller.\n+ */\n+class ParamBlockDescUtil\n+    : protected ParamBlockDesc2 \n+{\n+private:\n+\n+    BOOL finished;\n+\n+public:\n+\t///* From the header:\n+\tParamBlockDescUtil(  ClassDesc2* classDesc  ,\n+\t\tMCHAR* internalName, \n+\t\tStringResID localNameID, \n+\t\tBlockID id, \n+\t\tint flagMask       )        : ParamBlockDesc2(\n+\t\tid, \n+\t\tinternalName, \n+\t\tlocalNameID, \n+\t\tclassDesc, \n+\t\tP_AUTO_CONSTRUCT + P_AUTO_UI,\n+\t\t// [<auto_construct_block_refno>]\n+\t\t// [<auto_ui_parammap_specs>]\n+\t\tflagMask,\n+\t\t0, // could be APPENDROLL_CLOSED if the roll-up should be closed by default\n+\t\tdlgProc,\n+\t\tend),\n+\t\tfinished(FALSE){};  \n+\t//*/\n+    ParamBlockDescUtil\n+        (\n+\t\t\tBlockID     id              ,\n+            MCHAR* internalName, \n+            StringResID localNameID, \n+            ClassDesc2* classDesc       , \n+            int         flagMask        , // Set to BEGIN_EDIT_MOTION for controllers.\n+            // ---------------------\n+\t\t\tint         refNumber       ,\n+            ResID       dialogTemplateID, \n+            StringResID panelTitleID, \n+            ParamMap2UserDlgProc* dlgProc = NULL\n+        )\n+        : ParamBlockDesc2(\n+            id, \n+            internalName, \n+            localNameID, \n+            classDesc, \n+            P_AUTO_CONSTRUCT + P_AUTO_UI,\n+                // [<auto_construct_block_refno>]\n+                refNumber,\n+                // [<auto_ui_parammap_specs>]\n+                dialogTemplateID,\n+                panelTitleID,\n+                flagMask,\n+                0, // could be APPENDROLL_CLOSED if the roll-up should be closed by default\n+                dlgProc,\n+            end),\n+        finished(FALSE)\n+    {\n+    }\n+\n+    /**\n+     * After the last parameter is added, call this function. \n+     * This is used to assure internally that no one tries to add additional parameters.\n+     */\n+    void SetFinished()\n+    {\n+        DbgAssert(!finished);\n+        finished = TRUE;\n+    }\n+\n+    /**\n+     * Adds an angle parameter with an associated spinner controller.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     * \\param  min An argument of type float.\n+     * \\param  max An argument of type float.\n+     * \\param  def An argument of type float.\n+     * \\param  editID An argument of type ResID.\n+     * \\param  spinnerID An argument of type ResID.\n+     * \\param  incAmount An argument of type float.\n+     */\n+    void AddAngleSpinnerParam(ParamID id, MCHAR* internalName, StringResID localNameID, float min, float max, float def, ResID editID, ResID spinnerID, float incAmount = SPIN_AUTOSCALE)\n+    {\n+        DbgAssert(!finished);\n+        AddAngleParam(id, internalName, localNameID);\n+        SetRangeAndDefault(id, min, max, def);\n+        AddEditAndSpinner(id, editID, spinnerID, incAmount);\n+    }\n+\n+    \n+    /**\n+     * Adds a float parameter with an associated spinner controller.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     * \\param  min An argument of type float.\n+     * \\param  max An argument of type float.\n+     * \\param  def An argument of type float.\n+     * \\param  editID An argument of type ResID.\n+     * \\param  spinnerID An argument of type ResID.\n+     * \\param  incAmount An argument of type float.\n+     */\n+    void AddFloatSpinnerParam(ParamID id, MCHAR* internalName, StringResID localNameID, float min, float max, float def, ResID editID, ResID spinnerID, float incAmount = SPIN_AUTOSCALE)\n+    {\n+        DbgAssert(!finished);\n+        AddFloatParam(id, internalName, localNameID);\n+        SetRangeAndDefault(id, min, max, def);\n+        AddEditAndSpinner(id, editID, spinnerID, incAmount);\n+    }\n+        \n+    /**\n+     * Adds a node parameter, with a node pick button. \n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     * \\param  buttonID An argument of type ResID.\n+     */\n+    void AddNodePickParam(ParamID id, MCHAR* internalName, StringResID localNameID, ResID buttonID)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_INODE, 0, localNameID, end);\n+        ParamOption(id, p_ui, TYPE_PICKNODEBUTTON, buttonID, end);\n+    }\n+\n+    /**\n+     * Sets the range and default values of the specified float parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  min An argument of type float.\n+     * \\param  max An argument of type float.\n+     * \\param  def An argument of type float.\n+     */\n+    void SetRangeAndDefault(ParamID id, float min, float max, float def)\n+    {\n+        DbgAssert(!finished);\n+        DbgAssert(min <= max);\n+        DbgAssert(def >= min);\n+        DbgAssert(def <= max);\n+        ParamOption(id, p_range, min, max, end);\n+        ParamOption(id, p_default, def, end);\n+    }\n+\n+    /**\n+     * Adds an angle parameter. \n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddAngleParam(ParamID id, MCHAR* internalName, StringResID localNameID)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_ANGLE, P_ANIMATABLE, localNameID, end);        \n+    }\n+\n+    /**\n+     * Adds a float parameter.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  internalName An argument of type char *.\n+     * \\param  localNameID An argument of type StringResID.\n+     */\n+    void AddFloatParam(ParamID id, MCHAR* internalName, StringResID localNameID)\n+    {\n+        DbgAssert(!finished);\n+        AddParam(id, internalName, TYPE_FLOAT, P_ANIMATABLE, localNameID, end);        \n+    }\n+\n+    /**\n+     * Adds an edit control and and a spinner control to the specified parameter. \n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  editID An argument of type ResID.\n+     * \\param  spinnerID An argument of type ResID.\n+     * \\param  incAmount An argument of type float.\n+     */\n+    void AddEditAndSpinner(ParamID id, ResID editID, ResID spinnerID, float incAmount = SPIN_AUTOSCALE)\n+    {\n+        DbgAssert(!finished);\n+        ParamOption(id, p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, editID, spinnerID, incAmount);\n+    }\n+\n+    \n+    /**\n+     * Associates a button with a parameter, which become a pick-node button.\n+     * This means when the user clicks on it, 3ds Max goes into node picking mode.\n+     * \n+     * \\param  id An argument of type ParamID.\n+     * \\param  buttonID An argument of type ResID.\n+     */\n+    void AddPickNodeButton(ParamID id, ResID buttonID)\n+    {\n+        DbgAssert(!finished);\n+        ParamOption(id, p_ui, TYPE_PICKNODEBUTTON, buttonID, end);\n+    }\n+};\ndiff --git a/Shared/ReferenceManager.h b/Shared/ReferenceManager.h\nnew file mode 100644\nindex 00000000..95ba33a8\n--- /dev/null\n+++ b/Shared/ReferenceManager.h\n@@ -0,0 +1,313 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/**\n+ * Instantiations of this class template can be used as the base-class \n+ * for plug-ins that derive from ReferenceMaker but that \n+ * manage arbitrary but fixed number of reference targets. The plug-in type is passed \n+ * as the template parameter, and a template instantiation will derive from it.\n+ * \n+ * \\see        ReferenceMaker\n+ */\n+template<typename Base_T>\n+class ReferenceManager \n+    : public Base_T\n+{\n+private:\n+\n+    /**\n+     * Stores information about each reference managed by ReferenceManager.\n+     */\n+    struct RefInfo\n+    {\n+        ReferenceTarget* m_target;\n+        BOOL m_isWeak;\n+        BOOL m_isPersisted;\n+\n+        RefInfo(ReferenceTarget* target, BOOL isWeak, BOOL isPersisted)\n+            : m_target(target), m_isWeak(isWeak), m_isPersisted(isPersisted)\n+        {\n+        }\n+    };\n+\n+    /** \n+     * The array of reference targets. \n+     * \\remarks The template class "RefMgr" is not \n+     * used because it may be deprecated in versions of the 3ds Max SDK after 2010.\n+     * The class IRefTargContainer is not used because it does not support node targets,\n+     * and was intended for use with dynamically changing lists of references.\n+     */\n+    MaxSDK::Array<RefInfo> m_refs;\n+\n+public:\n+\n+    ReferenceManager()\n+        : Base_T()\n+    { }\n+    \n+    ~ReferenceManager() \n+    {\n+        // Informs 3ds Max that it is safe to delete the all references from and to this object\n+        // This will delete the reference connections but will not directly delete memory to objects.\n+        // Once any objects have no more strong references, they will be deleted. \n+        // This should not be strictly necessary if all plug-ins and components behave \n+        // as they should, but in this case being paranoid has little cost. \n+        DeleteAllRefs();\n+    }\n+\n+    //============================================================\n+    // ReferenceMaker / ReferenceTarget overloads\n+    \n+    /// \\name ReferenceMaker Overrides\n+    //@{\n+    /**\n+     * Returns the number of references.\n+     * \\return     Returns a value of type int.\n+     */\n+    virtual int NumRefs() \n+    {\n+        return (int)m_refs.length();\n+    }\n+\n+    /**\n+     * Returns the nth reference.\n+     *\n+     * \\see        ReferenceMaker::GetReference()\n+     * \n+     * \\param  i An argument of type int.\n+     * \\return     Returns a value of type RefTargetHandle.\n+     */\n+    virtual RefTargetHandle GetReference(int i) \n+    {\n+        DbgAssert(IsValidReferenceIndex(i));\n+        if (!IsValidReferenceIndex(i))\n+            return NULL;\n+        return m_refs[i].m_target;\n+    }\n+\n+\t/**\n+\t * Set the nth reference. \n+     * \\note       Do not call this function directly, instead use ReplaceReference. \n+\t * \\see        ReferenceMaker::SetReference()\n+\t * \n+\t * \\param  i An argument of type int.\n+\t * \\param  rtarg An argument of type RefTargetHandle.\n+\t */\n+\tvirtual void SetReference(int i, RefTargetHandle rtarg) \n+    { \n+        DbgAssert(IsValidReferenceIndex(i));\n+        if (!IsValidReferenceIndex(i))\n+            return;\n+        m_refs[i].m_target = rtarg;\n+    }\n+\n+    /**\n+     * Called by 3ds Max in response to a change in an observed ReferenceTarget. \n+     * \n+     * \\see        ReferenceMaker::NotifyRefChanged()\n+     * \n+     * \\param  changeInt An argument of type Interval.\n+     * \\param  hTarget An argument of type RefTargetHandle.\n+     * \\param  partID An argument of type PartID &.\n+     * \\param  message An argument of type RefMessage.\n+     * \\return     Returns a value of type RefResult.\n+     */\n+    virtual RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ) \n+   \t{\n+\t    switch (message) \n+        {\n+        case REFMSG_TARGET_DELETED:            \n+            if (hTarget != NULL)\n+            {\n+                int n = GetReferenceIndex(hTarget);\n+                DbgAssert(n >= 0 && "Internal error, reference could not be found");\n+                if (n >= 0)\n+                    m_refs[n].m_target = NULL;\n+        \t    return REF_SUCCEED;\n+            }\n+        }\n+        return REF_SUCCEED;\n+    }\n+\n+    /**\n+     * Indicate whether the nth reference is a strong reference or not. \n+     * \\see        ReferenceMaker::IsRealDependency()\n+     * \n+     * \\param  rtarg An argument of type ReferenceTarget *.\n+     * \\return     TRUE if a strong reference, FALSE if a weak reference.\n+     */\n+    virtual BOOL IsRealDependency(ReferenceTarget* rtarg)\n+    {\n+        int n = GetReferenceIndex(rtarg);\n+        if (n < 0)\n+            return FALSE;\n+        return !m_refs[n].m_isWeak;\n+    }\n+\n+    /**\n+     * Called by 3ds Max for weak references, to see if a plug-in wishes for the \n+     * reference links to be saved to file. \n+     * \\see        ReferenceMaker::ShouldPersistWeakRef\n+     * \n+     * \\param  rtarg An argument of type ReferenceTarget *.\n+     * \\return     Returns a value of type BOOL.\n+     */\n+    virtual BOOL ShouldPersistWeakRef(ReferenceTarget* rtarg)\n+    {\n+        int n = GetReferenceIndex(rtarg);\n+        if (n < 0)\n+            return TRUE;\n+        return m_refs[n].m_isPersisted;\n+    }\n+    //@}\n+\n+    /// \\name ReferenceTarget Overrides\n+    //@{\n+    /**\n+     * This default implementation of ReferenceTarget::BaseClone() will clone all objects that have strong references \n+     *\n+     * \\see        ReferenceTarget::BaseClone()\n+     * \n+     * \\param  from An argument of type ReferenceTarget *.\n+     * \\param  to An argument of type ReferenceTarget *.\n+     * \\param  remap An argument of type RemapDir &.\n+     */\n+    virtual void BaseClone(ReferenceTarget* from, ReferenceTarget* to, RemapDir& remap) \n+    {\n+        if (!from || !to || from == to)\n+            return;\n+\n+        Base_T::BaseClone(from, to, remap);\n+\n+        int numRefs = NumRefs();\n+        for (int i=0; i < numRefs; ++i)\n+        {\n+            ReferenceTarget* fromTarget = from->GetReference(i);\n+\n+            if (from->IsRealDependency(fromTarget))\n+                to->ReplaceReference(i, remap.CloneRef(fromTarget));\n+            else\n+            {\n+                // We do not clone weak references, just copy them\n+                // however, the weakly referenced item may have been cloned\n+                // so we have to look for it.\n+                RefTargetHandle newTarget = remap.FindMapping( fromTarget );\n+                if (newTarget)\n+                    fromTarget = newTarget;\n+                to->ReplaceReference(i, fromTarget);       \n+            }\n+        }\n+    }\n+\n+    //@}\n+    //========================================================================\n+    // New reference related methods\n+        \n+    /// \\name ReferenceManager Specific Methods\n+    //@{\n+    /**\n+     * Registers a reference with the reference manager as the specified index. \n+     * The n parameter is not strictly necessary, because it should always be equal to the number \n+     * of references. This is the contract of using this function. By requiring it though, we \n+     * help plug-in users be explicit about what the indexes of their references are.\n+     *\n+     * \\note Do not call RegisterReference after construction. All fully constructed \n+     * instances of a plug-in must have the same number of references if they want to \n+     * use ReferenceManager. \n+     * \n+     * \\param  n An argument of type int.\n+     * \\param  ref An argument of type ReferenceTarget *.\n+     * \\param  isWeak An argument of type BOOL.\n+     * \\param  isPersisted An argument of type BOOL.\n+     * \\return     Returns a value of type RefResult.\n+     */\n+    RefResult RegisterReference(int n, ReferenceTarget* ref, BOOL isWeak = FALSE, BOOL isPersisted = TRUE) \n+    {\n+\t\t/*\n+        DbgAssert(n == NumRefs() && "Can only register a reference in the last available place");\n+\t\t*/\n+\t\twhile (n >= NumRefs())\n+\t\t{\n+\t\t\tm_refs.append(RefInfo(NULL, false, false));\n+\t\t}\n+\n+        // Add a new RefInfo structure to the array.\n+        m_refs[n] = RefInfo(NULL, isWeak, isPersisted);\n+        \n+        DbgAssert(isWeak || isPersisted == TRUE && "Strong references are always persisted automatically");\n+\n+        // We have to call ReplaceReference to set the reference so that 3ds Max can \n+        // track the reference correctly. This will also check that result is not a circular reference\n+        RefResult result = ReplaceReference(n, ref);\n+        \n+        return result;           \n+    }    \n+\n+    /**\n+     * Casts the nth reference to a specific type.\n+     * \n+     * \\tparam     T the type to cast the nth reference to.\n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type T.\n+     */\n+    template<typename T>\n+    T GetReferenceAs(int n) \n+    {\n+\t\tRefTargetHandle ref = GetReference(n);\n+\t\treturn dynamic_cast<T>(ref);\n+    }\n+\n+    /**\n+     * Indicates whether \\c n is a valid reference index.\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     \\c true if \\c n is a valid reference index, \\c false otherwise.\n+     */\n+    bool IsValidReferenceIndex(int n)\n+    {\n+        return n >= 0 && n < NumRefs();\n+    }\n+\n+    /**\n+     * Given a ReferenceTarget, returns its reference index.\n+     * In the case of multiple references to the same item,  \n+     * the lowest index is given.      \n+     * Returns -1 if the ReferenceTarget is not observed.\n+     * \n+     * \\param  ref An argument of type ReferenceTarget *.\n+     * \\return     Returns a value of type int.\n+     */\n+    int GetReferenceIndex(ReferenceTarget* ref) \n+    {\n+        for (int i=0; i < NumRefs(); ++i)\n+            if (GetReference(i) == ref)\n+                return i;\n+        return -1;\n+    }\n+    \n+    /**\n+     * Returns TRUE if the nth index is a weak reference,\n+     * or FALSE otherwise.\n+     * \n+     * \\param  n An int indicating the reference index.\n+     * \\return     Returns a value of type BOOL.\n+     */\n+    BOOL IsWeakRef(int n)\n+    {\n+        DbgAssert(IsValidReferenceIndex(n));\n+        return m_refs[n].m_isWeak;\n+    }\n+    //@}\n+};\n+\n+\ndiff --git a/Shared/SubAnimManager.h b/Shared/SubAnimManager.h\nnew file mode 100644\nindex 00000000..5993f3de\n--- /dev/null\n+++ b/Shared/SubAnimManager.h\n@@ -0,0 +1,299 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "ReferenceManager.h"\n+\n+//=========================================================\n+// The template parameter Base_T is used as the Base class\n+// \n+// The SubAnimManager manages sub-anims. The sub-anims are all assumed\n+// to be strong references registered with the ReferenceManager. \n+// The number of sub-anims is assumed to be \n+// fixed at construction. In order to manage the sub-animatables automatically \n+// they must be register using RegisterSubAnim after they have been registered\n+// as Reference using RegisterReference.\n+//\n+// The main advantage of this class, is that it provides default implementations\n+// of many of the sub-animatable related functions from the Animatable base class.\n+// It also provides a large amount of contract checking.\n+\n+/**\n+ * Implements the sub-animatable related functions for a plug-in. The sub-animatables are \n+ * assumed to already be registered with the ReferenceManager as strong references. \n+ * Sub-animatables must first be registered by calling ReferenceManager::RegisterReference()\n+ * then they are registered by called SubAnimmanager::RegisterSubAnim()\n+ * \n+ * \\see        Animatable\n+ */\n+template<typename Base_T>\n+class SubAnimManager : \n+    public ReferenceManager<Base_T>\n+{\n+private:\n+\n+    /// Used to store information about each sub-animatable\n+    struct SubAnimInfo\n+    {\n+        SubAnimInfo(int refIndex, \n+            MCHAR* name, BOOL canDelete, BOOL canCopy, BOOL canAssign, BOOL canMakeUnique\n+            ) : \n+                m_refIndex(refIndex), m_name(name), m_canDelete(canDelete)\n+                ,m_canCopy(canCopy), m_canAssign(canAssign), m_canMakeUnique(canMakeUnique)\n+        { }\n+\n+        int m_refIndex;\n+        MCHAR* m_name;\n+        BOOL m_canDelete;\n+        BOOL m_canCopy;\n+        BOOL m_canAssign;\n+        BOOL m_canMakeUnique;\n+    };\n+    \n+    /// The internal array of structures describing each sub-animatables.\n+    MaxSDK::Array<SubAnimInfo> m_subAnims;\n+    \n+    /**\n+     * Used to retrieve information about a sub-animatable.\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type SubAnimManager<Base_T>::SubAnimInfo &.\n+     * \\see        Related API elements\n+     */\n+    SubAnimInfo& GetSubAnimInfo(int n) \n+    {\n+        DbgAssert(IsValidSubAnimIndex(n));\n+        return m_subAnims[n];\n+    }\n+\n+public:\n+\n+    /// \\name Animatable Overrides\n+    //@{\n+    /**\n+     * Returns the number of sub-animatables. \n+     * \n+     * \\return     Returns a value of type int.\n+     * \\see        Animatables::NumSubs()\n+     */\n+    virtual int NumSubs() \n+    { \n+        return (int)m_subAnims.length();\n+    }  \n+    \n+\t/**\n+\t * Returns nth sub-animatable.\n+\t * \n+\t * \\param  n An argument of type int.\n+\t * \\return     Returns a value of type Animatable *.\n+\t * \\see        Animatable::SubAnim()\n+\t */\n+\tvirtual Animatable* SubAnim(int n) \n+    { \n+        DbgAssert(IsValidSubAnimIndex(n));\n+        if (!IsValidSubAnimIndex(n))\n+            return NULL;\n+        int refNum = SubAnimToRefNum(n);\n+        return GetReferenceAs<Animatable*>(refNum);\n+    }\n+    \n+    /**\n+     * Returns the name of the nth sub-animatable.\n+     * \n+     * \\param  i An argument of type int.\n+     * \\return     Returns a value of type MSTR.\n+     * \\see        Animatable::SubAnimName()\n+     */\n+    virtual MSTR SubAnimName(int i) \n+    {\n+        return GetSubAnimInfo(i).m_name;\n+    }\n+    \n+    /**\n+     * Returns TRUE if the sub-animatable can be copied and pasted in the track view\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type BOOL.\n+     * \\see        Animatable::CanCopyAnim()\n+     */\n+    virtual BOOL CanCopyAnim(int n)\n+    {\n+        return GetSubAnimInfo(n).m_canCopy;\n+    }\n+    \n+    /**\n+     * Returns FALSE if the sub-animatable prohibits being made unique from the UI\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type BOOL.\n+     * \\see        Animatable::CanMakeUnique()\n+     */\n+    virtual BOOL CanMakeUnique(int n)\n+    {\n+        return GetSubAnimInfo(n).m_canMakeUnique;\n+    }\n+   \n+    /**\n+     * Returns true if the specified sub-animatable can be reassigned. \n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type BOOL.\n+     * \\see        Animatable::CanAssignController()\n+     */\n+    virtual BOOL CanAssignController(int n)\n+    {\n+        return GetSubAnimInfo(n).m_canAssign;\n+    }\n+    \n+    /**\n+     * Called by 3ds Max when the controller is reassigned.\n+     * \n+     * \\param  control An argument of type Animatable *.\n+     * \\param  subAnim An argument of type int.\n+     * \\return     Returns a value of type BOOL.\n+     * \\see        Animatable::AssignController()\n+     */\n+    virtual BOOL AssignController(Animatable* control, int subAnim)\n+    {\n+        DbgAssert(CanAssignController(subAnim) && "Cannot assign the specified sub-anim");\n+\n+        // Convert the control into a reference target\n+        ReferenceTarget* ref = dynamic_cast<ReferenceTarget*>(control);\n+\n+        // Because the sub-animatable is a reference we ask 3ds Max to replace the reference\n+        ReplaceReference(SubAnimToRefNum(subAnim), ref);\n+\n+        // Finally we notify the dependents of the change to a controller\n+        NotifyDependents(FOREVER, 0, REFMSG_CONTROLREF_CHANGE);\n+        return TRUE;\n+    }\n+    \n+    /**\n+     * Returns TRUE if the sub-animatable can be deleted by the UI or programmatically \n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type BOOL.\n+     * \\see        Animatable::CanDeleteSubAnim()\n+     */\n+    virtual BOOL CanDeleteSubAnim(int n)\n+    {\n+        return GetSubAnimInfo(n).m_canDelete;\n+    }\n+\n+    /**\n+     * Delete the nth sub-animatable.\n+     * \n+     * \\param  n An argument of type int.\n+     * \\see        Animatable::DeleteSubAnim()\n+     */\n+    virtual void DeleteSubAnim(int n)\n+    {\n+        DbgAssert(CanDeleteSubAnim(n) && "Cannot delete the specified sub-anim");\n+\n+        // Because the sub-animatable is a reference we ask 3ds Max to delete the reference\n+        DeleteReference(SubAnimToRefNum(n));\n+    }\n+\n+    /**\n+     * Used for copying and pasting in the track view. It converts an anim index to a reference index or returns \n+     * -1 if there is no correspondence. If a client does not wish an anim to be copied or pasted then it can \n+     * return -1 even if there is a corresponding reference number.\n+     * \n+     * \\param  subNum An argument of type int.\n+     * \\return     Returns a value of type int.\n+     * \\see        Animatable::SubNumToRefNum()\n+     */\n+    virtual int SubNumToRefNum(int subNum)\n+    {\n+        if (!CanCopyAnim(subNum)) return -1;\n+        return SubAnimToRefNum(subNum);\n+    }\n+    //@}\n+\n+    /// \\name Functions introduced by SubAnimManager\n+    //@{\n+    /**\n+     * Associates a sub-anim index with a reference index. \n+     * Only call this after registering all references\n+     * \n+     * \\param  nSubAnimIndex An argument of type int.\n+     * \\param  nRefIndex An argument of type int, the index of the reference which is used as a sub-animatable..\n+     * \\param  name An argument of type char *.\n+     * \\param  canDelete An argument of type BOOL.\n+     * \\param  canCopy An argument of type BOOL.\n+     * \\param  canAssign An argument of type BOOL.\n+     * \\param  canMakeUnique An argument of type BOOL.\n+     */\n+    void RegisterSubAnim(int nSubAnimIndex, int nRefIndex, MCHAR* name, BOOL canDelete = FALSE, BOOL canCopy = TRUE, BOOL canAssign = TRUE, BOOL canMakeUnique = TRUE)\n+    {\n+        // Make sure that the reference index is valid.\n+        DbgAssert(IsValidReferenceIndex(nRefIndex));\n+\n+        // Technically we could guess what nSubAnimIndex is, but this way avoids confusion\n+        DbgAssert(nSubAnimIndex == NumSubs() && "Can only add sub-anims to the last place");\n+        \n+        // Make sure that we are not mapping to an already used place\n+#ifdef _DEBUG\n+        for (int i=0; i < NumSubs(); ++i) \n+            DbgAssert(m_subAnims[i].m_refIndex != nRefIndex && "The reference index is already used");\n+#endif\n+\n+        // Create a new subAnimInfo structure and copy it into the list\n+        SubAnimInfo subAnim(nRefIndex, name, canDelete, canCopy, canAssign, canMakeUnique);        \n+        m_subAnims.append(subAnim);\n+    }\n+    \n+    /**\n+     * Will return true if the number is not a valid sub-animatable index\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type BOOL.\n+     * \\see        Related API elements\n+     */\n+    BOOL IsValidSubAnimIndex(int n)\n+    {\n+        return n >= 0 && n < NumSubs();\n+    }\n+\n+    /**\n+     * Converts a sub-animatable index into a reference index. Unlike the override of \n+     * Animatable::SubNumToRefNum() it does not return -1 if copying and pasting is \n+     * disallowed.\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type int.\n+     * \\see        SubNumToRefNum()\n+     */\n+    int SubAnimToRefNum(int n)\n+    {\n+        return GetSubAnimInfo(n).m_refIndex;\n+    }\n+\n+    /**\n+     * Converts a reference number to sub-animatable index.\n+     * \n+     * \\param  n An argument of type int.\n+     * \\return     Returns a value of type int.\n+     * \\see        Related API elements\n+     */\n+    int RefNumToSubAnim(int n)\n+    {\n+        // Not blazingly fast, but simple. Should be sufficient because it is only \n+        // the number of sub-anims should be minimal.\n+        for (int i=0; i < NumSubs(); ++i) \n+            if (SubAnimToRefNum(i) == n) \n+                return i;\n+        return -1;\n+    }    \n+    //@}\n+};\n+\ndiff --git a/Shared/Texture2DPlugin.h b/Shared/Texture2DPlugin.h\nnew file mode 100644\nindex 00000000..1954f78b\n--- /dev/null\n+++ b/Shared/Texture2DPlugin.h\n@@ -0,0 +1,169 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "TexturePlugin.h"\n+\n+/**\n+ * Provides a base class for 2D texture plug-ins. Automatically\n+ * manages the UVGen reference / sub-animatable, and uses \n+ * it to provide many of the default implementations.\n+ * Also provides MapSampler functionality.\n+ */\n+class Texture2DPlugin\n+    : public TexturePlugin<Texmap>\n+    , public MapSampler\n+{\n+\n+public:\n+\n+    Texture2DPlugin(ClassDesc2* classDesc)\n+        : TexturePlugin(classDesc), MapSampler()\n+    {\n+        RegisterReference(1, GetNewDefaultUVGen());\n+        RegisterSubAnim(1, 1, _T("UVGen"));\n+    }\n+\n+    ~Texture2DPlugin()\n+    {\n+    }\n+\n+    //=========================================================\n+    // TexMap overrides\n+\n+    /// \\name TexMap Overrides\n+    //@{\n+    /**\n+     * \\see         TexMap::GetTheUVGen()\n+     * \n+     * \\return      Returns a value of type UVGen *.\n+     */\n+    UVGen* GetTheUVGen() \n+    { \n+        return GetReferenceAs<UVGen*>(1);\n+    }\n+    /**\n+     * \\see         TexMap::GetUVTransform()\n+     * \n+     * \\param       uvtrans An argument of type Matrix3 &.\n+     */\n+    void GetUVTransform(Matrix3 &uvtrans) \n+    { \n+        GetTheUVGen()->GetUVTransform(uvtrans); \n+    }\n+    /**\n+     * \\see         TexMap::GetTextureTiling() \n+     * \n+     * \\return      Returns a value of type int.\n+     */\n+    int GetTextureTiling() \n+    { \n+        return GetTheUVGen()->GetTextureTiling(); \n+    }\n+\t/**\n+\t * \\see         TexMap::GetUVWSource() \n+\t * \n+\t * \\return      Returns a value of type int.\n+\t */\n+\tint GetUVWSource() \n+    { \n+        return GetTheUVGen()->GetUVWSource(); \n+    }\n+\n+    /**\n+     * \\see         TexMap::EvalColor()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type AColor.\n+     */\n+    AColor EvalColor(ShadeContext& sc)\n+    {\n+    \tif (!sc.doMaps) return AColor(0.0f,0.0f,0.0f,0.0f);;\n+    \tif (gbufID) sc.SetGBufferID(gbufID);\n+        return GetTheUVGen()->EvalUVMap(sc, this);\n+    }\n+\n+    /**\n+     * \\see         TexMap::EvalMono()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type float.\n+     */\n+    float EvalMono(ShadeContext& sc)\n+    {\n+\t    if (!sc.doMaps) return 0.0f;\n+    \tif (gbufID) sc.SetGBufferID(gbufID);\n+    \treturn GetTheUVGen()->EvalUVMapMono(sc, this);\n+    }\n+\n+    /**\n+     * \\see         TexMap::EvalNormalPerturb()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type Point3.\n+     */\n+    Point3 EvalNormalPerturb(ShadeContext& sc)\n+    {\n+\t    if (!sc.doMaps) return Point3(0,0,0);\n+\t    if (gbufID) sc.SetGBufferID(gbufID);\n+\n+        Point3 dPdu, dPdv;\n+        GetTheUVGen()->GetBumpDP(sc, dPdu, dPdv);\t    \n+\n+        Point2 dM = GetTheUVGen()->EvalDeriv(sc, this);\n+\t    Point3 np = dM.x*dPdu+dM.y*dPdv;        \n+\t    return np;\n+    }\n+\n+    /**\n+     * \\see         TexMap::LocalRequirements()\n+     * \n+     * \\param       subMtlNum An argument of type int.\n+     * \\return      Returns a value of type ULONG.\n+     */\n+    ULONG LocalRequirements(int subMtlNum)\n+    {\n+\t    return GetTheUVGen()->Requirements(subMtlNum); \n+    }\n+    //@}\n+\n+    /// \\name MapSampler Overrides\n+    //@{\n+    /**\n+     * \\see         TexMap::Sample()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\param       u An argument of type float.\n+     * \\param       v An argument of type float.\n+     * \\return      Returns a value of type AColor.\n+     */\n+    virtual AColor Sample(ShadeContext &sc, float u, float v)\n+    {\n+        return AColor(0.0f, 0.0f, 0.0f, 0.0f);\n+    }\n+\n+    /**\n+     * \\see         TexMap::SampleFilter()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\param       u An argument of type float.\n+     * \\param       v An argument of type float.\n+     * \\param       du An argument of type float.\n+     * \\param       dv An argument of type float.\n+     * \\return      Returns a value of type AColor.\n+     */\n+    virtual AColor SampleFilter(ShadeContext &sc, float u, float v, float du, float dv)\n+    {\n+        return AColor(0.0f, 0.0f, 0.0f, 0.0f);\n+    }\n+    //@}\n+};\ndiff --git a/Shared/Texture3DPlugin.h b/Shared/Texture3DPlugin.h\nnew file mode 100644\nindex 00000000..bfadd636\n--- /dev/null\n+++ b/Shared/Texture3DPlugin.h\n@@ -0,0 +1,110 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "TexturePlugin.h"\n+\n+/**\n+ * Base class for 3D texture plug-ins.\n+ * Manages a XYZGen and uses it to provide many of the default \n+ * implementations. \n+ */\n+class Texture3DPlugin\n+    : public TexturePlugin<Tex3D>\n+{\n+\n+public:\n+\n+    Texture3DPlugin(ClassDesc2* classDesc)\n+        : TexturePlugin(classDesc)\n+    {\n+        RegisterReference(1, GetNewDefaultXYZGen());\n+        RegisterSubAnim(1, 1, _T("XYZGen"));\n+    }\n+\n+    //=========================================================\n+    // TexMap overrides\n+\n+    /// \\name TexMap Overrides\n+    //@{\n+    /**\n+     * \\see         TexMap::GetTheXYZGen()\n+     * \n+     * \\return      Returns a value of type XYZGen *.\n+     */\n+    XYZGen* GetTheXYZGen() \n+    { \n+        return GetReferenceAs<XYZGen*>(1);\n+    }\n+\n+    /**\n+     * \\see         TexMap::EvalColor()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type AColor.\n+     */\n+    AColor EvalColor(ShadeContext& sc)\n+    {\n+    \tif (!sc.doMaps) return AColor(0.0f,0.0f,0.0f,0.0f);;\n+    \tif (gbufID) sc.SetGBufferID(gbufID);\n+        return AColor(0.0f,0.0f,0.0f,0.0f);;\n+    }\n+\n+    /**\n+     * \\see         TexMap::EvalMono()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type float.\n+     */\n+    float EvalMono(ShadeContext& sc)\n+    {\n+\t    if (!sc.doMaps) return 0.0f;\n+    \tif (gbufID) sc.SetGBufferID(gbufID);\n+    \treturn Intens(EvalColor(sc));\n+    }\n+\n+    /**\n+     * \\see         TexMap::EvalNormalPerturb()\n+     * \n+     * \\param       sc An argument of type ShadeContext &.\n+     * \\return      Returns a value of type Point3.\n+     */\n+    Point3 EvalNormalPerturb(ShadeContext& sc)\n+    {\n+\t    if (!sc.doMaps) return Point3(0,0,0);\n+\t    if (gbufID) sc.SetGBufferID(gbufID);\n+        return Point3(0,0,0);\n+    }\n+\n+    /**\n+     * \\see         TexMap::LocalRequirements()\n+     * \n+     * \\param       subMtlNum An argument of type int.\n+     * \\return      Returns a value of type ULONG.\n+     */\n+    ULONG LocalRequirements(int subMtlNum)\n+    {\n+\t    return GetTheXYZGen()->Requirements(subMtlNum); \n+    }\n+\n+\t/**\n+\t * \\see         TexMap::ReadSXPData()\n+\t * \n+\t * \\param       name An argument of type TCHAR *.\n+\t * \\param       sxpdata An argument of type void *.\n+\t */\n+\tvoid ReadSXPData(TCHAR *name, void *sxpdata) \n+    { \n+        // Do nothing\n+    }\n+    //@}\n+};\ndiff --git a/Shared/TexturePlugin.h b/Shared/TexturePlugin.h\nnew file mode 100644\nindex 00000000..6008b18c\n--- /dev/null\n+++ b/Shared/TexturePlugin.h\n@@ -0,0 +1,115 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "MtlBasePlugin.h"\n+\n+/**\n+ * A base-class for both 2D and 3D textures. It manages ParamDlgs\n+ * for both UVGen and XYZGen. \n+ */\n+template<typename Base_T>\n+class TexturePlugin\n+    : public MtlBasePlugin<Base_T>\n+{\n+    // Note: these fields may get deleted (i.e. invalidated) at some arbitrary \n+    // point by 3ds Max. They are only used in DlgSetThing.\n+    // This is slightly inefficient, because both all maps whether they are \n+    // 2D or 3D will have access to both. This is inconsequential, and putting \n+    // them here, reduce complexity in those classes. Besides, if you wanted \n+    // you can now create a texture map that is both 2D and 3D.\n+    ParamDlg* dlgUVGen;\n+    ParamDlg* dlgXYZGen;\n+    \n+public:\n+\n+    //===========================================================================\n+    // Constructor\n+\n+    TexturePlugin(ClassDesc2* classDesc) \n+        : MtlBasePlugin(classDesc), dlgUVGen(NULL), dlgXYZGen(NULL)\n+    {\n+    }\n+\n+    //===========================================================================\n+    // MtlBase overrides\n+\n+    /// \\name MtlBase Overrides\n+    //@{\n+    virtual ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) \n+    {\n+        IAutoMParamDlg* masterDlg = \n+            dynamic_cast<IAutoMParamDlg*>(MtlBasePlugin::CreateParamDlg(hwMtlEdit, imp));\n+        DbgAssert(masterDlg != NULL);\n+\n+        if (GetTheUVGen() != NULL)\n+        {\n+            dlgUVGen = GetTheUVGen()->CreateParamDlg(hwMtlEdit, imp);\n+            masterDlg->AddDlg(dlgUVGen);\n+        }\n+\n+        if (GetTheXYZGen() != NULL)\n+        {\n+            dlgXYZGen = GetTheXYZGen()->CreateParamDlg(hwMtlEdit, imp);\n+            masterDlg->AddDlg(dlgXYZGen);\n+        }\n+\n+\t    return masterDlg;\n+    }  \n+    //@}\n+\n+    //===========================================================================\n+    // Texmap overrides\n+\n+    /// \\name Texmap Overrides\n+    //@{\n+    /**\n+     * \\see         Texmap::SetDlgThing()\n+     * \n+     * \\param       dlg An argument of type ParamDlg *.\n+     * \\return      Returns a value of type BOOL.\n+     */\n+    virtual BOOL SetDlgThing(ParamDlg* dlg)\n+    {\n+\t    if (dlg == dlgUVGen)\n+        {\n+\t\t    dlgUVGen->SetThing(GetTheUVGen());\n+    \t    return TRUE;\n+        }\n+\t    else if (dlg == dlgXYZGen)\n+        {\n+            dlgXYZGen->SetThing(GetTheXYZGen());\n+    \t    return TRUE;\n+        }\n+\t\telse \n+        {\n+            return FALSE;\n+        }\n+    }\n+\n+    /**\n+     * Resets the validity interval and calls reset on the UVGen \n+     * and XYZGen\n+     * \n+     * \\see         MtlBasePlugin::Reset() \n+     */\n+    virtual void Reset()\n+    {\n+        if (GetTheUVGen() != NULL)\n+            GetTheUVGen()->Reset();\n+        if (GetTheXYZGen() != NULL)\n+            GetTheXYZGen()->Reset();\n+        MtlBasePlugin::Reset();\n+    }\n+    //@}\n+};\n+    \ndiff --git a/Shared/Utility.h b/Shared/Utility.h\nnew file mode 100644\nindex 00000000..dc3ebb2f\n--- /dev/null\n+++ b/Shared/Utility.h\n@@ -0,0 +1,394 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+#include "ILinkTMCtrl.h"\n+\n+//=======================================================\n+// Forward declarations\n+\n+INode* FindNodeRef(ReferenceTarget *rt);\n+\n+//=======================================================\n+// Utility classes\n+\n+/**\n+ * Sets a GraphicWindows into HitTest mode and restores \n+ * the original state upon destruction. This is an idiom \n+ * known as RAII, for resource allocation is initialization. \n+ */\n+class HitTestModeRAII :\n+    MaxSDK::Util::Noncopyable\n+{\n+    GraphicsWindow* gw;\n+    DWORD savedLimits;\n+\n+public:\n+    HitTestModeRAII(GraphicsWindow* x)\n+        : gw(x), savedLimits(x->getRndLimits())\n+    {\n+        gw->clearHitCode();\n+        gw->setRndLimits((savedLimits | GW_PICK) & ~GW_ILLUM);\n+    }\n+\n+    ~HitTestModeRAII()\n+    {\n+        gw->setRndLimits(savedLimits);\n+    }\n+};\n+\n+//=======================================================\n+// Utility functions, many are taken from various samples.\n+\n+/**\n+ * Deletes a pointer variable and sets it to NULL. \n+ * \n+ * \\param       x An argument of type T *&.\n+ */\n+template<typename T>\n+void DeleteAndNullify(T*& x)\n+{\n+    delete x;\n+    x = NULL;\n+}\n+\n+\n+/**\n+ * Returns the current time. \n+ * \n+ * \\return      Returns a value of type TimeValue.\n+ */\n+TimeValue Now() \n+{\n+    return GetCOREInterface()->GetTime();\n+}\n+\n+/**\n+ * Get the first node pointing to a reference maker.\n+ * If "rm" is a node, returns it instead.\n+ * \n+ * \\param       rm An argument of type ReferenceMaker *.\n+ * \\return      Returns a value of type INode *.\n+ */\n+INode* GetNodeRef(ReferenceMaker *rm) \n+{\n+\tif (rm->SuperClassID()==BASENODE_CLASS_ID) \n+        return dynamic_cast<INode*>(rm);\n+\telse \n+        return rm->IsRefTarget() \n+            ? FindNodeRef(dynamic_cast<ReferenceTarget*>(rm))\n+            : NULL;\n+}\n+\n+/**\n+ * Finds the first node referencing a reference target.\n+ * \n+ * \\param       rt An argument of type ReferenceTarget *.\n+ * \\return      Returns a value of type INode *.\n+ */\n+INode* FindNodeRef(ReferenceTarget *rt) {\n+\tDependentIterator di(rt);\n+\tINode *nd = NULL;\t\n+\tfor (ReferenceMaker *rm = di.Next(); rm != NULL; rm = di.Next())\n+    {\t\n+\t\tnd = GetNodeRef(rm);\n+\t\tif (nd) return nd;\n+\t}\n+\treturn NULL;\n+}\n+\n+/**\n+ * Return a pointer to a TriObject given an INode or NULL if no associated tri-object is found.\n+ * \n+ * \\param       node An argument of type INode *.\n+ * \\param       t An argument of type const TimeValue.\n+ * \\param       deleteIt An argument of type bool &.\n+ * \\return      Returns a value of type TriObject *.\n+ */\n+TriObject* GetTriObjectFromNode(INode *node,const TimeValue t,bool &deleteIt)\n+{\n+\tdeleteIt = false;\n+\tObject *obj = node->EvalWorldState(t).obj;\n+\tif(obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID,0)))\n+    { \n+\t\tTriObject *tri = (TriObject *) obj->ConvertToType(0,Class_ID(TRIOBJ_CLASS_ID,0));\n+\t\t// Note that the TriObject should only be deleted\n+\t\t// if the pointer to it is not equal to the object\n+\t\t// pointer that called ConvertToType()\n+\t\tif (obj != reinterpret_cast<Object*>(tri))\n+            deleteIt = true;\n+\t\treturn tri;\n+\t}\n+\telse\n+\t\treturn NULL;\n+}\n+\n+/**\n+ * Gets the rendered image aspect ratio.\n+ * \n+ * \\return      Returns a value of type float.\n+ */\n+float GetAspect() \n+{\n+\treturn GetCOREInterface()->GetRendImageAspect();\n+}\n+\n+/**\n+ * Gets the aperture width in millimeters.\n+ * \n+ * \\return      Returns a value of type float.\n+ */\n+float GetApertureWidth() \n+{\n+\treturn GetCOREInterface()->GetRendApertureWidth();\n+}\n+\n+/**\n+ * Gets the point targeted by a node. \n+ * Returns FALSE is there is no target transformation matrix.\n+ * \n+ * \\param       t An argument of type TimeValue.\n+ * \\param       inode An argument of type INode *.\n+ * \\param       p An argument of type Point3 &.\n+ * \\return      Returns a value of type BOOL.\n+ */\n+BOOL GetTargetPoint(TimeValue t, INode* inode, Point3& p) \n+{\n+\tMatrix3 tmat;\n+\tif (inode->GetTargetTM(t,tmat)) {\n+\t\tp = tmat.GetTrans();\n+\t\treturn TRUE;\n+\t}\n+    else {\n+\t\treturn FALSE;\n+    }\n+}\n+\n+/**\n+ * Gets the distance to a target, or 0.0f if there is no target.\n+ * \n+ * \\param       t An argument of type TimeValue.\n+ * \\param       inode An argument of type INode *.\n+ * \\return      Returns a value of type float.\n+ */\n+float GetTargetDistance(TimeValue t, INode* inode)\n+{\n+    Point3 pt;\n+    if (!GetTargetPoint(t, inode, pt))\n+        return 0.0f;\n+    return GetDistanceTo(inode->GetObjectTM(t), pt);\n+}\n+\n+/**\n+ * Converts millimeter to field-of-view radians using the current aperture width.\n+ * \n+ * \\param       mm An argument of type float.\n+ * \\param       width An argument of type float.\n+ * \\return      Returns a value of type float.\n+ */\n+float MMtoFOV(float mm, float width = GetApertureWidth()) \n+{\n+\treturn float(2.0f*atan(0.5f*width/mm));\n+}\n+\n+/**\n+ * Converts field-of-view radians to millimeters using the current aperture width.\n+ * \n+ * \\param       fov An argument of type float.\n+ * \\param       width An argument of type float.\n+ * \\return      Returns a value of type float.\n+ */\n+float FOVtoMM(float fov, float width = GetApertureWidth())\t\n+{\n+\treturn float((0.5f*width)/tan(fov/2.0f));\n+}\n+\n+/**\n+ * Displays a dialog box that asks the user a simple yes or no question.\n+ * \n+ * \\param       str An argument of type const MSTR &.\n+ * \\return      Returns a value of type BOOL.\n+ */\n+BOOL QueryUser(const MSTR& str)\n+{\n+    return MessageBox(GetCOREInterface()->GetMAXHWnd(), str, _M("Question"), MB_ICONQUESTION | MB_YESNO) == IDYES;\n+}\n+\n+/**\n+ * Displays a simple dialog box with a message in it.\n+ * \n+ * \\param       str An argument of type const MSTR &.\n+ */\n+void MessageBox(const MSTR& str)\n+{\n+    MessageBox(GetCOREInterface()->GetMAXHWnd(), str, _M("Information"), MB_OK);\n+}\n+\n+/**\n+ * Redraw all views at the specified time.\n+ * \n+ * \\param       t An argument of type TimeValue.\n+ */\n+void RedrawViews(TimeValue t = Now())\n+{\n+    GetCOREInterface()->RedrawViews(t);  \n+}\n+\n+/**\n+ * Removes scaling from a node\'s transformation matrix, so that it appears always \n+ * a constant size in the viewport (e.g. camera).\n+ * \n+ * \\param       t An argument of type TimeValue.\n+ * \\param       inode An argument of type INode *.\n+ * \\param       vpt An argument of type ViewExp *.\n+ * \\param       tm An argument of type Matrix3 &.\n+ */\n+void GetUnscaledMatrix(TimeValue t, INode* inode, ViewExp* vpt, Matrix3& tm) \n+{\n+    tm = inode->GetObjectTM(t);\n+    RemoveScaling(tm);\n+    float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth(tm.GetTrans()) / 360.0f;\n+    if (scaleFactor!=(float)1.0)\n+\t    tm.Scale(Point3(scaleFactor,scaleFactor,scaleFactor));\n+}\n+\n+/**\n+ * Returns the distance between two positions represented as matrices.\n+ * \n+ * \\param       x An argument of type const Matrix3 &.\n+ * \\param       y An argument of type const Matrix3 &.\n+ * \\return      Returns a value of type float.\n+ */\n+float GetDistanceBetween(const Matrix3& x, const Matrix3& y)\n+{\n+    return Length(x.GetTrans() - y.GetTrans());    \n+}\n+\n+/**\n+ * Returns the distance between two nodes.\n+ * \n+ * \\param       x An argument of type INode *.\n+ * \\param       y An argument of type INode *.\n+ * \\param       time An argument of type TimeValue.\n+ * \\return      Returns a value of type float.\n+ */\n+float GetDistanceBetween(INode* x, INode* y, TimeValue time = Now()) \n+{\n+    return GetDistanceBetween(x->GetObjectTM(time), y->GetObjectTM(time));\n+}\n+\n+/**\n+ * Facilitates drawing to a GraphicsWindow \n+ */\n+class DrawingContext\n+{\n+    GraphicsWindow* gw;\n+\n+public:\n+\n+    DrawingContext(GraphicsWindow* x)\n+        : gw(x)\n+    { }\n+\n+    void DrawLine(const Point3& a, const Point3& b)\n+    {\n+        Point3 line[2 + 1] = { a, b, Point3() };\n+ \t    gw->polyline( 2, line, NULL, NULL, TRUE, NULL );\n+    }    \n+};\n+\n+/**\n+ * Rotates a node using the passed matrix\n+ * \n+ * \\param       src An argument of type INode *.\n+ * \\param       mat An argument of type const Matrix3 &.\n+ * \\param       time An argument of type TimeValue.\n+ */\n+void RotateToMatrix(INode* src, const Matrix3& mat, TimeValue time = Now())\n+{\n+    Matrix3 tm = src->GetObjectTM(Now());\n+    Quat q(tm); \n+    Quat q2(mat);\n+    Quat q3 = q.Inverse() * q2; \n+    src->Rotate(time, Identity(), q3, TRUE);\n+}\n+\n+\n+/**\n+ * Rotates a node so that it faces another node.\n+ * \n+ * \\param       src An argument of type INode *.\n+ * \\param       target An argument of type INode *.\n+ * \\param       time An argument of type TimeValue.\n+ */\n+void TurnNodeTowards(INode* src, INode* target, TimeValue time = Now())\n+{\n+    if (src == NULL || target == NULL)\n+        return;\n+    Matrix3 srcMat = src->GetObjectTM(time);\n+    Matrix3 targetMat = target->GetObjectTM(time);\n+    Point3 srcPoint = srcMat.GetTrans();\n+    Point3 targetPoint = targetMat.GetTrans();\n+    Matrix3 rotMat;\n+    rotMat.SetFromToUp(srcPoint, targetPoint, Point3::ZAxis);    \n+    RotateToMatrix(src, rotMat, time);\n+}\n+\n+/**\n+ * Get the location associated with a particular node.\n+ * \n+ * \\param       node An argument of type INode *.\n+ * \\param       time An argument of type TimeValue.\n+ * \\return      Returns a value of type Point3.\n+ */\n+Point3 GetNodeLocation(INode* node, TimeValue time = Now())\n+{\n+    Matrix3 mat = node->GetObjectTM(time);\n+    return mat.GetTrans();\n+}\n+\n+/**\n+ * Gets a vector from a point to a node.\n+ * \n+ * \\param       point An argument of type const Point3 &.\n+ * \\param       target An argument of type INode *.\n+ * \\param       time An argument of type TimeValue.\n+ * \\return      Returns a value of type Point3.\n+ */\n+Point3 GetVectorTo(const Point3& point, INode* target, TimeValue time = Now())\n+{\n+    return GetNodeLocation(target, time) - point;\n+}\n+\n+/**\n+ * Get distance from a point to a node. \n+ * \n+ * \\param       point An argument of type const Point3 &.\n+ * \\param       target An argument of type INode *.\n+ * \\param       time An argument of type TimeValue.\n+ * \\return      Returns a value of type float.\n+ */\n+float GetDistanceTo(const Point3& point, INode* target, TimeValue time = Now())\n+{\n+    return GetVectorTo(point, target, time).Length();\n+}\n+\n+/**\n+ * Returns true if the point is equal to (0,0,0)\n+ *\n+ * \\param       point An argument of type const Point3 &.\n+ * \\return      Returns a value of type BOOL.\n+ */\n+BOOL IsZeroVector(const Point3& point)\n+{\n+    return point.Equals(Point3::Origin);\n+}\ndiff --git a/Shared/UtilityPlugin.h b/Shared/UtilityPlugin.h\nnew file mode 100644\nindex 00000000..216a9966\n--- /dev/null\n+++ b/Shared/UtilityPlugin.h\n@@ -0,0 +1,24 @@\n+/* Copyright (c) 2011, Autodesk \n+   All rights reserved.\n+\n+   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n+\n+  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n+  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n+  Neither the name of Autodesk or the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#pragma once\n+\n+/**\n+ * A class template for use as a base class for plug-ins that don\'t derive from Animatable.\n+ */\n+template<typename Base_T>\n+class UtilityPlugin\n+    : public Base_T\n+{\n+\t\n+\n+};\n+\ndiff --git a/Shared/common.def.NOTUSED b/Shared/common.def.NOTUSED\nnew file mode 100644\nindex 00000000..135daa3c\n--- /dev/null\n+++ b/Shared/common.def.NOTUSED\n@@ -0,0 +1,9 @@\n+EXPORTS\n+   LibDescription       @1       PRIVATE\n+   LibNumberClasses     @2       PRIVATE\n+   LibClassDesc         @3       PRIVATE\n+   LibVersion           @4       PRIVATE\n+   LibInitialize\t\t@5       PRIVATE\n+   LibShutdown\t\t\t@6       PRIVATE\n+SECTIONS\n+   .data READ WRITE\n', 'commit_subject': 'exploring alternative modifier formulations.', 'repo_name': 'Exocortex/ExocortexCrate', 'committer': {'name': 'Ben Houston', 'email': '73675debcd8a436be48ec22211dcf44fe0df0a64@exocortex.com', 'time_sec': '1331396652', 'tz_offset': '-300', 'date': {'seconds': '1331396652'}}, 'commit_tree': '590d38808a4cadb393938b98a1a56eaab0ebbca7', 'commit_message': 'exploring alternative modifier formulations.\n', 'language_name': 'Python', 'license': 'bsd-3-clause', 'hunks': '@@ -0,0 +1,9 @@\n+EXPORTS\n+   LibDescription       @1       PRIVATE\n+   LibNumberClasses     @2       PRIVATE\n+   LibClassDesc         @3       PRIVATE\n+   LibVersion           @4       PRIVATE\n+   LibInitialize\t\t@5       PRIVATE\n+   LibShutdown\t\t\t@6       PRIVATE\n+SECTIONS\n+   .data READ WRITE\n', 'addition_count': 9, 'deletion_count': 0, 'src_file': '/dev/null', 'tgt_file': 'b/Shared/common.def.NOTUSED', 'file_extension': '.NOTUSED', 'before_file': 'ADDFILE', 'message': 'exploring alternative modifier formulations.\n', 'author_name': 'Ben Houston', 'lang': 'def'}