瀏覽代碼

Merge branch 'development-gg' into glpboimprovement

Conflicts:
	Engine/source/gfx/gl/gfxGLTextureManager.cpp
Jeff Hutchinson 9 年之前
父節點
當前提交
e43a0ba6e9
共有 100 個文件被更改,包括 13362 次插入1984 次删除
  1. 138 0
      Engine/source/T3D/assets/ComponentAsset.cpp
  2. 83 0
      Engine/source/T3D/assets/ComponentAsset.h
  3. 134 0
      Engine/source/T3D/assets/GameObjectAsset.cpp
  4. 75 0
      Engine/source/T3D/assets/GameObjectAsset.h
  5. 4 2
      Engine/source/T3D/assets/ShapeAsset.cpp
  6. 6 4
      Engine/source/T3D/assets/ShapeAsset.h
  7. 717 0
      Engine/source/T3D/components/animation/animationComponent.cpp
  8. 138 0
      Engine/source/T3D/components/animation/animationComponent.h
  9. 222 0
      Engine/source/T3D/components/animation/animationComponent_ScriptBinding.h
  10. 483 0
      Engine/source/T3D/components/camera/cameraComponent.cpp
  11. 159 0
      Engine/source/T3D/components/camera/cameraComponent.h
  12. 91 0
      Engine/source/T3D/components/camera/cameraComponent_ScriptBinding.h
  13. 172 0
      Engine/source/T3D/components/camera/cameraOrbiterComponent.cpp
  14. 42 28
      Engine/source/T3D/components/camera/cameraOrbiterComponent.h
  15. 582 0
      Engine/source/T3D/components/collision/collisionComponent.cpp
  16. 208 0
      Engine/source/T3D/components/collision/collisionComponent.h
  17. 172 0
      Engine/source/T3D/components/collision/collisionComponent_ScriptBinding.h
  18. 258 0
      Engine/source/T3D/components/collision/collisionInterfaces.cpp
  19. 167 0
      Engine/source/T3D/components/collision/collisionInterfaces.h
  20. 619 0
      Engine/source/T3D/components/collision/collisionTrigger.cpp
  21. 145 0
      Engine/source/T3D/components/collision/collisionTrigger.h
  22. 640 0
      Engine/source/T3D/components/component.cpp
  23. 197 0
      Engine/source/T3D/components/component.h
  24. 101 0
      Engine/source/T3D/components/coreInterfaces.h
  25. 434 0
      Engine/source/T3D/components/game/stateMachine.cpp
  26. 259 0
      Engine/source/T3D/components/game/stateMachine.h
  27. 215 0
      Engine/source/T3D/components/game/stateMachineComponent.cpp
  28. 81 0
      Engine/source/T3D/components/game/stateMachineComponent.h
  29. 359 0
      Engine/source/T3D/components/game/triggerComponent.cpp
  30. 74 0
      Engine/source/T3D/components/game/triggerComponent.h
  31. 368 0
      Engine/source/T3D/components/physics/physicsBehavior.cpp
  32. 135 0
      Engine/source/T3D/components/physics/physicsBehavior.h
  33. 0 0
      Engine/source/T3D/components/physics/physicsComponentInterface.cpp
  34. 15 21
      Engine/source/T3D/components/physics/physicsComponentInterface.h
  35. 865 0
      Engine/source/T3D/components/physics/playerControllerComponent.cpp
  36. 212 0
      Engine/source/T3D/components/physics/playerControllerComponent.h
  37. 467 0
      Engine/source/T3D/components/physics/rigidBodyComponent.cpp
  38. 183 0
      Engine/source/T3D/components/physics/rigidBodyComponent.h
  39. 530 0
      Engine/source/T3D/components/render/meshComponent.cpp
  40. 183 0
      Engine/source/T3D/components/render/meshComponent.h
  41. 155 0
      Engine/source/T3D/components/render/meshComponent_ScriptBinding.h
  42. 62 0
      Engine/source/T3D/components/render/renderComponentInterface.h
  43. 1939 0
      Engine/source/T3D/entity.cpp
  44. 283 0
      Engine/source/T3D/entity.h
  45. 1 1
      Engine/source/T3D/fps/guiCrossHairHud.cpp
  46. 79 2
      Engine/source/T3D/gameBase/gameConnection.cpp
  47. 11 0
      Engine/source/T3D/gameBase/processList.cpp
  48. 32 0
      Engine/source/T3D/gameBase/std/stdGameProcess.cpp
  49. 1 1
      Engine/source/T3D/gameFunctions.cpp
  50. 5 3
      Engine/source/T3D/lightAnimData.cpp
  51. 1 1
      Engine/source/T3D/lightAnimData.h
  52. 10 3
      Engine/source/T3D/objectTypes.h
  53. 86 0
      Engine/source/T3D/physics/bullet/btBody.cpp
  54. 4 0
      Engine/source/T3D/physics/bullet/btBody.h
  55. 15 10
      Engine/source/T3D/physics/bullet/btPlayer.cpp
  56. 4 0
      Engine/source/T3D/physics/bullet/btPlayer.h
  57. 8 0
      Engine/source/T3D/physics/physicsBody.h
  58. 70 0
      Engine/source/T3D/physics/physx3/px3Body.cpp
  59. 5 0
      Engine/source/T3D/physics/physx3/px3Body.h
  60. 30 0
      Engine/source/T3D/physics/physx3/px3Player.cpp
  61. 2 2
      Engine/source/T3D/physics/physx3/px3Player.h
  62. 58 1
      Engine/source/T3D/physics/physx3/px3World.cpp
  63. 5 0
      Engine/source/T3D/physics/physx3/px3World.h
  64. 10 9
      Engine/source/T3D/shapeBase.cpp
  65. 42 1
      Engine/source/T3D/vehicles/vehicle.cpp
  66. 7 0
      Engine/source/T3D/vehicles/vehicle.h
  67. 1 1
      Engine/source/assets/assetManager.cpp
  68. 0 82
      Engine/source/component/componentInterface.cpp
  69. 0 234
      Engine/source/component/componentInterface.h
  70. 0 209
      Engine/source/component/dynamicConsoleMethodComponent.cpp
  71. 0 89
      Engine/source/component/dynamicConsoleMethodComponent.h
  72. 0 452
      Engine/source/component/simComponent.cpp
  73. 0 256
      Engine/source/component/simComponent.h
  74. 0 159
      Engine/source/component/simpleComponent.h
  75. 0 68
      Engine/source/component/test/moreAdvancedComponentTest.cpp
  76. 0 149
      Engine/source/component/test/simComponentTest.cpp
  77. 0 131
      Engine/source/component/test/simpleComponentTest.cpp
  78. 4 0
      Engine/source/console/compiledEval.cpp
  79. 2 0
      Engine/source/console/consoleObject.h
  80. 25 0
      Engine/source/console/consoleTypes.cpp
  81. 4 0
      Engine/source/console/consoleTypes.h
  82. 4 0
      Engine/source/console/simObject.h
  83. 1 1
      Engine/source/gfx/gfxDrawUtil.cpp
  84. 18 40
      Engine/source/gfx/gl/gfxGLCardProfiler.cpp
  85. 7 5
      Engine/source/gfx/gl/gfxGLCircularVolatileBuffer.h
  86. 17 1
      Engine/source/gfx/gl/gfxGLDevice.cpp
  87. 12 0
      Engine/source/gfx/gl/gfxGLDevice.h
  88. 6 1
      Engine/source/gfx/gl/gfxGLShader.cpp
  89. 3 2
      Engine/source/gfx/gl/gfxGLStateBlock.cpp
  90. 10 3
      Engine/source/gfx/gl/gfxGLTextureManager.cpp
  91. 8 3
      Engine/source/gfx/gl/gfxGLTextureObject.cpp
  92. 1 1
      Engine/source/gfx/gl/gfxGLTextureTarget.cpp
  93. 2 2
      Engine/source/gfx/gl/gfxGLVertexBuffer.cpp
  94. 2 2
      Engine/source/gfx/gl/gfxGLVertexDecl.cpp
  95. 1 1
      Engine/source/gfx/gl/gfxGLWindowTarget.cpp
  96. 69 1
      Engine/source/gui/controls/guiTreeViewCtrl.cpp
  97. 12 1
      Engine/source/gui/controls/guiTreeViewCtrl.h
  98. 1 1
      Engine/source/gui/core/guiCanvas.h
  99. 42 0
      Engine/source/gui/editor/guiInspector.cpp
  100. 252 0
      Engine/source/gui/editor/inspector/componentGroup.cpp

+ 138 - 0
Engine/source/T3D/assets/ComponentAsset.cpp

@@ -0,0 +1,138 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef COMPONENT_ASSET_H
+#include "ComponentAsset.h"
+#endif
+
+#ifndef _ASSET_MANAGER_H_
+#include "assets/assetManager.h"
+#endif
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+#ifndef _TAML_
+#include "persistence/taml/taml.h"
+#endif
+
+#ifndef _ASSET_PTR_H_
+#include "assets/assetPtr.h"
+#endif
+
+// Debug Profiling.
+#include "platform/profiler.h"
+
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(ComponentAsset);
+
+ConsoleType(ComponentAssetPtr, TypeComponentAssetPtr, ComponentAsset, ASSET_ID_FIELD_PREFIX)
+
+//-----------------------------------------------------------------------------
+
+ConsoleGetType(TypeComponentAssetPtr)
+{
+   // Fetch asset Id.
+   return (*((AssetPtr<ComponentAsset>*)dptr)).getAssetId();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleSetType(TypeComponentAssetPtr)
+{
+   // Was a single argument specified?
+   if (argc == 1)
+   {
+      // Yes, so fetch field value.
+      const char* pFieldValue = argv[0];
+
+      // Fetch asset pointer.
+      AssetPtr<ComponentAsset>* pAssetPtr = dynamic_cast<AssetPtr<ComponentAsset>*>((AssetPtrBase*)(dptr));
+
+      // Is the asset pointer the correct type?
+      if (pAssetPtr == NULL)
+      {
+         // No, so fail.
+         //Con::warnf("(TypeTextureAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
+         return;
+      }
+
+      // Set asset.
+      pAssetPtr->setAssetId(pFieldValue);
+
+      return;
+   }
+
+   // Warn.
+   Con::warnf("(TypeTextureAssetPtr) - Cannot set multiple args to a single asset.");
+}
+
+//-----------------------------------------------------------------------------
+
+ComponentAsset::ComponentAsset() :
+   mAcquireReferenceCount(0),
+   mpOwningAssetManager(NULL),
+   mAssetInitialized(false)
+{
+   // Generate an asset definition.
+   mpAssetDefinition = new AssetDefinition();
+
+   mComponentName = StringTable->lookup("");
+   mComponentClass = StringTable->lookup("");
+   mFriendlyName = StringTable->lookup("");
+   mComponentType = StringTable->lookup("");
+   mDescription = StringTable->lookup("");
+}
+
+//-----------------------------------------------------------------------------
+
+ComponentAsset::~ComponentAsset()
+{
+   // If the asset manager does not own the asset then we own the
+   // asset definition so delete it.
+   if (!getOwned())
+      delete mpAssetDefinition;
+}
+
+//-----------------------------------------------------------------------------
+
+void ComponentAsset::initPersistFields()
+{
+   // Call parent.
+   Parent::initPersistFields();
+
+   addField("componentName", TypeString, Offset(mComponentName, ComponentAsset), "Unique Name of the component. Defines the namespace of the scripts for the component.");
+   addField("componentClass", TypeString, Offset(mComponentClass, ComponentAsset), "Class of object this component uses.");
+   addField("friendlyName", TypeString, Offset(mFriendlyName, ComponentAsset), "The human-readble name for the component.");
+   addField("componentType", TypeString, Offset(mComponentType, ComponentAsset), "The category of the component for organizing in the editor.");
+   addField("description", TypeString, Offset(mDescription, ComponentAsset), "Simple description of the component.");
+}
+
+//------------------------------------------------------------------------------
+
+void ComponentAsset::copyTo(SimObject* object)
+{
+   // Call to parent.
+   Parent::copyTo(object);
+}

+ 83 - 0
Engine/source/T3D/assets/ComponentAsset.h

@@ -0,0 +1,83 @@
+#pragma once
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+#ifndef COMPONENT_ASSET_H
+#define COMPONENT_ASSET_H
+
+#ifndef _ASSET_BASE_H_
+#include "assets/assetBase.h"
+#endif
+
+#ifndef _ASSET_DEFINITION_H_
+#include "assets/assetDefinition.h"
+#endif
+
+#ifndef _STRINGUNIT_H_
+#include "string/stringUnit.h"
+#endif
+
+#ifndef _ASSET_FIELD_TYPES_H_
+#include "assets/assetFieldTypes.h"
+#endif
+
+//-----------------------------------------------------------------------------
+class ComponentAsset : public AssetBase
+{
+   typedef AssetBase Parent;
+
+   AssetManager*           mpOwningAssetManager;
+   bool                    mAssetInitialized;
+   AssetDefinition*        mpAssetDefinition;
+   U32                     mAcquireReferenceCount;
+
+   StringTableEntry mComponentName;
+   StringTableEntry mComponentClass;
+   StringTableEntry mFriendlyName;
+   StringTableEntry mComponentType;
+   StringTableEntry mDescription;
+
+public:
+   ComponentAsset();
+   virtual ~ComponentAsset();
+
+   /// Engine.
+   static void initPersistFields();
+   virtual void copyTo(SimObject* object);
+
+   /// Declare Console Object.
+   DECLARE_CONOBJECT(ComponentAsset);
+
+   StringTableEntry getComponentName() { return mComponentName; }
+   StringTableEntry getComponentClass() { return mComponentClass; }
+   StringTableEntry getFriendlyName() { return mFriendlyName; }
+   StringTableEntry getFriendlyType() { return mComponentType; }
+   StringTableEntry getDescription() { return mDescription; }
+
+protected:
+   virtual void            initializeAsset(void) {}
+   virtual void            onAssetRefresh(void) {}
+};
+
+DefineConsoleType(TypeComponentAssetPtr, ComponentAsset)
+
+#endif // _ASSET_BASE_H_
+

+ 134 - 0
Engine/source/T3D/assets/GameObjectAsset.cpp

@@ -0,0 +1,134 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef GAME_OBJECT_ASSET_H
+#include "GameObjectAsset.h"
+#endif
+
+#ifndef _ASSET_MANAGER_H_
+#include "assets/assetManager.h"
+#endif
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+#ifndef _TAML_
+#include "persistence/taml/taml.h"
+#endif
+
+#ifndef _ASSET_PTR_H_
+#include "assets/assetPtr.h"
+#endif
+
+// Debug Profiling.
+#include "platform/profiler.h"
+
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(GameObjectAsset);
+
+ConsoleType(GameObjectAssetPtr, TypeGameObjectAssetPtr, GameObjectAsset, ASSET_ID_FIELD_PREFIX)
+
+//-----------------------------------------------------------------------------
+
+ConsoleGetType(TypeGameObjectAssetPtr)
+{
+   // Fetch asset Id.
+   return (*((AssetPtr<GameObjectAsset>*)dptr)).getAssetId();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleSetType(TypeGameObjectAssetPtr)
+{
+   // Was a single argument specified?
+   if (argc == 1)
+   {
+      // Yes, so fetch field value.
+      const char* pFieldValue = argv[0];
+
+      // Fetch asset pointer.
+      AssetPtr<GameObjectAsset>* pAssetPtr = dynamic_cast<AssetPtr<GameObjectAsset>*>((AssetPtrBase*)(dptr));
+
+      // Is the asset pointer the correct type?
+      if (pAssetPtr == NULL)
+      {
+         // No, so fail.
+         //Con::warnf("(TypeTextureAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
+         return;
+      }
+
+      // Set asset.
+      pAssetPtr->setAssetId(pFieldValue);
+
+      return;
+   }
+
+   // Warn.
+   Con::warnf("(TypeTextureAssetPtr) - Cannot set multiple args to a single asset.");
+}
+
+//-----------------------------------------------------------------------------
+
+GameObjectAsset::GameObjectAsset() :
+   mAcquireReferenceCount(0),
+   mpOwningAssetManager(NULL),
+   mAssetInitialized(false)
+{
+   // Generate an asset definition.
+   mpAssetDefinition = new AssetDefinition();
+
+   mGameObjectName = StringTable->lookup("");
+   mScriptFilePath = StringTable->lookup("");
+   mTAMLFilePath = StringTable->lookup("");
+}
+
+//-----------------------------------------------------------------------------
+
+GameObjectAsset::~GameObjectAsset()
+{
+   // If the asset manager does not own the asset then we own the
+   // asset definition so delete it.
+   if (!getOwned())
+      delete mpAssetDefinition;
+}
+
+//-----------------------------------------------------------------------------
+
+void GameObjectAsset::initPersistFields()
+{
+   // Call parent.
+   Parent::initPersistFields();
+
+   addField("gameObjectName", TypeString, Offset(mGameObjectName, GameObjectAsset), "Name of the game object. Defines the created object's class.");
+   addField("scriptFilePath", TypeString, Offset(mScriptFilePath, GameObjectAsset), "Path to the script file for the GameObject's script code.");
+   addField("TAMLFilePath", TypeString, Offset(mTAMLFilePath, GameObjectAsset), "Path to the taml file for the GameObject's heirarchy.");
+}
+
+//------------------------------------------------------------------------------
+
+void GameObjectAsset::copyTo(SimObject* object)
+{
+   // Call to parent.
+   Parent::copyTo(object);
+}

+ 75 - 0
Engine/source/T3D/assets/GameObjectAsset.h

@@ -0,0 +1,75 @@
+#pragma once
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+#ifndef GAME_OBJECT_ASSET_H
+#define GAME_OBJECT_ASSET_H
+
+#ifndef _ASSET_BASE_H_
+#include "assets/assetBase.h"
+#endif
+
+#ifndef _ASSET_DEFINITION_H_
+#include "assets/assetDefinition.h"
+#endif
+
+#ifndef _STRINGUNIT_H_
+#include "string/stringUnit.h"
+#endif
+
+#ifndef _ASSET_FIELD_TYPES_H_
+#include "assets/assetFieldTypes.h"
+#endif
+
+//-----------------------------------------------------------------------------
+class GameObjectAsset : public AssetBase
+{
+   typedef AssetBase Parent;
+
+   AssetManager*           mpOwningAssetManager;
+   bool                    mAssetInitialized;
+   AssetDefinition*        mpAssetDefinition;
+   U32                     mAcquireReferenceCount;
+
+   StringTableEntry mGameObjectName;
+   StringTableEntry mScriptFilePath;
+   StringTableEntry mTAMLFilePath;
+
+public:
+   GameObjectAsset();
+   virtual ~GameObjectAsset();
+
+   /// Engine.
+   static void initPersistFields();
+   virtual void copyTo(SimObject* object);
+
+   /// Declare Console Object.
+   DECLARE_CONOBJECT(GameObjectAsset);
+
+protected:
+   virtual void            initializeAsset(void) {}
+   virtual void            onAssetRefresh(void) {}
+};
+
+DefineConsoleType(TypeGameObjectAssetPtr, GameObjectAsset)
+
+#endif // _ASSET_BASE_H_
+

+ 4 - 2
Engine/source/T3D/assets/ShapeAsset.cpp

@@ -100,8 +100,6 @@ mAcquireReferenceCount(0),
 mpOwningAssetManager(NULL),
 mAssetInitialized(false)
 {
-   // Generate an asset definition.
-   mpAssetDefinition = new AssetDefinition();
 }
 
 //-----------------------------------------------------------------------------
@@ -154,4 +152,8 @@ void ShapeAsset::copyTo(SimObject* object)
 {
    // Call to parent.
    Parent::copyTo(object);
+}
+
+void ShapeAsset::onAssetRefresh(void)
+{
 }

+ 6 - 4
Engine/source/T3D/assets/ShapeAsset.h

@@ -19,8 +19,8 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
-#ifndef _SHAPE_ASSET_H_
-#define _SHAPE_ASSET_H_
+#ifndef SHAPE_ASSET_H
+#define SHAPE_ASSET_H
 
 #ifndef _ASSET_BASE_H_
 #include "assets/assetBase.h"
@@ -76,11 +76,13 @@ public:
 
    TSShape* getShape() { return mShape; }
 
+   Resource<TSShape> getShapeResource() { return mShape; }
+
 protected:
-   virtual void            onAssetRefresh(void) {}
+   virtual void            onAssetRefresh(void);
 };
 
 DefineConsoleType(TypeShapeAssetPtr, ShapeAsset)
 
-#endif // _ASSET_BASE_H_
+#endif
 

+ 717 - 0
Engine/source/T3D/components/animation/animationComponent.cpp

@@ -0,0 +1,717 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/animation/animationComponent.h"
+#include "T3D/components/animation/animationComponent_ScriptBinding.h"
+#include "T3D/components/render/meshComponent.h"
+
+#include "platform/platform.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "ts/tsShapeInstance.h"
+#include "core/stream/bitStream.h"
+#include "sim/netConnection.h"
+#include "gfx/gfxTransformSaver.h"
+#include "console/engineAPI.h"
+#include "lighting/lightQuery.h"
+#include "gfx/sim/debugDraw.h" 
+
+extern bool gEditingMission;
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_CALLBACK( AnimationComponent, onAnimationStart, void, ( Component* obj, const String& animName ), ( obj, animName ),
+                   "@brief Called when we collide with another object.\n\n"
+                   "@param obj The ShapeBase object\n"
+                   "@param collObj The object we collided with\n"
+                   "@param vec Collision impact vector\n"
+                   "@param len Length of the impact vector\n" );
+
+IMPLEMENT_CALLBACK(AnimationComponent, onAnimationEnd, void, (Component* obj, const char* animName), (obj, animName),
+                   "@brief Called when we collide with another object.\n\n"
+                   "@param obj The ShapeBase object\n"
+                   "@param collObj The object we collided with\n"
+                   "@param vec Collision impact vector\n"
+                   "@param len Length of the impact vector\n" );
+
+IMPLEMENT_CALLBACK(AnimationComponent, onAnimationTrigger, void, (Component* obj, const String& animName, S32 triggerID), (obj, animName, triggerID),
+                   "@brief Called when we collide with another object.\n\n"
+                   "@param obj The ShapeBase object\n"
+                   "@param collObj The object we collided with\n"
+                   "@param vec Collision impact vector\n"
+                   "@param len Length of the impact vector\n" );
+
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+AnimationComponent::AnimationComponent() : Component()
+{
+   mNetworked = true;
+   mNetFlags.set(Ghostable | ScopeAlways);
+
+   mFriendlyName = "Animation(Component)";
+   mComponentType = "Render";
+
+   mDescription = getDescriptionText("Allows a rendered mesh to be animated");
+
+   mOwnerRenderInst = NULL;
+
+   mOwnerShapeInstance = NULL;
+
+   for (U32 i = 0; i < MaxScriptThreads; i++)
+   {
+      mAnimationThreads[i].sequence = -1;
+      mAnimationThreads[i].thread = 0;
+      mAnimationThreads[i].sound = 0;
+      mAnimationThreads[i].state = Thread::Stop;
+      mAnimationThreads[i].atEnd = false;
+      mAnimationThreads[i].timescale = 1.f;
+      mAnimationThreads[i].position = -1.f;
+      mAnimationThreads[i].transition = true;
+   }
+}
+
+AnimationComponent::~AnimationComponent()
+{
+   for(S32 i = 0;i < mFields.size();++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(AnimationComponent);
+
+bool AnimationComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   //we need at least one layer
+   for (U32 i = 0; i < MaxScriptThreads; i++) 
+   {
+      Thread& st = mAnimationThreads[i];
+
+      if (st.sequence != -1) 
+      {
+         // TG: Need to see about suppressing non-cyclic sounds
+         // if the sequences were activated before the object was
+         // ghosted.
+         // TG: Cyclic animations need to have a random pos if
+         // they were started before the object was ghosted.
+
+         // If there was something running on the old shape, the thread
+         // needs to be reset. Otherwise we assume that it's been
+         // initialized either by the constructor or from the server.
+         bool reset = st.thread != 0;
+         st.thread = 0;
+
+         if (st.sequence != -1)
+         {
+            setThreadSequence(i, st.sequence, reset);
+         }
+      }
+
+      if (st.thread)
+         updateThread(st);
+   }
+
+   return true;
+}
+
+void AnimationComponent::onRemove()
+{
+   Parent::onRemove();
+
+   mOwnerRenderInst = NULL;
+}
+
+void AnimationComponent::onComponentAdd()
+{
+   //test if this is a shape component!
+   RenderComponentInterface *shapeInstanceInterface = mOwner->getComponent<RenderComponentInterface>();
+   if (shapeInstanceInterface)
+   {
+      shapeInstanceInterface->onShapeInstanceChanged.notify(this, &AnimationComponent::targetShapeChanged);
+      targetShapeChanged(shapeInstanceInterface);
+   }
+}
+
+void AnimationComponent::componentAddedToOwner(Component *comp)
+{
+   if (comp->getId() == getId())
+      return;
+
+   //test if this is a shape component!
+   RenderComponentInterface *shapeInstanceInterface = dynamic_cast<RenderComponentInterface*>(comp);
+   if (shapeInstanceInterface)
+   {
+      shapeInstanceInterface->onShapeInstanceChanged.notify(this, &AnimationComponent::targetShapeChanged);
+      targetShapeChanged(shapeInstanceInterface);
+   }
+}
+
+void AnimationComponent::componentRemovedFromOwner(Component *comp)
+{
+   if (comp->getId() == getId()) //?????????
+      return;
+
+   //test if this is a shape component!
+   RenderComponentInterface *shapeInstanceInterface = dynamic_cast<RenderComponentInterface*>(comp);
+   if (shapeInstanceInterface)
+   {
+      shapeInstanceInterface->onShapeInstanceChanged.remove(this, &AnimationComponent::targetShapeChanged);
+      mOwnerRenderInst = NULL;
+   }
+}
+
+void AnimationComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
+{
+   mOwnerRenderInst = instanceInterface;
+
+   if (!mOwnerRenderInst || !getShape())
+      return;
+
+   MeshComponent* meshComp = dynamic_cast<MeshComponent*>(mOwnerRenderInst);
+
+   mOwnerShapeInstance = meshComp->getShapeInstance();
+
+   if (!mOwnerShapeInstance)
+      return;
+
+   for (U32 i = 0; i < MaxScriptThreads; i++)
+   {
+      Thread& st = mAnimationThreads[i];
+
+      st.thread = mOwnerShapeInstance->addThread();
+   }
+}
+
+void AnimationComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+U32 AnimationComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   //early test if we lack an owner, ghost-wise
+   //no point in trying, just re-queue the mask and go
+   if (!mOwner || con->getGhostIndex(mOwner) == -1)
+   {
+      stream->writeFlag(false);
+      return retMask |= ThreadMask;
+   }
+   else
+   {
+      stream->writeFlag(true);
+
+      for (int i = 0; i < MaxScriptThreads; i++) 
+      {
+         Thread& st = mAnimationThreads[i];
+         if (stream->writeFlag( (st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)) ) ) 
+         {
+            stream->writeInt(st.sequence,ThreadSequenceBits);
+            stream->writeInt(st.state,2);
+            stream->write(st.timescale);
+            stream->write(st.position);
+            stream->writeFlag(st.atEnd);
+            stream->writeFlag(st.transition);
+         }
+      }
+   }
+
+   return retMask;
+}
+
+void AnimationComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if (stream->readFlag()) 
+   {
+      for (S32 i = 0; i < MaxScriptThreads; i++) 
+      {
+         if (stream->readFlag()) 
+         {
+            Thread& st = mAnimationThreads[i];
+            U32 seq = stream->readInt(ThreadSequenceBits);
+            st.state = stream->readInt(2);
+            stream->read( &st.timescale );
+            stream->read( &st.position );
+            st.atEnd = stream->readFlag();
+            bool transition = stream->readFlag();
+
+            if (!st.thread || st.sequence != seq && st.state != Thread::Destroy)
+               setThreadSequence(i, seq, false, transition);
+            else
+               updateThread(st);
+
+         }
+      }
+   }
+}
+void AnimationComponent::processTick()
+{
+   Parent::processTick();
+
+   if (!isActive())
+      return;
+
+   if (isServerObject()) 
+   {
+      // Server only...
+      advanceThreads(TickSec);
+   }
+}
+
+void AnimationComponent::advanceTime(F32 dt)
+{
+   Parent::advanceTime(dt);
+
+   // On the client, the shape threads and images are
+   // advanced at framerate.
+   advanceThreads(dt);
+}
+//
+const char *AnimationComponent::getThreadSequenceName(U32 slot)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence == -1)
+   {
+      // Invalid Animation.
+      return "";
+   }
+
+   // Name Index
+   TSShape* shape = getShape();
+
+   if (shape)
+   {
+      const U32 nameIndex = shape->sequences[st.sequence].nameIndex;
+
+      // Return Name.
+      return shape->getName(nameIndex);
+   }
+
+   return "";
+}
+
+bool AnimationComponent::setThreadSequence(U32 slot, S32 seq, bool reset, bool transition, F32 transTime)
+{
+   if (!mOwnerShapeInstance)
+      return false;
+
+   Thread& st = mAnimationThreads[slot];
+   if (st.thread && st.sequence == seq && st.state == Thread::Play && !reset)
+      return true;
+
+   // Handle a -1 sequence, as this may be set when a thread has been destroyed.
+   if (seq == -1)
+      return true;
+
+   if (seq < MaxSequenceIndex)
+   {
+      setMaskBits(-1);
+      setMaskBits(ThreadMaskN << slot);
+      st.sequence = seq;
+      st.transition = transition;
+
+      if (reset)
+      {
+         st.state = Thread::Play;
+         st.atEnd = false;
+         st.timescale = 1.f;
+         st.position = 0.f;
+      }
+
+      if (mOwnerShapeInstance)
+      {
+         if (!st.thread)
+            st.thread = mOwnerShapeInstance->addThread();
+
+         if (transition)
+         {
+            mOwnerShapeInstance->transitionToSequence(st.thread, seq, st.position, transTime, true);
+         }
+         else
+         {
+            mOwnerShapeInstance->setSequence(st.thread, seq, 0);
+            stopThreadSound(st);
+         }
+
+         updateThread(st);
+      }
+      return true;
+   }
+   return false;
+}
+
+S32 AnimationComponent::getThreadSequenceID(S32 slot)
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
+   {
+      return mAnimationThreads[slot].sequence;
+   }
+   else
+   {
+      return -1;
+   }
+}
+
+void AnimationComponent::updateThread(Thread& st)
+{
+   if (!mOwnerRenderInst)
+      return;
+
+   switch (st.state)
+   {
+      case Thread::Stop:
+      {
+         mOwnerShapeInstance->setTimeScale(st.thread, 1.f);
+         mOwnerShapeInstance->setPos(st.thread, (st.timescale > 0.f) ? 0.0f : 1.0f);
+      } // Drop through to pause state
+
+      case Thread::Pause:
+      {
+         if (st.position != -1.f)
+         {
+            mOwnerShapeInstance->setTimeScale(st.thread, 1.f);
+            mOwnerShapeInstance->setPos(st.thread, st.position);
+         }
+
+         mOwnerShapeInstance->setTimeScale(st.thread, 0.f);
+         stopThreadSound(st);
+      } break;
+
+      case Thread::Play:
+      {
+         if (st.atEnd)
+         {
+            mOwnerShapeInstance->setTimeScale(st.thread, 1);
+            mOwnerShapeInstance->setPos(st.thread, (st.timescale > 0.f) ? 1.0f : 0.0f);
+            mOwnerShapeInstance->setTimeScale(st.thread, 0);
+            stopThreadSound(st);
+            st.state = Thread::Stop;
+         }
+         else
+         {
+            if (st.position != -1.f)
+            {
+               mOwnerShapeInstance->setTimeScale(st.thread, 1.f);
+               mOwnerShapeInstance->setPos(st.thread, st.position);
+            }
+
+            mOwnerShapeInstance->setTimeScale(st.thread, st.timescale);
+            if (!st.sound)
+            {
+               startSequenceSound(st);
+            }
+         }
+      } break;
+
+      case Thread::Destroy:
+      {
+         stopThreadSound(st);
+         st.atEnd = true;
+         st.sequence = -1;
+         if (st.thread)
+         {
+            mOwnerShapeInstance->destroyThread(st.thread);
+            st.thread = 0;
+         }
+      } break;
+   }
+}
+
+bool AnimationComponent::stopThread(U32 slot)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1 && st.state != Thread::Stop) 
+   {
+      setMaskBits(ThreadMaskN << slot);
+      st.state = Thread::Stop;
+      updateThread(st);
+      return true;
+   }
+   return false;
+}
+
+bool AnimationComponent::destroyThread(U32 slot)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1 && st.state != Thread::Destroy) 
+   {
+      setMaskBits(ThreadMaskN << slot);
+      st.state = Thread::Destroy;
+      updateThread(st);
+      return true;
+   }
+   return false;
+}
+
+bool AnimationComponent::pauseThread(U32 slot)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1 && st.state != Thread::Pause) 
+   {
+      setMaskBits(ThreadMaskN << slot);
+      st.state = Thread::Pause;
+      updateThread(st);
+      return true;
+   }
+   return false;
+}
+
+bool AnimationComponent::playThread(U32 slot)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1 && st.state != Thread::Play)
+   {
+      setMaskBits(ThreadMaskN << slot);
+      st.state = Thread::Play;
+      updateThread(st);
+      return true;
+   }
+   return false;
+}
+
+bool AnimationComponent::playThread(U32 slot, const char* name, bool transition, F32 transitionTime)
+{
+   if (slot < AnimationComponent::MaxScriptThreads)
+   {
+      if (!dStrEqual(name, ""))
+      {
+         if (TSShape* shape = getShape())
+         {
+            S32 seq = shape->findSequence(name);
+            if (seq != -1 && setThreadSequence(slot, seq, true, transition, transitionTime))
+            {
+               return true;
+            }
+            else if (seq == -1)
+            {
+               //We tried to play a non-existaint sequence, so stop the thread just in case
+               destroyThread(slot);
+               return false;
+            }
+         }
+      }
+      else
+      {
+         if (playThread(slot))
+            return true;
+      }
+   }
+
+   return false;
+}
+
+bool AnimationComponent::setThreadAnimation(U32 slot, const char* name)
+{
+   if (slot < AnimationComponent::MaxScriptThreads)
+   {
+      if (!dStrEqual(name, ""))
+      {
+         if (TSShape* shape = getShape())
+         {
+            S32 seq = shape->findSequence(name);
+            if (seq != -1 && setThreadSequence(slot, seq, false, false))
+            {
+               Thread& st = mAnimationThreads[slot];
+               if (st.position == -1)
+                  st.position = 0;
+               //st.state = Thread::Pause;
+               return true;
+            }
+            else if (seq == -1)
+            {
+               //We tried to play a non-existaint sequence, so stop the thread just in case
+               destroyThread(slot);
+               return false;
+            }
+         }
+      }
+      else
+      {
+         if (playThread(slot))
+            return true;
+      }
+   }
+
+   return false;
+}
+
+bool AnimationComponent::setThreadPosition(U32 slot, F32 pos)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1)
+   {
+      setMaskBits(ThreadMaskN << slot);
+      st.position = pos;
+      st.atEnd = false;
+      updateThread(st);
+
+      return true;
+   }
+   return false;
+}
+
+bool AnimationComponent::setThreadDir(U32 slot, bool forward)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1)
+   {
+      if ((st.timescale >= 0.f) != forward)
+      {
+         setMaskBits(ThreadMaskN << slot);
+         st.timescale *= -1.f;
+         st.atEnd = false;
+         updateThread(st);
+      }
+      return true;
+   }
+   return false;
+}
+
+bool AnimationComponent::setThreadTimeScale(U32 slot, F32 timeScale)
+{
+   Thread& st = mAnimationThreads[slot];
+   if (st.sequence != -1)
+   {
+      if (st.timescale != timeScale)
+      {
+         setMaskBits(ThreadMaskN << slot);
+         st.timescale = timeScale;
+         updateThread(st);
+      }
+      return true;
+   }
+   return false;
+}
+
+void AnimationComponent::stopThreadSound(Thread& thread)
+{
+   return;
+}
+
+void AnimationComponent::startSequenceSound(Thread& thread)
+{
+   return;
+}
+
+void AnimationComponent::advanceThreads(F32 dt)
+{
+   if (!mOwnerRenderInst)
+      return;
+
+   for (U32 i = 0; i < MaxScriptThreads; i++)
+   {
+      Thread& st = mAnimationThreads[i];
+      if (st.thread && st.sequence != -1)
+      {
+         bool cyclic = getShape()->sequences[st.sequence].isCyclic();
+
+         if (!getShape()->sequences[st.sequence].isCyclic() &&
+            !st.atEnd &&
+            ((st.timescale > 0.f) ? mOwnerShapeInstance->getPos(st.thread) >= 1.0 : mOwnerShapeInstance->getPos(st.thread) <= 0))
+         {
+            st.atEnd = true;
+            updateThread(st);
+
+            if (!isGhost())
+            {
+               Con::executef(this, "onAnimationEnd", st.thread->getSequenceName());
+            }
+         }
+
+         // Make sure the thread is still valid after the call to onEndSequence_callback().
+         // Someone could have called destroyThread() while in there.
+         if (st.thread)
+         {
+            mOwnerShapeInstance->advanceTime(dt, st.thread);
+         }
+
+         if (mOwnerShapeInstance && !isGhost())
+         {
+            for (U32 i = 1; i < 32; i++)
+            {
+               if (mOwnerShapeInstance->getTriggerState(i))
+               {
+                  const char* animName = st.thread->getSequenceName().c_str();
+                  onAnimationTrigger_callback(this, animName, i);
+               }
+            }
+         }
+
+         if (isGhost())
+            mOwnerShapeInstance->animate();
+      }
+   }
+}
+
+TSShape* AnimationComponent::getShape()
+{
+   if (mOwner == NULL)
+      return NULL;
+
+   if (mOwnerRenderInst == NULL)
+      return NULL;
+
+   return mOwnerRenderInst->getShape();
+}
+
+S32 AnimationComponent::getAnimationCount()
+{
+   if (getShape())
+      return getShape()->sequences.size();
+   else
+      return 0;
+}
+
+S32 AnimationComponent::getAnimationIndex(const char* name)
+{
+   if (getShape())
+      return getShape()->findSequence(name);
+   else
+      return -1;
+}
+
+const char* AnimationComponent::getAnimationName(S32 index)
+{
+   if (getShape())
+   {
+      if (index >= 0 && index < getShape()->sequences.size())
+         return getShape()->getName(getShape()->sequences[index].nameIndex);
+   }
+
+   return "";
+}

+ 138 - 0
Engine/source/T3D/components/animation/animationComponent.h

@@ -0,0 +1,138 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef ANIMATION_COMPONENT_H
+#define ANIMATION_COMPONENT_H
+
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef _TSSHAPE_H_
+#include "ts/tsShapeInstance.h"
+#endif
+#ifndef ENTITY_H
+#include "T3D/entity.h"
+#endif
+#ifndef RENDER_COMPONENT_INTERFACE_H
+#include "T3D/components/render/renderComponentInterface.h"
+#endif
+
+class SceneRenderState;
+
+class AnimationComponent : public Component
+{
+   typedef Component Parent;
+public:
+   enum PublicConstants {
+      ThreadSequenceBits = 6,
+      MaxSequenceIndex = (1 << ThreadSequenceBits) - 1,
+      MaxScriptThreads = 16,            ///< Should be a power of 2
+   };
+
+   enum MaskBits {
+      ThreadMaskN = Parent::NextFreeMask << 0,
+      ThreadMask = (ThreadMaskN << MaxScriptThreads) - ThreadMaskN,
+      NextFreeMask = ThreadMaskN << MaxScriptThreads
+   };
+
+protected:
+
+   struct Thread 
+   {
+      /// State of the animation thread.
+      enum State 
+      {
+         Play, Stop, Pause, Destroy
+      };
+      TSThread* thread; ///< Pointer to 3space data.
+      U32 state;        ///< State of the thread
+      ///
+      ///  @see Thread::State
+      S32 sequence;     ///< The animation sequence which is running in this thread.
+      F32 timescale;    ///< Timescale
+      U32 sound;        ///< Handle to sound.
+      bool atEnd;       ///< Are we at the end of this thread?
+      F32 position;
+      bool transition;
+   };
+
+   Thread mAnimationThreads[MaxScriptThreads];
+
+protected:
+   RenderComponentInterface * mOwnerRenderInst;
+
+   TSShapeInstance *mOwnerShapeInstance;
+
+public:
+   AnimationComponent();
+   virtual ~AnimationComponent();
+   DECLARE_CONOBJECT(AnimationComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   TSShape* getShape();
+
+   void targetShapeChanged(RenderComponentInterface* instanceInterface);
+
+   virtual void processTick();
+   virtual void advanceTime(F32 dt);
+
+   const char *getThreadSequenceName(U32 slot);
+   bool setThreadSequence(U32 slot, S32 seq, bool reset = true, bool transition = true, F32 transitionTime = 0.5);
+   void updateThread(Thread& st);
+   bool stopThread(U32 slot);
+   bool destroyThread(U32 slot);
+   bool pauseThread(U32 slot);
+   bool playThread(U32 slot);
+   bool playThread(U32 slot, const char* name, bool transition, F32 transitionTime);
+   bool setThreadAnimation(U32 slot, const char* name);
+   bool setThreadPosition(U32 slot, F32 pos);
+   bool setThreadDir(U32 slot, bool forward);
+   bool setThreadTimeScale(U32 slot, F32 timeScale);
+   void stopThreadSound(Thread& thread);
+   void startSequenceSound(Thread& thread);
+   void advanceThreads(F32 dt);
+
+   S32 getThreadSequenceID(S32 slot);
+
+   //other helper functions
+   S32 getAnimationCount();
+   S32 getAnimationIndex(const char* name);
+   const char* getAnimationName(S32 index);
+
+   //callbacks
+   DECLARE_CALLBACK(void, onAnimationStart, (Component* obj, const String& animName));
+   DECLARE_CALLBACK(void, onAnimationEnd, (Component* obj, const char* animName));
+   DECLARE_CALLBACK(void, onAnimationTrigger, (Component* obj, const String& animName, S32 triggerID));
+};
+
+#endif //_ANIMATION_COMPONENT_H

+ 222 - 0
Engine/source/T3D/components/animation/animationComponent_ScriptBinding.h

@@ -0,0 +1,222 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "console/engineAPI.h"
+#include "T3D/components/animation/animationComponent.h"
+
+DefineEngineMethod(AnimationComponent, playThread, bool, (S32 slot, const char* name, bool transition, F32 transitionTime), (-1, "", true, 0.5),
+   "@brief Start a new animation thread, or restart one that has been paused or "
+   "stopped.\n\n"
+
+   "@param slot thread slot to play. Valid range is 0 - 3)\n"  // 3 = AnimationComponent::MaxScriptThreads-1
+   "@param name name of the animation sequence to play in this slot. If not "
+   "specified, the paused or stopped thread in this slot will be resumed.\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@tsexample\n"
+   "%obj.playThread( 0, \"ambient\" );      // Play the ambient sequence in slot 0\n"
+   "%obj.setThreadTimeScale( 0, 0.5 );    // Play at half-speed\n"
+   "%obj.pauseThread( 0 );                // Pause the sequence\n"
+   "%obj.playThread( 0 );                 // Resume playback\n"
+   "%obj.playThread( 0, \"spin\" );         // Replace the sequence in slot 0\n"
+   "@endtsexample\n"
+
+   "@see pauseThread()\n"
+   "@see stopThread()\n"
+   "@see setThreadDir()\n"
+   "@see setThreadTimeScale()\n"
+   "@see destroyThread()\n")
+{
+   return object->playThread(slot, name, transition, transitionTime);
+}
+
+DefineEngineMethod(AnimationComponent, setThreadDir, bool, (S32 slot, bool fwd), ,
+   "@brief Set the playback direction of an animation thread.\n\n"
+
+   "@param slot thread slot to modify\n"
+   "@param fwd true to play the animation forwards, false to play backwards\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@see playThread()\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads) 
+   {
+      if (object->setThreadDir(slot, fwd))
+         return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(AnimationComponent, setThreadTimeScale, bool, (S32 slot, F32 scale), ,
+   "@brief Set the playback time scale of an animation thread.\n\n"
+
+   "@param slot thread slot to modify\n"
+   "@param scale new thread time scale (1=normal speed, 0.5=half speed etc)\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@see playThread\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
+   {
+      if (object->setThreadTimeScale(slot, scale))
+         return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(AnimationComponent, setThreadPosition, bool, (S32 slot, F32 pos), ,
+   "@brief Set the position within an animation thread.\n\n"
+
+   "@param slot thread slot to modify\n"
+   "@param pos position within thread\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@see playThread\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads) 
+   {
+      if (object->setThreadPosition(slot, pos))
+         return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(AnimationComponent, setThreadAnimation, bool, (S32 slot, const char* name), (""),
+   "@brief Force-sets the animation in a particular thread without starting it playing."
+
+   "@param slot thread slot to play. Valid range is 0 - 3)\n"  // 3 = AnimationComponent::MaxScriptThreads-1
+   "@param name name of the animation sequence to play in this slot. If not "
+   "specified, the paused or stopped thread in this slot will be resumed.\n"
+   "@return true if successful, false if failed\n\n")
+{
+   return object->setThreadAnimation(slot, name);
+}
+
+DefineEngineMethod(AnimationComponent, getThreadAnimation, String, (S32 slot), ,
+   "@brief Force-sets the animation in a particular thread without starting it playing."
+
+   "@param slot thread slot to play. Valid range is 0 - 3)\n"  // 3 = AnimationComponent::MaxScriptThreads-1
+   "@param name name of the animation sequence to play in this slot. If not "
+   "specified, the paused or stopped thread in this slot will be resumed.\n"
+   "@return true if successful, false if failed\n\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads)
+   {
+      if (TSShape* shape = object->getShape())
+      {
+         S32 seq = object->getThreadSequenceID(slot);
+         if (seq != -1)
+         {
+            String animationName = object->getAnimationName(seq);
+            return animationName;
+         }
+      }
+   }
+
+   return "";
+}
+
+DefineEngineMethod(AnimationComponent, stopThread, bool, (S32 slot), ,
+   "@brief Stop an animation thread.\n\n"
+
+   "If restarted using playThread, the animation "
+   "will start from the beginning again.\n"
+   "@param slot thread slot to stop\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@see playThread\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads) 
+   {
+      if (object->stopThread(slot))
+         return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(AnimationComponent, destroyThread, bool, (S32 slot), ,
+   "@brief Destroy an animation thread, which prevents it from playing.\n\n"
+
+   "@param slot thread slot to destroy\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@see playThread\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads) 
+   {
+      if (object->destroyThread(slot))
+         return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(AnimationComponent, pauseThread, bool, (S32 slot), ,
+   "@brief Pause an animation thread.\n\n"
+
+   "If restarted using playThread, the animation "
+   "will resume from the paused position.\n"
+   "@param slot thread slot to stop\n"
+   "@return true if successful, false if failed\n\n"
+
+   "@see playThread\n")
+{
+   if (slot >= 0 && slot < AnimationComponent::MaxScriptThreads) 
+   {
+      if (object->pauseThread(slot))
+         return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(AnimationComponent, getAnimationCount, S32, (), ,
+   "Get the total number of sequences in the shape.\n"
+   "@return the number of sequences in the shape\n\n")
+{
+   return object->getAnimationCount();
+}
+
+DefineEngineMethod(AnimationComponent, getAnimationIndex, S32, (const char* name), ,
+   "Find the index of the sequence with the given name.\n"
+   "@param name name of the sequence to lookup\n"
+   "@return index of the sequence with matching name, or -1 if not found\n\n"
+   "@tsexample\n"
+   "// Check if a given sequence exists in the shape\n"
+   "if ( %this.getSequenceIndex( \"walk\" ) == -1 )\n"
+   "   echo( \"Could not find 'walk' sequence\" );\n"
+   "@endtsexample\n")
+{
+   return object->getAnimationIndex(name);
+}
+
+DefineEngineMethod(AnimationComponent, getAnimationName, const char*, (S32 index), ,
+   "Get the name of the indexed sequence.\n"
+   "@param index index of the sequence to query (valid range is 0 - getSequenceCount()-1)\n"
+   "@return the name of the sequence\n\n"
+   "@tsexample\n"
+   "// print the name of all sequences in the shape\n"
+   "%count = %this.getSequenceCount();\n"
+   "for ( %i = 0; %i < %count; %i++ )\n"
+   "   echo( %i SPC %this.getSequenceName( %i ) );\n"
+   "@endtsexample\n")
+{
+   return object->getAnimationName(index);
+}

+ 483 - 0
Engine/source/T3D/components/camera/cameraComponent.cpp

@@ -0,0 +1,483 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/camera/cameraComponent.h"
+#include "T3D/components/camera/cameraComponent_ScriptBinding.h"
+#include "platform/platform.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "ts/tsShapeInstance.h"
+#include "core/stream/bitStream.h"
+#include "gfx/gfxTransformSaver.h"
+#include "console/engineAPI.h"
+#include "lighting/lightQuery.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "T3D/gameFunctions.h"
+#include "math/mathUtils.h"
+#include "T3D/components/render/renderComponentInterface.h"
+
+IMPLEMENT_CALLBACK( CameraComponent, validateCameraFov, F32, (F32 fov), (fov),
+                   "@brief Called on the server when the client has requested a FOV change.\n\n"
+
+                   "When the client requests that its field of view should be changed (because "
+                   "they want to use a sniper scope, for example) this new FOV needs to be validated "
+                   "by the server.  This method is called if it exists (it is optional) to validate "
+                   "the requested FOV, and modify it if necessary.  This could be as simple as checking "
+                   "that the FOV falls within a correct range, to making sure that the FOV matches the "
+                   "capabilities of the current weapon.\n\n"
+
+                   "Following this method, ShapeBase ensures that the given FOV still falls within "
+                   "the datablock's mCameraMinFov and mCameraMaxFov.  If that is good enough for your "
+                   "purposes, then you do not need to define the validateCameraFov() callback for "
+                   "your ShapeBase.\n\n"
+
+                   "@param fov The FOV that has been requested by the client.\n"
+                   "@return The FOV as validated by the server.\n\n"
+
+                   "@see ShapeBaseData\n\n");
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+
+CameraComponent::CameraComponent() : Component()
+{
+   mClientScreen = Point2F(1, 1);
+
+   mCameraFov = mCameraDefaultFov = 80;
+   mCameraMinFov = 5;
+   mCameraMaxFov = 175;
+
+   mTargetNodeIdx = -1;
+
+   mPosOffset = Point3F(0, 0, 0);
+   mRotOffset = EulerF(0, 0, 0);
+
+   mTargetNode = "";
+
+   mUseParentTransform = true;
+
+   mFriendlyName = "Camera(Component)";
+}
+
+CameraComponent::~CameraComponent()
+{
+   for(S32 i = 0;i < mFields.size();++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(CameraComponent);
+
+bool CameraComponent::onAdd()
+{
+   if(! Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void CameraComponent::onRemove()
+{
+   Parent::onRemove();
+}
+
+void CameraComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addProtectedField("FOV", TypeF32, Offset(mCameraFov, CameraComponent), &_setCameraFov, defaultProtectedGetFn, "");
+
+   addField("MinFOV", TypeF32, Offset(mCameraMinFov, CameraComponent), "");
+
+   addField("MaxFOV", TypeF32, Offset(mCameraMaxFov, CameraComponent), "");
+
+   addField("ScreenAspect", TypePoint2I, Offset(mClientScreen, CameraComponent), "");
+
+   addProtectedField("targetNode", TypeString, Offset(mTargetNode, CameraComponent), &_setNode, defaultProtectedGetFn, "");
+
+   addProtectedField("positionOffset", TypePoint3F, Offset(mPosOffset, CameraComponent), &_setPosOffset, defaultProtectedGetFn, "");
+
+   addProtectedField("rotationOffset", TypeRotationF, Offset(mRotOffset, CameraComponent), &_setRotOffset, defaultProtectedGetFn, "");
+
+   addField("useParentTransform", TypeBool, Offset(mUseParentTransform, CameraComponent), "");
+}
+
+bool CameraComponent::_setNode(void *object, const char *index, const char *data)
+{
+   CameraComponent *mcc = static_cast<CameraComponent*>(object);
+   
+   mcc->mTargetNode = StringTable->insert(data);
+   mcc->setMaskBits(OffsetMask);
+
+   return true;
+}
+
+bool CameraComponent::_setPosOffset(void *object, const char *index, const char *data)
+{
+   CameraComponent *mcc = static_cast<CameraComponent*>(object);
+   
+   if (mcc)
+   {
+      Point3F pos;
+      Con::setData(TypePoint3F, &pos, 0, 1, &data);
+
+      mcc->mPosOffset = pos;
+      mcc->setMaskBits(OffsetMask);
+
+      return true;
+   }
+
+   return false;
+}
+
+bool CameraComponent::_setRotOffset(void *object, const char *index, const char *data)
+{
+   CameraComponent *mcc = static_cast<CameraComponent*>(object);
+
+   if (mcc)
+   {
+      RotationF rot;
+      Con::setData(TypeRotationF, &rot, 0, 1, &data);
+
+      mcc->mRotOffset = rot;
+      mcc->setMaskBits(OffsetMask);
+
+      return true;
+   }
+
+   return false;
+}
+
+bool CameraComponent::isValidCameraFov(F32 fov)
+{
+   return((fov >= mCameraMinFov) && (fov <= mCameraMaxFov));
+}
+
+bool CameraComponent::_setCameraFov(void *object, const char *index, const char *data)
+{
+   CameraComponent *cCI = static_cast<CameraComponent*>(object);
+   cCI->setCameraFov(dAtof(data));
+   return true;
+}
+
+void CameraComponent::setCameraFov(F32 fov)
+{
+   mCameraFov = mClampF(fov, mCameraMinFov, mCameraMaxFov);
+
+   if (isClientObject())
+      GameSetCameraTargetFov(mCameraFov);
+
+   if (isServerObject())
+      setMaskBits(FOVMask);
+}
+
+void CameraComponent::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)
+{
+   // update the camera query
+   query->camera = this;
+
+   if(GameConnection * con = dynamic_cast<GameConnection*>(cr))
+   {
+      // get the fov from the connection (in deg)
+      F32 fov;
+      if (con->getControlCameraFov(&fov))
+      {
+         query->fov = mDegToRad(fov/2);
+         query->sinFov = mSin(query->fov);
+         query->cosFov = mCos(query->fov);
+      }
+      else
+      {
+         query->fov = mDegToRad(mCameraFov/2);
+         query->sinFov = mSin(query->fov);
+         query->cosFov = mCos(query->fov);
+      }
+   }
+
+   // use eye rather than camera transform (good enough and faster)
+   MatrixF camTransform = mOwner->getTransform();
+   camTransform.getColumn(3, &query->pos);
+   camTransform.getColumn(1, &query->orientation);
+
+   // Get the visible distance.
+   if (mOwner->getSceneManager() != NULL)
+      query->visibleDistance = mOwner->getSceneManager()->getVisibleDistance();
+}
+
+bool CameraComponent::getCameraTransform(F32* pos,MatrixF* mat)
+{
+   // Returns camera to world space transform
+   // Handles first person / third person camera position
+   bool isServer = isServerObject();
+
+   if (mTargetNodeIdx == -1)
+   {
+      if (mUseParentTransform)
+      {
+         MatrixF rMat = mOwner->getRenderTransform();
+
+         rMat.mul(mRotOffset.asMatrixF());
+         
+         mat->set(rMat.toEuler(), rMat.getPosition() + mPosOffset);
+      }
+      else
+      {
+         mat->set(mRotOffset.asEulerF(), mPosOffset);
+      }
+
+      return true;
+   }
+   else
+   {
+      RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
+
+      if (!renderInterface)
+         return false;
+
+      if (mUseParentTransform)
+      {
+         MatrixF rMat = mOwner->getRenderTransform();
+
+         Point3F position = rMat.getPosition();
+
+         RotationF rot = mRotOffset;
+
+         if (mTargetNodeIdx != -1)
+         {
+            Point3F nodPos;
+            MatrixF nodeTrans = renderInterface->getNodeTransform(mTargetNodeIdx);
+            nodeTrans.getColumn(3, &nodPos);
+
+            // Scale the camera position before applying the transform
+            const Point3F& scale = mOwner->getScale();
+            nodPos.convolve(scale);
+
+            mOwner->getRenderTransform().mulP(nodPos, &position);
+
+            nodeTrans.mul(rMat);
+
+            rot = nodeTrans;
+         }
+
+         position += mPosOffset;
+
+         MatrixF rotMat = rot.asMatrixF();
+
+         MatrixF rotOffsetMat = mRotOffset.asMatrixF();
+
+         rotMat.mul(rotOffsetMat);
+
+         rot = RotationF(rotMat);
+
+         mat->set(rot.asEulerF(), position);
+      }
+      else
+      {
+         MatrixF rMat = mOwner->getRenderTransform();
+
+         Point3F position = rMat.getPosition();
+
+         RotationF rot = mRotOffset;
+
+         if (mTargetNodeIdx != -1)
+         {
+            Point3F nodPos;
+            MatrixF nodeTrans = renderInterface->getNodeTransform(mTargetNodeIdx);
+            nodeTrans.getColumn(3, &nodPos);
+
+            // Scale the camera position before applying the transform
+            const Point3F& scale = mOwner->getScale();
+            nodPos.convolve(scale);
+
+            position = nodPos;
+         }
+
+         position += mPosOffset;
+
+         mat->set(rot.asEulerF(), position);
+      }
+
+      return true;
+   }
+}
+
+void CameraComponent::getCameraParameters(F32 *min, F32* max, Point3F* off, MatrixF* rot)
+{
+   *min = 0.2f;
+   *max = 0.f;
+   off->set(0, 0, 0);
+   rot->identity();
+}
+
+U32 CameraComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retmask = Parent::packUpdate(con, mask, stream);
+
+   if (stream->writeFlag(mask & FOVMask))
+   {
+      stream->write(mCameraFov);
+   }
+
+   if (stream->writeFlag(mask & OffsetMask))
+   {
+      RenderComponentInterface* renderInterface = getOwner()->getComponent<RenderComponentInterface>();
+
+      if (renderInterface && renderInterface->getShape())
+      {
+         S32 nodeIndex = renderInterface->getShape()->findNode(mTargetNode);
+
+         mTargetNodeIdx = nodeIndex;
+      }
+
+      stream->writeInt(mTargetNodeIdx, 32);
+      //send offsets here
+
+      stream->writeCompressedPoint(mPosOffset);
+      stream->writeCompressedPoint(mRotOffset.asEulerF());
+
+      stream->writeFlag(mUseParentTransform);
+   }
+
+   return retmask;
+}
+
+void CameraComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if (stream->readFlag())
+   {
+      F32 fov;
+      stream->read(&fov);
+      setCameraFov(fov);
+   }
+
+   if(stream->readFlag())
+   {
+      mTargetNodeIdx = stream->readInt(32);
+
+      stream->readCompressedPoint(&mPosOffset);
+
+      EulerF rot;
+      stream->readCompressedPoint(&rot);
+
+      mRotOffset = RotationF(rot);
+
+      mUseParentTransform = stream->readFlag();
+   }
+}
+
+void CameraComponent::setForwardVector(VectorF newForward, VectorF upVector)
+{
+   MatrixF mat;
+   F32 pos = 0;
+   getCameraTransform(&pos, &mat);
+
+   mPosOffset = mat.getPosition();
+
+   VectorF up(0.0f, 0.0f, 1.0f);
+   VectorF axisX;
+   VectorF axisY = newForward;
+   VectorF axisZ;
+
+   if (upVector != VectorF::Zero)
+      up = upVector;
+
+   // Validate and normalize input:  
+   F32 lenSq;
+   lenSq = axisY.lenSquared();
+   if (lenSq < 0.000001f)
+   {
+      axisY.set(0.0f, 1.0f, 0.0f);
+      Con::errorf("Entity::setForwardVector() - degenerate forward vector");
+   }
+   else
+   {
+      axisY /= mSqrt(lenSq);
+   }
+
+   lenSq = up.lenSquared();
+   if (lenSq < 0.000001f)
+   {
+      up.set(0.0f, 0.0f, 1.0f);
+      Con::errorf("SceneObject::setForwardVector() - degenerate up vector - too small");
+   }
+   else
+   {
+      up /= mSqrt(lenSq);
+   }
+
+   if (fabsf(mDot(up, axisY)) > 0.9999f)
+   {
+      Con::errorf("SceneObject::setForwardVector() - degenerate up vector - same as forward");
+      // i haven't really tested this, but i think it generates something which should be not parallel to the previous vector:  
+      F32 tmp = up.x;
+      up.x = -up.y;
+      up.y = up.z;
+      up.z = tmp;
+   }
+
+   // construct the remaining axes:  
+   mCross(axisY, up, &axisX);
+   mCross(axisX, axisY, &axisZ);
+
+   mat.setColumn(0, axisX);
+   mat.setColumn(1, axisY);
+   mat.setColumn(2, axisZ);
+
+   mRotOffset = RotationF(mat.toEuler());
+   mRotOffset.y = 0;
+
+   setMaskBits(OffsetMask);
+}
+
+void CameraComponent::setPosition(Point3F newPos)
+{
+   mPosOffset = newPos;
+   setMaskBits(OffsetMask);
+}
+
+void CameraComponent::setRotation(RotationF newRot)
+{
+   mRotOffset = newRot;
+   setMaskBits(OffsetMask);
+}
+
+Frustum CameraComponent::getFrustum()
+{
+   Frustum visFrustum;
+   F32 left, right, top, bottom;
+   F32 aspectRatio = mClientScreen.x / mClientScreen.y;
+
+   visFrustum.set(false, mDegToRad(mCameraFov), aspectRatio, 0.1f, 1000, mOwner->getTransform());
+
+   return visFrustum;
+}

+ 159 - 0
Engine/source/T3D/components/camera/cameraComponent.h

@@ -0,0 +1,159 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef CAMERA_COMPONENT_H
+#define CAMERA_COMPONENT_H
+
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+#ifndef _MBOX_H_
+#include "math/mBox.h"
+#endif
+#ifndef ENTITY_H
+#include "T3D/entity.h"
+#endif
+#ifndef CORE_INTERFACES_H
+#include "T3D/components/coreInterfaces.h"
+#endif
+
+class SceneRenderState;
+struct CameraScopeQuery;
+
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class CameraComponent : public Component, public CameraInterface
+{
+   typedef Component Parent;
+
+   F32  mCameraFov;           ///< The camera vertical FOV in degrees.
+
+   Point2F mClientScreen;     ///< The dimensions of the client's screen. Used to calculate the aspect ratio.
+
+   F32 mCameraDefaultFov;            ///< Default vertical FOV in degrees.
+   F32 mCameraMinFov;                ///< Min vertical FOV allowed in degrees.
+   F32 mCameraMaxFov;                ///< Max vertical FOV allowed in degrees.
+
+protected:
+   Point3F mPosOffset;
+   RotationF mRotOffset;
+
+   StringTableEntry mTargetNode;
+   S32 mTargetNodeIdx;
+
+   bool mUseParentTransform;
+
+   enum
+   {
+      FOVMask = Parent::NextFreeMask,
+      OffsetMask = Parent::NextFreeMask << 1,
+      NextFreeMask = Parent::NextFreeMask << 2,
+   };
+
+public:
+   CameraComponent();
+   virtual ~CameraComponent();
+   DECLARE_CONOBJECT(CameraComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   static bool _setCameraFov(void *object, const char *index, const char *data);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   static bool _setNode(void *object, const char *index, const char *data);
+   static bool _setPosOffset(void *object, const char *index, const char *data);
+   static bool _setRotOffset(void *object, const char *index, const char *data);
+
+   void setRotOffset(RotationF rot)
+   {
+      mRotOffset = rot;
+      setMaskBits(OffsetMask);
+   }
+
+   RotationF getRotOffset()
+   {
+      return mRotOffset;
+   }
+
+   Point3F getPosOffset()
+   {
+      return mPosOffset;
+   }
+
+   /// Gets the minimum viewing distance, maximum viewing distance, camera offsetand rotation
+   /// for this object, if the world were to be viewed through its eyes
+   /// @param   min   Minimum viewing distance
+   /// @param   max   Maximum viewing distance
+   /// @param   offset Offset of the camera from the origin in local space
+   /// @param   rot   Rotation matrix
+   virtual void getCameraParameters(F32 *min, F32* max, Point3F* offset, MatrixF* rot);
+
+   /// Gets the camera to world space transform matrix
+   /// @todo Find out what pos does
+   /// @param   pos   TODO: Find out what this does
+   /// @param   mat   Camera transform (out)
+   virtual bool getCameraTransform(F32* pos, MatrixF* mat);
+
+   /// Returns the vertical field of view in degrees for 
+   /// this object if used as a camera.
+   virtual F32 getCameraFov() { return mCameraFov; }
+
+   /// Returns the default vertical field of view in degrees
+   /// if this object is used as a camera.
+   virtual F32 getDefaultCameraFov() { return mCameraDefaultFov; }
+
+   /// Sets the vertical field of view in degrees for this 
+   /// object if used as a camera.
+   /// @param   yfov  The vertical FOV in degrees to test.
+   virtual void setCameraFov(F32 fov);
+
+   /// Returns true if the vertical FOV in degrees is within 
+   /// allowable parameters of the datablock.
+   /// @param   yfov  The vertical FOV in degrees to test.
+   /// @see ShapeBaseData::cameraMinFov
+   /// @see ShapeBaseData::cameraMaxFov
+   virtual bool isValidCameraFov(F32 fov);
+   /// @}
+
+   virtual Frustum getFrustum();
+
+   /// Control object scoping
+   void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);
+
+   void setForwardVector(VectorF newForward, VectorF upVector = VectorF::Zero);
+   void setPosition(Point3F newPos);
+   void setRotation(RotationF newRot);
+
+protected:
+   DECLARE_CALLBACK(F32, validateCameraFov, (F32 fov));
+};
+
+#endif // CAMERA_BEHAVIOR_H

+ 91 - 0
Engine/source/T3D/components/camera/cameraComponent_ScriptBinding.h

@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "console/engineAPI.h"
+#include "T3D/components/camera/cameraComponent.h"
+
+//Basically, this only exists for backwards compatibility for parts of the editors
+ConsoleMethod(CameraComponent, getMode, const char*, 2, 2, "() - We get the first behavior of the requested type on our owner object.\n"
+   "@return (string name) The type of the behavior we're requesting")
+{
+   return "fly";
+}
+
+DefineConsoleMethod(CameraComponent, getForwardVector, VectorF, (), ,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   F32 pos = 0;
+   MatrixF cameraMat;
+   object->getCameraTransform(&pos, &cameraMat);
+
+   VectorF returnVec = cameraMat.getForwardVector();
+   returnVec = VectorF(mRadToDeg(returnVec.x), mRadToDeg(returnVec.y), mRadToDeg(returnVec.z));
+   returnVec.normalize();
+   return returnVec;
+}
+
+DefineConsoleMethod(CameraComponent, getRightVector, VectorF, (), ,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   F32 pos = 0;
+   MatrixF cameraMat;
+   object->getCameraTransform(&pos, &cameraMat);
+
+   VectorF returnVec = cameraMat.getRightVector();
+   returnVec = VectorF(mRadToDeg(returnVec.x), mRadToDeg(returnVec.y), mRadToDeg(returnVec.z));
+   returnVec.normalize();
+   return returnVec;
+}
+
+DefineConsoleMethod(CameraComponent, getUpVector, VectorF, (), ,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   F32 pos = 0;
+   MatrixF cameraMat;
+   object->getCameraTransform(&pos, &cameraMat);
+
+   VectorF returnVec = cameraMat.getUpVector();
+   returnVec = VectorF(mRadToDeg(returnVec.x), mRadToDeg(returnVec.y), mRadToDeg(returnVec.z));
+   returnVec.normalize();
+   return returnVec;
+}
+
+DefineConsoleMethod(CameraComponent, setForwardVector, void, (VectorF newForward), (VectorF(0, 0, 0)),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   object->setForwardVector(newForward);
+}
+
+DefineConsoleMethod(CameraComponent, getWorldPosition, Point3F, (), ,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   F32 pos = 0;
+   MatrixF mat;
+   object->getCameraTransform(&pos, &mat);
+
+   return mat.getPosition();
+}

+ 172 - 0
Engine/source/T3D/components/camera/cameraOrbiterComponent.cpp

@@ -0,0 +1,172 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/camera/cameraOrbiterComponent.h"
+#include "core/util/safeDelete.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "core/stream/bitStream.h"
+#include "console/engineAPI.h"
+#include "sim/netConnection.h"
+#include "math/mathUtils.h"
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+CameraOrbiterComponent::CameraOrbiterComponent() : Component()
+{
+   mMinOrbitDist = 0.0f;
+   mMaxOrbitDist = 0.0f;
+   mCurOrbitDist = 8.0f;
+   mPosition.set(0.0f, 0.0f, 0.0f);
+
+   mMaxPitchAngle = 70;
+   mMinPitchAngle = -10;
+
+   mRotation.set(0, 0, 0);
+
+   mCamera = NULL;
+}
+
+CameraOrbiterComponent::~CameraOrbiterComponent()
+{
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(CameraOrbiterComponent);
+
+bool CameraOrbiterComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void CameraOrbiterComponent::onRemove()
+{
+   Parent::onRemove();
+}
+void CameraOrbiterComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField("orbitDistance", TypeF32, Offset(mCurOrbitDist, CameraOrbiterComponent), "Object world orientation.");
+   addField("Rotation", TypeRotationF, Offset(mRotation, CameraOrbiterComponent), "Object world orientation.");
+   addField("maxPitchAngle", TypeF32, Offset(mMaxPitchAngle, CameraOrbiterComponent), "Object world orientation.");
+   addField("minPitchAngle", TypeF32, Offset(mMinPitchAngle, CameraOrbiterComponent), "Object world orientation.");
+}
+
+//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
+void CameraOrbiterComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   CameraComponent *cam = mOwner->getComponent<CameraComponent>();
+   if (cam)
+   {
+      mCamera = cam;
+   }
+}
+
+void CameraOrbiterComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+}
+
+void CameraOrbiterComponent::componentAddedToOwner(Component *comp)
+{
+   if (comp->getId() == getId())
+      return;
+
+   //test if this is a shape component!
+   CameraComponent *camComponent = dynamic_cast<CameraComponent*>(comp);
+   if (camComponent)
+   {
+      mCamera = camComponent;
+   }
+}
+
+void CameraOrbiterComponent::componentRemovedFromOwner(Component *comp)
+{
+   if (comp->getId() == getId()) //?????????
+      return;
+
+   //test if this is a shape component!
+   CameraComponent *camComponent = dynamic_cast<CameraComponent*>(comp);
+   if (camComponent)
+   {
+      mCamera = NULL;
+   }
+}
+
+U32 CameraOrbiterComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+   return retMask;
+}
+
+void CameraOrbiterComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+}
+
+void CameraOrbiterComponent::processTick()
+{
+   Parent::processTick();
+
+   if (!mOwner)
+      return;
+
+   if (mCamera)
+   {
+      //Clamp our pitch to whatever range we allow, first.
+      mRotation.x = mClampF(mRotation.x, mDegToRad(mMinPitchAngle), mDegToRad(mMaxPitchAngle));
+
+      MatrixF ownerTrans = mOwner->getRenderTransform();
+      Point3F ownerPos = ownerTrans.getPosition();
+
+      Point3F pos;
+      pos.x = mCurOrbitDist * mSin(mRotation.x + M_HALFPI_F) * mCos(-1.0f * (mRotation.z + M_HALFPI_F));
+      pos.y = mCurOrbitDist * mSin(mRotation.x + M_HALFPI_F) * mSin(-1.0f * (mRotation.z + M_HALFPI_F));
+      pos.z = mCurOrbitDist * mSin(mRotation.x);
+
+      //orient the camera towards the owner
+      VectorF ownerVec = ownerPos - pos;
+      ownerVec.normalize();
+
+      MatrixF xRot, zRot, cameraMatrix;
+      xRot.set(EulerF(mRotation.x, 0.0f, 0.0f));
+      zRot.set(EulerF(0.0f, 0.0f, mRotation.z));
+
+      cameraMatrix.mul(zRot, xRot);
+      cameraMatrix.getColumn(1, &ownerVec);
+      cameraMatrix.setColumn(3, pos - ownerVec * pos);
+
+      RotationF camRot = RotationF(cameraMatrix);
+
+      if (camRot != mCamera->getRotOffset())
+         mCamera->setRotation(camRot);
+
+      if (pos != mCamera->getPosOffset())
+         mCamera->setPosition(pos);
+   }
+}

+ 42 - 28
Engine/source/component/moreAdvancedComponent.cpp → Engine/source/T3D/components/camera/cameraOrbiterComponent.h

@@ -20,41 +20,55 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
-#include "component/moreAdvancedComponent.h"
+#ifndef CAMERA_ORBITER_COMPONENT_H
+#define CAMERA_ORBITER_COMPONENT_H
 
-// unitTest_runTests("Component/MoreAdvancedComponent");
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef CAMERA_COMPONENT_H
+#include "T3D/components/camera/cameraComponent.h"
+#endif
 
 //////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class CameraOrbiterComponent : public Component
+{
+   typedef Component Parent;
 
-IMPLEMENT_CONOBJECT(MoreAdvancedComponent);
+   F32 mMinOrbitDist;
+   F32 mMaxOrbitDist;
+   F32 mCurOrbitDist;
+   Point3F mPosition;
 
-ConsoleDocClass( MoreAdvancedComponent,
-				"@brief This is a slightly more advanced component which will be used to demonstrate "
-				"components which are dependent on other components.\n\n"
-				"Not intended for game development, for editors or internal use only.\n\n "
-				"@internal");
+   F32 mMaxPitchAngle;
+   F32 mMinPitchAngle;
 
-bool MoreAdvancedComponent::onComponentRegister( SimComponent *owner )
-{
-   if( !Parent::onComponentRegister( owner ) )
-      return false;
+   RotationF mRotation;
 
-   // This will return the first interface of type SimpleComponent that is cached
-   // on the parent object. 
-   mSCInterface = owner->getInterface<SimpleComponentInterface>();
+   CameraComponent* mCamera;
 
-   // If we can't find this interface, our component can't function, so false
-   // will be returned, and this will signify, to the top-level component, that it
-   // should fail the onAdd call.
-   return ( mSCInterface != NULL );
-}
+public:
+   CameraOrbiterComponent();
+   virtual ~CameraOrbiterComponent();
+   DECLARE_CONOBJECT(CameraOrbiterComponent);
 
-bool MoreAdvancedComponent::testDependentInterface()
-{
-   // These two requirements must be met in order for the test to proceed, so
-   // lets check them.
-   if( mSCInterface == NULL || !mSCInterface->isValid() )
-      return false;
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void processTick();
+};
 
-   return mSCInterface->isFortyTwo( 42 );
-}
+#endif // EXAMPLEBEHAVIOR_H

+ 582 - 0
Engine/source/T3D/components/collision/collisionComponent.cpp

@@ -0,0 +1,582 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/collision/collisionComponent.h"
+#include "T3D/components/collision/collisionComponent_ScriptBinding.h"
+#include "T3D/components/physics/physicsBehavior.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "core/stream/bitStream.h"
+#include "scene/sceneRenderState.h"
+#include "gfx/gfxTransformSaver.h"
+#include "gfx/gfxDrawUtil.h"
+#include "console/engineAPI.h"
+#include "T3D/physics/physicsPlugin.h"
+#include "T3D/physics/physicsBody.h"
+#include "T3D/physics/physicsCollision.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "collision/extrudedPolyList.h"
+#include "math/mathIO.h"
+#include "gfx/sim/debugDraw.h"  
+#include "collision/concretePolyList.h"
+
+#include "T3D/trigger.h"
+#include "opcode/Opcode.h"
+#include "opcode/Ice/IceAABB.h"
+#include "opcode/Ice/IcePoint.h"
+#include "opcode/OPC_AABBTree.h"
+#include "opcode/OPC_AABBCollider.h"
+
+#include "math/mathUtils.h"
+#include "materials/baseMatInstance.h"
+#include "collision/vertexPolyList.h"
+
+extern bool gEditingMission;
+
+static bool sRenderColliders = false;
+
+//Docs
+ConsoleDocClass(CollisionComponent,
+   "@brief The Box Collider component uses a box or rectangular convex shape for collisions.\n\n"
+
+   "Colliders are individualized components that are similarly based off the CollisionInterface core.\n"
+   "They are basically the entire functionality of how Torque handles collisions compacted into a single component.\n"
+   "A collider will both collide against and be collided with, other entities.\n"
+   "Individual colliders will offer different shapes. This box collider will generate a box/rectangle convex, \n"
+   "while the mesh collider will take the owner Entity's rendered shape and do polysoup collision on it, etc.\n\n"
+
+   "The general flow of operations for how collisions happen is thus:\n"
+   "  -When the component is added(or updated) prepCollision() is called.\n"
+   "    This will set up our initial convex shape for usage later.\n\n"
+
+   "  -When we update via processTick(), we first test if our entity owner is mobile.\n"
+   "    If our owner isn't mobile(as in, they have no components that provide it a velocity to move)\n"
+   "    then we skip doing our active collision checks. Collisions are checked by the things moving, as\n"
+   "    opposed to being reactionary. If we're moving, we call updateWorkingCollisionSet().\n"
+   "    updateWorkingCollisionSet() estimates our bounding space for our current ticket based on our position and velocity.\n"
+   "    If our bounding space has changed since the last tick, we proceed to call updateWorkingList() on our convex.\n"
+   "    This notifies any object in the bounding space that they may be collided with, so they will call buildConvex().\n"
+   "    buildConvex() will set up our ConvexList with our collision convex info.\n\n"
+
+   "  -When the component that is actually causing our movement, such as SimplePhysicsBehavior, updates, it will check collisions.\n"
+   "    It will call checkCollisions() on us. checkCollisions() will first build a bounding shape for our convex, and test\n"
+   "    if we can early out because we won't hit anything based on our starting point, velocity, and tick time.\n"
+   "    If we don't early out, we proceed to call updateCollisions(). This builds an ExtrudePolyList, which is then extruded\n"
+   "    based on our velocity. We then test our extruded polies on our working list of objects we build\n"
+   "    up earlier via updateWorkingCollisionSet. Any collisions that happen here will be added to our mCollisionList.\n"
+   "    Finally, we call handleCollisionList() on our collisionList, which then queues out the colliison notice\n"
+   "    to the object(s) we collided with so they can do callbacks and the like. We also report back on if we did collide\n"
+   "    to the physics component via our bool return in checkCollisions() so it can make the physics react accordingly.\n\n"
+
+   "One interesting point to note is the usage of mBlockColliding.\n"
+   "This is set so that it dictates the return on checkCollisions(). If set to false, it will ensure checkCollisions()\n"
+   "will return false, regardless if we actually collided. This is useful, because even if checkCollisions() returns false,\n"
+   "we still handle the collisions so the callbacks happen. This enables us to apply a collider to an object that doesn't block\n"
+   "objects, but does have callbacks, so it can act as a trigger, allowing for arbitrarily shaped triggers, as any collider can\n"
+   "act as a trigger volume(including MeshCollider).\n\n"
+
+   "@tsexample\n"
+   "new CollisionComponentInstance()\n"
+   "{\n"
+   "   template = CollisionComponentTemplate;\n"
+   "   colliderSize = \"1 1 2\";\n"
+   "   blockColldingObject = \"1\";\n"
+   "};\n"
+   "@endtsexample\n"
+
+   "@see SimplePhysicsBehavior\n"
+   "@ingroup Collision\n"
+   "@ingroup Components\n"
+   );
+//Docs
+
+/////////////////////////////////////////////////////////////////////////
+ImplementEnumType(CollisionMeshMeshType,
+   "Type of mesh data available in a shape.\n"
+   "@ingroup gameObjects")
+{ CollisionComponent::None, "None", "No mesh data." },
+{ CollisionComponent::Bounds, "Bounds", "Bounding box of the shape." },
+{ CollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
+{ CollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." },
+EndImplementEnumType;
+
+//
+CollisionComponent::CollisionComponent() : Component()
+{
+   mNetFlags.set(Ghostable | ScopeAlways);
+
+   mFriendlyName = "Collision(Component)";
+
+   mOwnerRenderInterface = NULL;
+   mOwnerPhysicsInterface = NULL;
+
+   mBlockColliding = true;
+
+   mCollisionType = CollisionMesh;
+   mLOSType = CollisionMesh;
+   mDecalType = CollisionMesh;
+
+   colisionMeshPrefix = StringTable->insert("Collision");
+
+   CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
+      StaticShapeObjectType | VehicleObjectType |
+      VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
+
+   mPhysicsRep = NULL;
+   mPhysicsWorld = NULL;
+
+   mTimeoutList = NULL;
+}
+
+CollisionComponent::~CollisionComponent()
+{
+   for (S32 i = 0; i < mFields.size(); ++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(CollisionComponent);
+
+void CollisionComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
+   if (renderInterface)
+   {
+      renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
+      mOwnerRenderInterface = renderInterface;
+   }
+
+   //physicsInterface
+   PhysicsComponentInterface *physicsInterface = mOwner->getComponent<PhysicsComponentInterface>();
+   if (!physicsInterface)
+   {
+      mPhysicsRep = PHYSICSMGR->createBody();
+   }
+
+   prepCollision();
+}
+
+void CollisionComponent::onComponentRemove()
+{
+   SAFE_DELETE(mPhysicsRep);
+
+   Parent::onComponentRemove();
+}
+
+void CollisionComponent::componentAddedToOwner(Component *comp)
+{
+   if (comp->getId() == getId())
+      return;
+
+   //test if this is a shape component!
+   RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
+   if (renderInterface)
+   {
+      renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
+      mOwnerRenderInterface = renderInterface;
+      prepCollision();
+   }
+
+   PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
+   if (physicsInterface)
+   {
+      if (mPhysicsRep)
+         SAFE_DELETE(mPhysicsRep);
+
+      prepCollision();
+   }
+}
+
+void CollisionComponent::componentRemovedFromOwner(Component *comp)
+{
+   if (comp->getId() == getId()) //?????????
+      return;
+
+   //test if this is a shape component!
+   RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
+   if (renderInterface)
+   {
+      renderInterface->onShapeInstanceChanged.remove(this, &CollisionComponent::targetShapeChanged);
+      mOwnerRenderInterface = NULL;
+      prepCollision();
+   }
+
+   //physicsInterface
+   PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
+   if (physicsInterface)
+   {
+      mPhysicsRep = PHYSICSMGR->createBody();
+
+      prepCollision();
+   }
+}
+
+void CollisionComponent::checkDependencies()
+{
+}
+
+void CollisionComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addGroup("Collision");
+
+      addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, CollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, CollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, CollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, CollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("BlockCollisions", TypeBool, Offset(mBlockColliding, CollisionComponent), "");
+
+   endGroup("Collision");
+}
+
+void CollisionComponent::inspectPostApply()
+{
+   // Apply any transformations set in the editor
+   Parent::inspectPostApply();
+
+   if (isServerObject())
+   {
+      setMaskBits(ColliderMask);
+      prepCollision();
+   }
+}
+
+U32 CollisionComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (stream->writeFlag(mask & (ColliderMask | InitialUpdateMask)))
+   {
+      stream->write((U32)mCollisionType);
+      stream->writeString(colisionMeshPrefix);
+   }
+
+   return retMask;
+}
+
+void CollisionComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if (stream->readFlag()) // UpdateMask
+   {
+      U32 collisionType = CollisionMesh;
+
+      stream->read(&collisionType);
+
+      // Handle it if we have changed CollisionType's
+      if ((MeshType)collisionType != mCollisionType)
+      {
+         mCollisionType = (MeshType)collisionType;
+
+         prepCollision();
+      }
+
+      char readBuffer[1024];
+
+      stream->readString(readBuffer);
+      colisionMeshPrefix = StringTable->insert(readBuffer);
+   }
+}
+
+void CollisionComponent::ownerTransformSet(MatrixF *mat)
+{
+   if (mPhysicsRep)
+      mPhysicsRep->setTransform(mOwner->getTransform());
+}
+
+void CollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
+{
+   prepCollision();
+}
+
+void CollisionComponent::prepCollision()
+{
+   if (!mOwner)
+      return;
+
+   // Let the client know that the collision was updated
+   setMaskBits(ColliderMask);
+
+   mOwner->disableCollision();
+
+   if ((!PHYSICSMGR || mCollisionType == None) ||
+      (mOwnerRenderInterface == NULL && (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh)))
+      return;
+
+   PhysicsCollision *colShape = NULL;
+
+   if (mCollisionType == Bounds)
+   {
+      MatrixF offset(true);
+
+      if (mOwnerRenderInterface && mOwnerRenderInterface->getShape())
+         offset.setPosition(mOwnerRenderInterface->getShape()->center);
+
+      colShape = PHYSICSMGR->createCollision();
+      colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset);
+   }
+   else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh /*&& !mOwner->getComponent<AnimatedMesh>()*/))
+   {
+      colShape = buildColShapes();
+   }
+
+   if (colShape)
+   {
+      mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+
+      if (mPhysicsRep)
+      {
+         if (mBlockColliding)
+            mPhysicsRep->init(colShape, 0, 0, mOwner, mPhysicsWorld);
+         else
+            mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER, mOwner, mPhysicsWorld);
+
+         mPhysicsRep->setTransform(mOwner->getTransform());
+      }
+   }
+
+   mOwner->enableCollision();
+
+   onCollisionChanged.trigger(colShape);
+}
+
+void CollisionComponent::processTick()
+{
+   if (!isActive())
+      return;
+
+   //ProcessTick is where our collision testing begins!
+
+   //callback if we have a persisting contact
+   if (mContactInfo.contactObject)
+   {
+      if (mContactInfo.contactTimer > 0)
+      {
+         if (isMethod("updateContact"))
+            Con::executef(this, "updateContact");
+
+         if (mOwner->isMethod("updateContact"))
+            Con::executef(mOwner, "updateContact");
+      }
+
+      ++mContactInfo.contactTimer;
+   }
+   else if (mContactInfo.contactTimer != 0)
+      mContactInfo.clear();
+}
+
+void CollisionComponent::updatePhysics()
+{
+   
+}
+
+PhysicsCollision* CollisionComponent::getCollisionData()
+{
+   if ((!PHYSICSMGR || mCollisionType == None) || mOwnerRenderInterface == NULL)
+      return NULL;
+
+   PhysicsCollision *colShape = NULL;
+   if (mCollisionType == Bounds)
+   {
+      MatrixF offset(true);
+      offset.setPosition(mOwnerRenderInterface->getShape()->center);
+      colShape = PHYSICSMGR->createCollision();
+      colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset);
+   }
+   else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh/* && !mOwner->getComponent<AnimatedMesh>()*/))
+   {
+      colShape = buildColShapes();
+      //colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale());
+   }
+   /*else if (mCollisionType == VisibleMesh && !mOwner->getComponent<AnimatedMesh>())
+   {
+   //We don't have support for visible mesh collisions with animated meshes currently in the physics abstraction layer
+   //so we don't generate anything if we're set to use a visible mesh but have an animated mesh component.
+   colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale());
+   }*/
+   else if (mCollisionType == VisibleMesh/* && mOwner->getComponent<AnimatedMesh>()*/)
+   {
+      Con::printf("CollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!");
+   }
+
+   return colShape;
+}
+
+bool CollisionComponent::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
+{
+   if (!mCollisionType == None)
+   {
+      if (mPhysicsWorld)
+      {
+         return mPhysicsWorld->castRay(start, end, info, Point3F::Zero);
+      }
+   }
+
+   return false;
+}
+
+PhysicsCollision* CollisionComponent::buildColShapes()
+{
+   PROFILE_SCOPE(CollisionComponent_buildColShapes);
+
+   PhysicsCollision *colShape = NULL;
+   U32 surfaceKey = 0;
+
+   TSShape* shape = mOwnerRenderInterface->getShape();
+
+   if (mCollisionType == VisibleMesh)
+   {
+      // Here we build triangle collision meshes from the
+      // visible detail levels.
+
+      // A negative subshape on the detail means we don't have geometry.
+      const TSShape::Detail &detail = shape->details[0];
+      if (detail.subShapeNum < 0)
+         return NULL;
+
+      // We don't try to optimize the triangles we're given
+      // and assume the art was created properly for collision.
+      ConcretePolyList polyList;
+      polyList.setTransform(&MatrixF::Identity, mOwner->getScale());
+
+      // Create the collision meshes.
+      S32 start = shape->subShapeFirstObject[detail.subShapeNum];
+      S32 end = start + shape->subShapeNumObjects[detail.subShapeNum];
+      for (S32 o = start; o < end; o++)
+      {
+         const TSShape::Object &object = shape->objects[o];
+         if (detail.objectDetailNum >= object.numMeshes)
+            continue;
+
+         // No mesh or no verts.... nothing to do.
+         TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
+         if (!mesh || mesh->mNumVerts == 0)
+            continue;
+
+         // Gather the mesh triangles.
+         polyList.clear();
+         mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
+
+         // Create the collision shape if we haven't already.
+         if (!colShape)
+            colShape = PHYSICSMGR->createCollision();
+
+         // Get the object space mesh transform.
+         MatrixF localXfm;
+         shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
+
+         colShape->addTriangleMesh(polyList.mVertexList.address(),
+            polyList.mVertexList.size(),
+            polyList.mIndexList.address(),
+            polyList.mIndexList.size() / 3,
+            localXfm);
+      }
+
+      // Return what we built... if anything.
+      return colShape;
+   }
+   else if (mCollisionType == CollisionMesh)
+   {
+
+      // Scan out the collision hulls...
+      //
+      // TODO: We need to support LOS collision for physics.
+      //
+      for (U32 i = 0; i < shape->details.size(); i++)
+      {
+         const TSShape::Detail &detail = shape->details[i];
+         const String &name = shape->names[detail.nameIndex];
+
+         // Is this a valid collision detail.
+         if (!dStrStartsWith(name, colisionMeshPrefix) || detail.subShapeNum < 0)
+            continue;
+
+         // Now go thru the meshes for this detail.
+         S32 start = shape->subShapeFirstObject[detail.subShapeNum];
+         S32 end = start + shape->subShapeNumObjects[detail.subShapeNum];
+         if (start >= end)
+            continue;
+
+         for (S32 o = start; o < end; o++)
+         {
+            const TSShape::Object &object = shape->objects[o];
+            const String &meshName = shape->names[object.nameIndex];
+
+            if (object.numMeshes <= detail.objectDetailNum)
+               continue;
+
+            // No mesh, a flat bounds, or no verts.... nothing to do.
+            TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
+            if (!mesh || mesh->getBounds().isEmpty() || mesh->mNumVerts == 0)
+               continue;
+
+            // We need the default mesh transform.
+            MatrixF localXfm;
+            shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
+
+            // We have some sort of collision shape... so allocate it.
+            if (!colShape)
+               colShape = PHYSICSMGR->createCollision();
+
+            // Any other mesh name we assume as a generic convex hull.
+            //
+            // Collect the verts using the vertex polylist which will 
+            // filter out duplicates.  This is importaint as the convex
+            // generators can sometimes fail with duplicate verts.
+            //
+            VertexPolyList polyList;
+            MatrixF meshMat(localXfm);
+
+            Point3F t = meshMat.getPosition();
+            t.convolve(mOwner->getScale());
+            meshMat.setPosition(t);
+
+            polyList.setTransform(&MatrixF::Identity, mOwner->getScale());
+            mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
+            colShape->addConvex(polyList.getVertexList().address(),
+               polyList.getVertexList().size(),
+               meshMat);
+         } // objects
+      } // details
+   }
+
+   return colShape;
+}

+ 208 - 0
Engine/source/T3D/components/collision/collisionComponent.h

@@ -0,0 +1,208 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef COLLISION_COMPONENT_H
+#define COLLISION_COMPONENT_H
+
+#ifndef __RESOURCE_H__
+#include "core/resource.h"
+#endif
+#ifndef _TSSHAPE_H_
+#include "ts/tsShape.h"
+#endif
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+#ifndef _MBOX_H_
+#include "math/mBox.h"
+#endif
+#ifndef ENTITY_H
+#include "T3D/entity.h"
+#endif
+#ifndef CORE_INTERFACES_H
+#include "T3D/components/coreInterfaces.h"
+#endif
+#ifndef COLLISION_INTERFACES_H
+#include "T3D/components/collision/collisionInterfaces.h"
+#endif
+#ifndef RENDER_COMPONENT_INTERFACE_H
+#include "T3D/components/render/renderComponentInterface.h"
+#endif
+#ifndef PHYSICS_COMPONENT_INTERFACE_H
+#include "T3D/components/physics/physicsComponentInterface.h"
+#endif
+#ifndef _T3D_PHYSICSCOMMON_H_
+#include "T3D/physics/physicsCommon.h"
+#endif
+#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
+#include "T3D/physics/physicsWorld.h"
+#endif
+
+class TSShapeInstance;
+class SceneRenderState;
+class CollisionComponent;
+class PhysicsBody;
+class PhysicsWorld;
+
+class CollisionComponent : public Component,
+   public CollisionInterface,
+   public CastRayInterface
+{
+   typedef Component Parent;
+public:
+   enum MeshType
+   {
+      None = 0,            ///< No mesh
+      Bounds = 1,          ///< Bounding box of the shape
+      CollisionMesh = 2,   ///< Specifically designated collision meshes
+      VisibleMesh = 3      ///< Rendered mesh polygons
+   };
+
+   PhysicsWorld* mPhysicsWorld;
+   PhysicsBody* mPhysicsRep;
+
+protected:
+   MeshType mCollisionType;
+   MeshType mDecalType;
+   MeshType mLOSType;
+
+   Vector<S32> mCollisionDetails;
+   Vector<S32> mLOSDetails;
+
+   StringTableEntry colisionMeshPrefix;
+
+   RenderComponentInterface* mOwnerRenderInterface;
+
+   PhysicsComponentInterface* mOwnerPhysicsInterface;
+
+   //only really relevent for the collision mesh type
+   //if we note an animation component is added, we flag as being animated.
+   //This way, if we're using collision meshes, we can set it up to update their transforms
+   //as needed
+   bool mAnimated;
+
+   enum
+   {
+      ColliderMask = Parent::NextFreeMask,
+   };
+
+public:
+   CollisionComponent();
+   virtual ~CollisionComponent();
+   DECLARE_CONOBJECT(CollisionComponent);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+   virtual void ownerTransformSet(MatrixF *mat);
+   void targetShapeChanged(RenderComponentInterface* instanceInterface);
+
+   virtual void onComponentRemove();
+   virtual void onComponentAdd();
+
+   virtual void checkDependencies();
+
+   static void initPersistFields();
+
+   void inspectPostApply();
+
+   virtual void processTick();
+
+   void prepCollision();
+
+   PhysicsCollision* buildColShapes();
+
+   void updatePhysics();
+
+   virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
+
+   virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere){ return false; }
+
+   virtual PhysicsCollision* getCollisionData();
+
+   //Utility functions, mostly for script
+   Point3F getContactNormal() { return mContactInfo.contactNormal; }
+   bool hasContact()
+   {
+      if (mContactInfo.contactObject)
+         return true;
+      else
+         return false;
+   }
+   S32 getCollisionCount()
+   {
+      return mCollisionList.getCount();
+   }
+
+   Point3F getCollisionNormal(S32 collisionIndex)
+   {
+      if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
+         return Point3F::Zero;
+
+      return mCollisionList[collisionIndex].normal;
+   }
+
+   F32 getCollisionAngle(S32 collisionIndex, Point3F upVector)
+   {
+      if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
+         return 0.0f;
+
+      return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
+   }
+
+   S32 getBestCollision(Point3F upVector)
+   {
+      S32 bestCollision = -1;
+
+      F32 bestAngle = 360.f;
+      S32 count = mCollisionList.getCount();
+      for (U32 i = 0; i < count; ++i)
+      {
+         F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector)));
+
+         if (angle < bestAngle)
+         {
+            bestCollision = i;
+            bestAngle = angle;
+         }
+      }
+
+      return bestCollision;
+   }
+
+   F32 getBestCollisionAngle(VectorF upVector)
+   {
+      S32 bestCol = getBestCollision(upVector);
+
+      if (bestCol == -1)
+         return 0;
+
+      return getCollisionAngle(bestCol, upVector);
+   }
+};
+
+typedef CollisionComponent::MeshType CollisionMeshMeshType;
+DefineEnumType(CollisionMeshMeshType);
+
+#endif // COLLISION_COMPONENT_H

+ 172 - 0
Engine/source/T3D/components/collision/collisionComponent_ScriptBinding.h

@@ -0,0 +1,172 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "console/engineAPI.h"
+#include "T3D/components/collision/collisionComponent.h"
+#include "materials/baseMatInstance.h"
+
+DefineConsoleMethod(CollisionComponent, getNumberOfContacts, S32, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   return object->getCollisionList()->getCount();
+}
+
+DefineConsoleMethod(CollisionComponent, getBestContact, S32, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   return 0;
+}
+
+DefineConsoleMethod(CollisionComponent, getContactNormal, Point3F, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getContactInfo())
+   {
+      if (object->getContactInfo()->contactObject)
+      {
+         return object->getContactInfo()->contactNormal;
+      }
+   }
+
+   return Point3F::Zero;
+}
+
+DefineConsoleMethod(CollisionComponent, getContactMaterial, S32, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getContactInfo())
+   {
+      if (object->getContactInfo()->contactObject)
+      {
+         if (object->getContactInfo()->contactMaterial != NULL)
+            return object->getContactInfo()->contactMaterial->getMaterial()->getId();
+      }
+   }
+
+   return 0;
+}
+
+DefineConsoleMethod(CollisionComponent, getContactObject, S32, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getContactInfo())
+   {
+      return object->getContactInfo()->contactObject != NULL ? object->getContactInfo()->contactObject->getId() : 0;
+   }
+
+   return 0;
+}
+
+DefineConsoleMethod(CollisionComponent, getContactPoint, Point3F, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getContactInfo())
+   {
+      if (object->getContactInfo()->contactObject)
+      {
+         return object->getContactInfo()->contactPoint;
+      }
+   }
+
+   return Point3F::Zero;
+}
+
+DefineConsoleMethod(CollisionComponent, getContactTime, S32, (), ,
+   "Gets the number of contacts this collider has hit.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getContactInfo())
+   {
+      if (object->getContactInfo()->contactObject)
+      {
+         return object->getContactInfo()->contactTimer;
+      }
+   }
+
+   return 0;
+}
+
+DefineEngineMethod(CollisionComponent, hasContact, bool, (), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->hasContact();
+}
+
+DefineEngineMethod(CollisionComponent, getCollisionCount, S32, (), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->getCollisionCount();
+}
+
+DefineEngineMethod(CollisionComponent, getCollisionNormal, Point3F, (S32 collisionIndex), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->getCollisionNormal(collisionIndex);
+}
+
+DefineEngineMethod(CollisionComponent, getCollisionAngle, F32, (S32 collisionIndex, VectorF upVector), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->getCollisionAngle(collisionIndex, upVector);
+}
+
+DefineEngineMethod(CollisionComponent, getBestCollisionAngle, F32, (VectorF upVector), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->getBestCollisionAngle(upVector);
+}

+ 258 - 0
Engine/source/T3D/components/collision/collisionInterfaces.cpp

@@ -0,0 +1,258 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/collision/collisionInterfaces.h"
+#include "scene/sceneObject.h"
+#include "T3D/entity.h"
+#include "console/engineAPI.h"
+#include "T3D/trigger.h"
+#include "materials/baseMatInstance.h"
+
+void CollisionInterface::handleCollisionList( CollisionList &collisionList, VectorF velocity )
+{
+   Collision bestCol;
+
+   mCollisionList = collisionList;
+
+   for (U32 i=0; i < collisionList.getCount(); ++i)
+   {
+      Collision& colCheck = collisionList[i];
+
+      if (colCheck.object)
+      {
+         if (colCheck.object->getTypeMask() & PlayerObjectType)
+         {
+            handleCollision( colCheck, velocity );
+         }
+         else if (colCheck.object->getTypeMask() & TriggerObjectType)
+         {
+            // We've hit it's bounding box, that's close enough for triggers
+            Trigger* pTrigger = static_cast<Trigger*>(colCheck.object);
+
+            Component *comp = dynamic_cast<Component*>(this);
+            pTrigger->potentialEnterObject(comp->getOwner());
+         }
+         else if (colCheck.object->getTypeMask() & DynamicShapeObjectType)
+         {
+            Con::printf("HIT A GENERICALLY DYNAMIC OBJECT");
+            handleCollision(colCheck, velocity);
+         }
+         else if(colCheck.object->getTypeMask() & EntityObjectType)
+         {
+            Entity* ent = dynamic_cast<Entity*>(colCheck.object);
+            if (ent)
+            {
+               CollisionInterface *colObjectInterface = ent->getComponent<CollisionInterface>();
+               if (colObjectInterface)
+               {
+                  //convert us to our component
+                  Component *thisComp = dynamic_cast<Component*>(this);
+                  if (thisComp)
+                  {
+                     colObjectInterface->onCollisionSignal.trigger(thisComp->getOwner());
+
+                     //TODO: properly do this
+                     Collision oppositeCol = colCheck;
+                     oppositeCol.object = thisComp->getOwner();
+
+                     colObjectInterface->handleCollision(oppositeCol, velocity);
+                  }
+               }
+            }
+         }
+         else
+         {
+            handleCollision(colCheck, velocity);
+         }
+      }
+   }
+}
+
+void CollisionInterface::handleCollision( Collision &col, VectorF velocity )
+{
+   if (col.object && (mContactInfo.contactObject == NULL ||
+      col.object->getId() != mContactInfo.contactObject->getId()))
+   {
+      queueCollision(col.object, velocity - col.object->getVelocity());
+
+      //do the callbacks to script for this collision
+      Component *comp = dynamic_cast<Component*>(this);
+      if (comp->isMethod("onCollision"))
+      {
+         S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
+         Con::executef(comp, "onCollision", col.object, col.normal, col.point, matId, velocity);
+      }
+
+      if (comp->getOwner()->isMethod("onCollisionEvent"))
+      {
+         S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
+         Con::executef(comp->getOwner(), "onCollisionEvent", col.object, col.normal, col.point, matId, velocity);
+      }
+   }
+}
+
+void CollisionInterface::handleCollisionNotifyList()
+{
+   //special handling for any collision components we should notify that a collision happened.
+   for (U32 i = 0; i < mCollisionNotifyList.size(); ++i)
+   {
+      //convert us to our component
+      Component *thisComp = dynamic_cast<Component*>(this);
+      if (thisComp)
+      {
+         mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner());
+      }
+   }
+
+   mCollisionNotifyList.clear();
+}
+
+Chunker<CollisionInterface::CollisionTimeout> sCollisionTimeoutChunker;
+CollisionInterface::CollisionTimeout* CollisionInterface::sFreeTimeoutList = 0;
+
+void CollisionInterface::queueCollision( SceneObject *obj, const VectorF &vec)
+{
+   // Add object to list of collisions.
+   SimTime time = Sim::getCurrentTime();
+   S32 num = obj->getId();
+
+   CollisionTimeout** adr = &mTimeoutList;
+   CollisionTimeout* ptr = mTimeoutList;
+   while (ptr) 
+   {
+      if (ptr->objectNumber == num) 
+      {
+         if (ptr->expireTime < time) 
+         {
+            ptr->expireTime = time + CollisionTimeoutValue;
+            ptr->object = obj;
+            ptr->vector = vec;
+         }
+         return;
+      }
+      // Recover expired entries
+      if (ptr->expireTime < time) 
+      {
+         CollisionTimeout* cur = ptr;
+         *adr = ptr->next;
+         ptr = ptr->next;
+         cur->next = sFreeTimeoutList;
+         sFreeTimeoutList = cur;
+      }
+      else 
+      {
+         adr = &ptr->next;
+         ptr = ptr->next;
+      }
+   }
+
+   // New entry for the object
+   if (sFreeTimeoutList != NULL)
+   {
+      ptr = sFreeTimeoutList;
+      sFreeTimeoutList = ptr->next;
+      ptr->next = NULL;
+   }
+   else
+   {
+      ptr = sCollisionTimeoutChunker.alloc();
+   }
+
+   ptr->object = obj;
+   ptr->objectNumber = obj->getId();
+   ptr->vector = vec;
+   ptr->expireTime = time + CollisionTimeoutValue;
+   ptr->next = mTimeoutList;
+
+   mTimeoutList = ptr;
+}
+
+bool CollisionInterface::checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale, 
+														Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList)
+{
+   Point3F end = start + velocity * time;
+   Point3F distance = end - start;
+
+   Box3F scaledBox = objectBox;
+   scaledBox.minExtents.convolve(objectScale);
+   scaledBox.maxExtents.convolve(objectScale);
+
+   if (mFabs(distance.x) < objectBox.len_x() &&
+      mFabs(distance.y) < objectBox.len_y() &&
+      mFabs(distance.z) < objectBox.len_z())
+   {
+      // We can potentially early out of this.  If there are no polys in the clipped polylist at our
+      //  end position, then we can bail, and just set start = end;
+      Box3F wBox = scaledBox;
+      wBox.minExtents += end;
+      wBox.maxExtents += end;
+
+      static EarlyOutPolyList eaPolyList;
+      eaPolyList.clear();
+      eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
+      eaPolyList.mPlaneList.clear();
+      eaPolyList.mPlaneList.setSize(6);
+      eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
+      eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
+      eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
+      eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
+      eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
+      eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
+
+      // Build list from convex states here...
+      CollisionWorkingList& rList = colWorkingList;
+      CollisionWorkingList* pList = rList.wLink.mNext;
+      while (pList != &rList) 
+      {
+         Convex* pConvex = pList->mConvex;
+
+         if (pConvex->getObject()->getTypeMask() & collisionMask) 
+         {
+            Box3F convexBox = pConvex->getBoundingBox();
+
+            if (wBox.isOverlapped(convexBox))
+            {
+               // No need to separate out the physical zones here, we want those
+               //  to cause a fallthrough as well...
+               pConvex->getPolyList(&eaPolyList);
+            }
+         }
+         pList = pList->wLink.mNext;
+      }
+
+      if (eaPolyList.isEmpty())
+      {
+         return true;
+      }
+   }
+
+   return false;
+}
+
+
+Collision* CollisionInterface::getCollision(S32 col) 
+{ 
+   if(col < mCollisionList.getCount() && col >= 0) 
+      return &mCollisionList[col];
+   else 
+      return NULL; 
+}

+ 167 - 0
Engine/source/T3D/components/collision/collisionInterfaces.h

@@ -0,0 +1,167 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef COLLISION_INTERFACES_H
+#define COLLISION_INTERFACES_H
+
+#ifndef _CONVEX_H_
+#include "collision/convex.h"
+#endif
+#ifndef _COLLISION_H_
+#include "collision/collision.h"
+#endif
+#ifndef _EARLYOUTPOLYLIST_H_
+#include "collision/earlyOutPolyList.h"
+#endif
+#ifndef _SIM_H_
+#include "console/sim.h"
+#endif
+#ifndef _SCENECONTAINER_H_
+#include "scene/sceneContainer.h"
+#endif
+#ifndef _T3D_PHYSICSCOMMON_H_
+#include "T3D/physics/physicsCommon.h"
+#endif
+
+struct ContactInfo 
+{
+   bool contacted, move;
+   SceneObject *contactObject;
+   VectorF  idealContactNormal;
+   VectorF  contactNormal;
+   Point3F  contactPoint;
+   F32	   contactTime;
+   S32	   contactTimer;
+   BaseMatInstance *contactMaterial;
+
+   void clear()
+   {
+      contacted=move=false; 
+      contactObject = NULL; 
+      contactNormal.set(0,0,0);
+      contactTime = 0.f;
+      contactTimer = 0;
+      idealContactNormal.set(0, 0, 1);
+      contactMaterial = NULL;
+   }
+
+   ContactInfo() { clear(); }
+
+};
+
+class CollisionInterface// : public Interface<CollisionInterface>
+{
+public:
+	// CollisionTimeout
+	// This struct lets us track our collisions and estimate when they've have timed out and we'll need to act on it.
+	struct CollisionTimeout 
+   {
+      CollisionTimeout* next;
+      SceneObject* object;
+      U32 objectNumber;
+      SimTime expireTime;
+      VectorF vector;
+   };
+
+   Signal< void( SceneObject* ) > onCollisionSignal;
+   Signal< void( SceneObject* ) > onContactSignal;
+
+protected:
+   CollisionTimeout* mTimeoutList;
+   static CollisionTimeout* sFreeTimeoutList;
+
+   CollisionList mCollisionList;
+   Vector<CollisionInterface*> mCollisionNotifyList;
+
+   ContactInfo mContactInfo;
+
+   Box3F mWorkingQueryBox;
+
+   U32 CollisionMoveMask;
+
+   Convex *mConvexList;
+
+   bool mBlockColliding;
+
+   void handleCollisionNotifyList();
+
+   void queueCollision( SceneObject *obj, const VectorF &vec);
+
+	/// checkEarlyOut
+	/// This function lets you trying and early out of any expensive collision checks by using simple extruded poly boxes representing our objects
+	/// If it returns true, we know we won't hit with the given parameters and can successfully early out. If it returns false, our test case collided
+	/// and we should do the full collision sim.
+	bool checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale, 
+														Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList);
+
+public:
+   /// checkCollisions
+   // This is our main function for checking if a collision is happening based on the start point, velocity and time
+   // We do the bulk of the collision checking in here
+   //virtual bool checkCollisions( const F32 travelTime, Point3F *velocity, Point3F start )=0;
+
+   CollisionList *getCollisionList() { return &mCollisionList; }
+
+   void clearCollisionList() { mCollisionList.clear(); }
+
+   void clearCollisionNotifyList() { mCollisionNotifyList.clear(); }
+
+   Collision *getCollision(S32 col);
+
+   ContactInfo* getContactInfo() { return &mContactInfo; }
+
+	Convex *getConvexList() { return mConvexList; }
+
+   virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0;
+
+	enum PublicConstants { 
+      CollisionTimeoutValue = 250
+   };
+
+   bool doesBlockColliding() { return mBlockColliding; }
+
+   /// handleCollisionList
+   /// This basically takes in a CollisionList and calls handleCollision for each.
+   void handleCollisionList(CollisionList &collisionList, VectorF velocity);
+
+   /// handleCollision
+   /// This will take a collision and queue the collision info for the object so that in knows about the collision.
+   void handleCollision(Collision &col, VectorF velocity);
+
+   virtual PhysicsCollision* getCollisionData() = 0;
+
+   Signal< void(PhysicsCollision* collision) > onCollisionChanged;
+};
+
+class BuildConvexInterface //: public Interface<CollisionInterface>
+{
+public:
+   virtual void buildConvex(const Box3F& box, Convex* convex)=0;
+};
+
+class BuildPolyListInterface// : public Interface<CollisionInterface>
+{
+public:
+   virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0;
+};
+
+#endif

+ 619 - 0
Engine/source/T3D/components/collision/collisionTrigger.cpp

@@ -0,0 +1,619 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "T3D/components/collision/collisionTrigger.h"
+
+#include "scene/sceneRenderState.h"
+#include "console/consoleTypes.h"
+#include "console/engineAPI.h"
+#include "collision/boxConvex.h"
+
+#include "core/stream/bitStream.h"
+#include "math/mathIO.h"
+#include "gfx/gfxTransformSaver.h"
+#include "renderInstance/renderPassManager.h"
+#include "gfx/gfxDrawUtil.h"
+#include "T3D/physics/physicsPlugin.h"
+#include "T3D/physics/physicsBody.h"
+#include "T3D/physics/physicsCollision.h"
+
+
+bool CollisionTrigger::smRenderCollisionTriggers = false;
+
+//-----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+//--------------------------------------------------------------------------
+
+IMPLEMENT_CO_NETOBJECT_V1(CollisionTrigger);
+
+ConsoleDocClass(CollisionTrigger,
+   "@brief A CollisionTrigger is a volume of space that initiates script callbacks "
+   "when objects pass through the CollisionTrigger.\n\n"
+
+   "CollisionTriggerData provides the callbacks for the CollisionTrigger when an object enters, stays inside "
+   "or leaves the CollisionTrigger's volume.\n\n"
+
+   "@see CollisionTriggerData\n"
+   "@ingroup gameObjects\n"
+   );
+
+IMPLEMENT_CALLBACK(CollisionTrigger, onAdd, void, (U32 objectId), (objectId),
+   "@brief Called when the CollisionTrigger is being created.\n\n"
+   "@param objectId the object id of the CollisionTrigger being created\n");
+
+IMPLEMENT_CALLBACK(CollisionTrigger, onRemove, void, (U32 objectId), (objectId),
+   "@brief Called just before the CollisionTrigger is deleted.\n\n"
+   "@param objectId the object id of the CollisionTrigger being deleted\n");
+
+CollisionTrigger::CollisionTrigger()
+{
+   // Don't ghost by default.
+   mNetFlags.set(Ghostable | ScopeAlways);
+
+   mTypeMask |= TriggerObjectType;
+
+   mObjScale.set(1, 1, 1);
+   mObjToWorld.identity();
+   mWorldToObj.identity();
+
+   mLastThink = 0;
+   mCurrTick = 0;
+
+   mConvexList = new Convex;
+
+   mPhysicsRep = NULL;
+}
+
+CollisionTrigger::~CollisionTrigger()
+{
+   delete mConvexList;
+   mConvexList = NULL;
+   SAFE_DELETE(mPhysicsRep);
+}
+
+bool CollisionTrigger::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
+{
+   // Collide against bounding box
+   F32 st, et, fst = 0, fet = 1;
+   F32 *bmin = &mObjBox.minExtents.x;
+   F32 *bmax = &mObjBox.maxExtents.x;
+   F32 const *si = &start.x;
+   F32 const *ei = &end.x;
+
+   for (S32 i = 0; i < 3; i++)
+   {
+      if (*si < *ei)
+      {
+         if (*si > *bmax || *ei < *bmin)
+            return false;
+         F32 di = *ei - *si;
+         st = (*si < *bmin) ? (*bmin - *si) / di : 0;
+         et = (*ei > *bmax) ? (*bmax - *si) / di : 1;
+      }
+      else
+      {
+         if (*ei > *bmax || *si < *bmin)
+            return false;
+         F32 di = *ei - *si;
+         st = (*si > *bmax) ? (*bmax - *si) / di : 0;
+         et = (*ei < *bmin) ? (*bmin - *si) / di : 1;
+      }
+      if (st > fst) fst = st;
+      if (et < fet) fet = et;
+      if (fet < fst)
+         return false;
+      bmin++; bmax++;
+      si++; ei++;
+   }
+
+   info->normal = start - end;
+   info->normal.normalizeSafe();
+   getTransform().mulV(info->normal);
+
+   info->t = fst;
+   info->object = this;
+   info->point.interpolate(start, end, fst);
+   info->material = 0;
+   return true;
+}
+
+//-----------------------------------------------------------------------------
+void CollisionTrigger::consoleInit()
+{
+   Con::addVariable("$CollisionTrigger::renderCollisionTriggers", TypeBool, &smRenderCollisionTriggers,
+      "@brief Forces all CollisionTrigger's to render.\n\n"
+      "Used by the Tools and debug render modes.\n"
+      "@ingroup gameObjects");
+}
+
+void CollisionTrigger::initPersistFields()
+{
+   addField("polyhedron", TypeTriggerPolyhedron, Offset(mCollisionTriggerPolyhedron, CollisionTrigger),
+      "@brief Defines a non-rectangular area for the CollisionTrigger.\n\n"
+      "Rather than the standard rectangular bounds, this optional parameter defines a quadrilateral "
+      "CollisionTrigger area.  The quadrilateral is defined as a corner point followed by three vectors "
+      "representing the edges extending from the corner.\n");
+
+   addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, CollisionTrigger), &setEnterCmd, &defaultProtectedGetFn,
+      "The command to execute when an object enters this CollisionTrigger. Object id stored in %%obj. Maximum 1023 characters.");
+   addProtectedField("leaveCommand", TypeCommand, Offset(mLeaveCommand, CollisionTrigger), &setLeaveCmd, &defaultProtectedGetFn,
+      "The command to execute when an object leaves this CollisionTrigger. Object id stored in %%obj. Maximum 1023 characters.");
+   addProtectedField("tickCommand", TypeCommand, Offset(mTickCommand, CollisionTrigger), &setTickCmd, &defaultProtectedGetFn,
+      "The command to execute while an object is inside this CollisionTrigger. Maximum 1023 characters.");
+
+   Parent::initPersistFields();
+}
+
+bool CollisionTrigger::setEnterCmd(void *object, const char *index, const char *data)
+{
+   static_cast<CollisionTrigger*>(object)->setMaskBits(EnterCmdMask);
+   return true; // to update the actual field
+}
+
+bool CollisionTrigger::setLeaveCmd(void *object, const char *index, const char *data)
+{
+   static_cast<CollisionTrigger*>(object)->setMaskBits(LeaveCmdMask);
+   return true; // to update the actual field
+}
+
+bool CollisionTrigger::setTickCmd(void *object, const char *index, const char *data)
+{
+   static_cast<CollisionTrigger*>(object)->setMaskBits(TickCmdMask);
+   return true; // to update the actual field
+}
+
+//--------------------------------------------------------------------------
+
+bool CollisionTrigger::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   onAdd_callback(getId());
+
+   Polyhedron temp = mCollisionTriggerPolyhedron;
+   setTriggerPolyhedron(temp);
+
+   addToScene();
+
+   if (isServerObject())
+      scriptOnAdd();
+
+   return true;
+}
+
+void CollisionTrigger::onRemove()
+{
+   onRemove_callback(getId());
+
+   mConvexList->nukeList();
+
+   removeFromScene();
+   Parent::onRemove();
+}
+
+bool CollisionTrigger::onNewDataBlock(GameBaseData *dptr, bool reload)
+{
+   return true;
+}
+
+void CollisionTrigger::onDeleteNotify(SimObject *obj)
+{
+   GameBase* pScene = dynamic_cast<GameBase*>(obj);
+
+   if (pScene != NULL)
+   {
+      for (U32 i = 0; i < mObjects.size(); i++)
+      {
+         if (pScene == mObjects[i])
+         {
+            mObjects.erase(i);
+            //onLeaveCollisionTrigger_callback(this, pScene);
+            break;
+         }
+      }
+   }
+
+   Parent::onDeleteNotify(obj);
+}
+
+void CollisionTrigger::inspectPostApply()
+{
+   setTriggerPolyhedron(mCollisionTriggerPolyhedron);
+   setMaskBits(PolyMask);
+   Parent::inspectPostApply();
+}
+
+//--------------------------------------------------------------------------
+
+void CollisionTrigger::buildConvex(const Box3F& box, Convex* convex)
+{
+   // These should really come out of a pool
+   mConvexList->collectGarbage();
+
+   Box3F realBox = box;
+   mWorldToObj.mul(realBox);
+   realBox.minExtents.convolveInverse(mObjScale);
+   realBox.maxExtents.convolveInverse(mObjScale);
+
+   if (realBox.isOverlapped(getObjBox()) == false)
+      return;
+
+   // Just return a box convex for the entire shape...
+   Convex* cc = 0;
+   CollisionWorkingList& wl = convex->getWorkingList();
+   for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
+      if (itr->mConvex->getType() == BoxConvexType &&
+         itr->mConvex->getObject() == this) {
+         cc = itr->mConvex;
+         break;
+      }
+   }
+   if (cc)
+      return;
+
+   // Create a new convex.
+   BoxConvex* cp = new BoxConvex;
+   mConvexList->registerObject(cp);
+   convex->addToWorkingList(cp);
+   cp->init(this);
+
+   mObjBox.getCenter(&cp->mCenter);
+   cp->mSize.x = mObjBox.len_x() / 2.0f;
+   cp->mSize.y = mObjBox.len_y() / 2.0f;
+   cp->mSize.z = mObjBox.len_z() / 2.0f;
+}
+
+
+//------------------------------------------------------------------------------
+
+void CollisionTrigger::setTransform(const MatrixF & mat)
+{
+   Parent::setTransform(mat);
+
+   if (mPhysicsRep)
+      mPhysicsRep->setTransform(mat);
+
+   if (isServerObject()) {
+      MatrixF base(true);
+      base.scale(Point3F(1.0 / mObjScale.x,
+         1.0 / mObjScale.y,
+         1.0 / mObjScale.z));
+      base.mul(mWorldToObj);
+      mClippedList.setBaseTransform(base);
+
+      setMaskBits(TransformMask | ScaleMask);
+   }
+}
+
+void CollisionTrigger::prepRenderImage(SceneRenderState *state)
+{
+   // only render if selected or render flag is set
+   if (!smRenderCollisionTriggers && !isSelected())
+      return;
+
+   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
+   ri->renderDelegate.bind(this, &CollisionTrigger::renderObject);
+   ri->type = RenderPassManager::RIT_Editor;
+   ri->translucentSort = true;
+   ri->defaultKey = 1;
+   state->getRenderPass()->addInst(ri);
+}
+
+void CollisionTrigger::renderObject(ObjectRenderInst *ri,
+   SceneRenderState *state,
+   BaseMatInstance *overrideMat)
+{
+   if (overrideMat)
+      return;
+
+   GFXStateBlockDesc desc;
+   desc.setZReadWrite(true, false);
+   desc.setBlend(true);
+
+   // CollisionTrigger polyhedrons are set up with outward facing normals and CCW ordering
+   // so can't enable backface culling.
+   desc.setCullMode(GFXCullNone);
+
+   GFXTransformSaver saver;
+
+   MatrixF mat = getRenderTransform();
+   mat.scale(getScale());
+
+   GFX->multWorld(mat);
+
+   GFXDrawUtil *drawer = GFX->getDrawUtil();
+
+   drawer->drawPolyhedron(desc, mCollisionTriggerPolyhedron, ColorI(255, 192, 0, 45));
+
+   // Render wireframe.
+
+   desc.setFillModeWireframe();
+   drawer->drawPolyhedron(desc, mCollisionTriggerPolyhedron, ColorI::BLACK);
+}
+
+void CollisionTrigger::setTriggerPolyhedron(const Polyhedron& rPolyhedron)
+{
+   mCollisionTriggerPolyhedron = rPolyhedron;
+
+   if (mCollisionTriggerPolyhedron.pointList.size() != 0) {
+      mObjBox.minExtents.set(1e10, 1e10, 1e10);
+      mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
+      for (U32 i = 0; i < mCollisionTriggerPolyhedron.pointList.size(); i++) {
+         mObjBox.minExtents.setMin(mCollisionTriggerPolyhedron.pointList[i]);
+         mObjBox.maxExtents.setMax(mCollisionTriggerPolyhedron.pointList[i]);
+      }
+   }
+   else {
+      mObjBox.minExtents.set(-0.5, -0.5, -0.5);
+      mObjBox.maxExtents.set(0.5, 0.5, 0.5);
+   }
+
+   MatrixF xform = getTransform();
+   setTransform(xform);
+
+   mClippedList.clear();
+   mClippedList.mPlaneList = mCollisionTriggerPolyhedron.planeList;
+   //   for (U32 i = 0; i < mClippedList.mPlaneList.size(); i++)
+   //      mClippedList.mPlaneList[i].neg();
+
+   MatrixF base(true);
+   base.scale(Point3F(1.0 / mObjScale.x,
+      1.0 / mObjScale.y,
+      1.0 / mObjScale.z));
+   base.mul(mWorldToObj);
+
+   mClippedList.setBaseTransform(base);
+
+   SAFE_DELETE(mPhysicsRep);
+
+   if (PHYSICSMGR)
+   {
+      PhysicsCollision *colShape = PHYSICSMGR->createCollision();
+
+      MatrixF colMat(true);
+      colMat.displace(Point3F(0, 0, mObjBox.getExtents().z * 0.5f * mObjScale.z));
+
+      colShape->addBox(mObjBox.getExtents() * 0.5f * mObjScale, colMat);
+      //MatrixF colMat( true );
+      //colMat.scale( mObjScale );
+      //colShape->addConvex( mCollisionTriggerPolyhedron.pointList.address(), mCollisionTriggerPolyhedron.pointList.size(), colMat );
+
+      PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+      mPhysicsRep = PHYSICSMGR->createBody();
+      mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world);
+      mPhysicsRep->setTransform(getTransform());
+   }
+}
+
+
+//--------------------------------------------------------------------------
+
+bool CollisionTrigger::testObject(GameBase* enter)
+{
+   if (mCollisionTriggerPolyhedron.pointList.size() == 0)
+      return false;
+
+   mClippedList.clear();
+
+   SphereF sphere;
+   sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5;
+   VectorF bv = mWorldBox.maxExtents - sphere.center;
+   sphere.radius = bv.len();
+
+   enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere);
+   return mClippedList.isEmpty() == false;
+}
+
+
+void CollisionTrigger::potentialEnterObject(GameBase* enter)
+{
+   for (U32 i = 0; i < mObjects.size(); i++) {
+      if (mObjects[i] == enter)
+         return;
+   }
+
+   if (testObject(enter) == true) {
+      mObjects.push_back(enter);
+      deleteNotify(enter);
+
+      if (!mEnterCommand.isEmpty())
+      {
+         String command = String("%obj = ") + enter->getIdString() + ";" + mEnterCommand;
+         Con::evaluate(command.c_str());
+      }
+
+      //onEnterCollisionTrigger_callback(this, enter);
+   }
+}
+
+
+void CollisionTrigger::processTick(const Move* move)
+{
+   Parent::processTick(move);
+
+   //
+   if (mObjects.size() == 0)
+      return;
+
+   if (mLastThink + 100 < mCurrTick)
+   {
+      mCurrTick = 0;
+      mLastThink = 0;
+
+      for (S32 i = S32(mObjects.size() - 1); i >= 0; i--)
+      {
+         if (testObject(mObjects[i]) == false)
+         {
+            GameBase* remove = mObjects[i];
+            mObjects.erase(i);
+            clearNotify(remove);
+
+            if (!mLeaveCommand.isEmpty())
+            {
+               String command = String("%obj = ") + remove->getIdString() + ";" + mLeaveCommand;
+               Con::evaluate(command.c_str());
+            }
+
+            //onLeaveCollisionTrigger_callback(this, remove);
+         }
+      }
+
+      if (!mTickCommand.isEmpty())
+         Con::evaluate(mTickCommand.c_str());
+
+      //if (mObjects.size() != 0)
+      //   onTickCollisionTrigger_callback(this);
+   }
+   else
+   {
+      mCurrTick += TickMs;
+   }
+}
+
+//--------------------------------------------------------------------------
+
+U32 CollisionTrigger::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
+{
+   U32 i;
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (stream->writeFlag(mask & TransformMask))
+   {
+      stream->writeAffineTransform(mObjToWorld);
+   }
+
+   // Write the polyhedron
+   if (stream->writeFlag(mask & PolyMask))
+   {
+      stream->write(mCollisionTriggerPolyhedron.pointList.size());
+      for (i = 0; i < mCollisionTriggerPolyhedron.pointList.size(); i++)
+         mathWrite(*stream, mCollisionTriggerPolyhedron.pointList[i]);
+
+      stream->write(mCollisionTriggerPolyhedron.planeList.size());
+      for (i = 0; i < mCollisionTriggerPolyhedron.planeList.size(); i++)
+         mathWrite(*stream, mCollisionTriggerPolyhedron.planeList[i]);
+
+      stream->write(mCollisionTriggerPolyhedron.edgeList.size());
+      for (i = 0; i < mCollisionTriggerPolyhedron.edgeList.size(); i++) {
+         const Polyhedron::Edge& rEdge = mCollisionTriggerPolyhedron.edgeList[i];
+
+         stream->write(rEdge.face[0]);
+         stream->write(rEdge.face[1]);
+         stream->write(rEdge.vertex[0]);
+         stream->write(rEdge.vertex[1]);
+      }
+   }
+
+   if (stream->writeFlag(mask & EnterCmdMask))
+      stream->writeLongString(CMD_SIZE - 1, mEnterCommand.c_str());
+   if (stream->writeFlag(mask & LeaveCmdMask))
+      stream->writeLongString(CMD_SIZE - 1, mLeaveCommand.c_str());
+   if (stream->writeFlag(mask & TickCmdMask))
+      stream->writeLongString(CMD_SIZE - 1, mTickCommand.c_str());
+
+   return retMask;
+}
+
+void CollisionTrigger::unpackUpdate(NetConnection* con, BitStream* stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   U32 i, size;
+
+   // Transform
+   if (stream->readFlag())
+   {
+      MatrixF temp;
+      stream->readAffineTransform(&temp);
+      setTransform(temp);
+   }
+
+   // Read the polyhedron
+   if (stream->readFlag())
+   {
+      Polyhedron tempPH;
+      stream->read(&size);
+      tempPH.pointList.setSize(size);
+      for (i = 0; i < tempPH.pointList.size(); i++)
+         mathRead(*stream, &tempPH.pointList[i]);
+
+      stream->read(&size);
+      tempPH.planeList.setSize(size);
+      for (i = 0; i < tempPH.planeList.size(); i++)
+         mathRead(*stream, &tempPH.planeList[i]);
+
+      stream->read(&size);
+      tempPH.edgeList.setSize(size);
+      for (i = 0; i < tempPH.edgeList.size(); i++) {
+         Polyhedron::Edge& rEdge = tempPH.edgeList[i];
+
+         stream->read(&rEdge.face[0]);
+         stream->read(&rEdge.face[1]);
+         stream->read(&rEdge.vertex[0]);
+         stream->read(&rEdge.vertex[1]);
+      }
+      setTriggerPolyhedron(tempPH);
+   }
+
+   if (stream->readFlag())
+   {
+      char buf[CMD_SIZE];
+      stream->readLongString(CMD_SIZE - 1, buf);
+      mEnterCommand = buf;
+   }
+   if (stream->readFlag())
+   {
+      char buf[CMD_SIZE];
+      stream->readLongString(CMD_SIZE - 1, buf);
+      mLeaveCommand = buf;
+   }
+   if (stream->readFlag())
+   {
+      char buf[CMD_SIZE];
+      stream->readLongString(CMD_SIZE - 1, buf);
+      mTickCommand = buf;
+   }
+}
+
+//ConsoleMethod( CollisionTrigger, getNumObjects, S32, 2, 2, "")
+DefineEngineMethod(CollisionTrigger, getNumObjects, S32, (), ,
+   "@brief Get the number of objects that are within the CollisionTrigger's bounds.\n\n"
+   "@see getObject()\n")
+{
+   return object->getNumCollisionTriggeringObjects();
+}
+
+//ConsoleMethod( CollisionTrigger, getObject, S32, 3, 3, "(int idx)")
+DefineEngineMethod(CollisionTrigger, getObject, S32, (S32 index), ,
+   "@brief Retrieve the requested object that is within the CollisionTrigger's bounds.\n\n"
+   "@param index Index of the object to get (range is 0 to getNumObjects()-1)\n"
+   "@returns The SimObjectID of the object, or -1 if the requested index is invalid.\n"
+   "@see getNumObjects()\n")
+{
+   if (index >= object->getNumCollisionTriggeringObjects() || index < 0)
+      return -1;
+   else
+      return object->getObject(U32(index))->getId();
+}

+ 145 - 0
Engine/source/T3D/components/collision/collisionTrigger.h

@@ -0,0 +1,145 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _H_CollisionTrigger
+#define _H_CollisionTrigger
+
+#ifndef _GAMEBASE_H_
+#include "T3D/gameBase/gameBase.h"
+#endif
+#ifndef _MBOX_H_
+#include "math/mBox.h"
+#endif
+#ifndef _EARLYOUTPOLYLIST_H_
+#include "collision/earlyOutPolyList.h"
+#endif
+#ifndef _MPOLYHEDRON_H_
+#include "math/mPolyhedron.h"
+#endif
+#ifndef _TRIGGER_H_
+#include "T3D/trigger.h"
+#endif
+
+class Convex;
+class PhysicsBody;
+class TriggerPolyhedronType;
+
+class CollisionTrigger : public GameBase
+{
+   typedef GameBase Parent;
+
+   /// CollisionTrigger polyhedron with *outward* facing normals and CCW ordered
+   /// vertices.
+   Polyhedron mCollisionTriggerPolyhedron;
+
+   EarlyOutPolyList  mClippedList;
+   Vector<GameBase*> mObjects;
+
+   PhysicsBody      *mPhysicsRep;
+
+   U32               mLastThink;
+   U32               mCurrTick;
+   Convex            *mConvexList;
+
+   String            mEnterCommand;
+   String            mLeaveCommand;
+   String            mTickCommand;
+
+   enum CollisionTriggerUpdateBits
+   {
+      TransformMask = Parent::NextFreeMask << 0,
+      PolyMask = Parent::NextFreeMask << 1,
+      EnterCmdMask = Parent::NextFreeMask << 2,
+      LeaveCmdMask = Parent::NextFreeMask << 3,
+      TickCmdMask = Parent::NextFreeMask << 4,
+      NextFreeMask = Parent::NextFreeMask << 5,
+   };
+
+   static const U32 CMD_SIZE = 1024;
+
+protected:
+
+   static bool smRenderCollisionTriggers;
+   bool testObject(GameBase* enter);
+   void processTick(const Move *move);
+
+   void buildConvex(const Box3F& box, Convex* convex);
+
+   static bool setEnterCmd(void *object, const char *index, const char *data);
+   static bool setLeaveCmd(void *object, const char *index, const char *data);
+   static bool setTickCmd(void *object, const char *index, const char *data);
+
+public:
+   CollisionTrigger();
+   ~CollisionTrigger();
+
+   // SimObject
+   DECLARE_CONOBJECT(CollisionTrigger);
+
+   DECLARE_CALLBACK(void, onAdd, (U32 objectId));
+   DECLARE_CALLBACK(void, onRemove, (U32 objectId));
+
+   static void consoleInit();
+   static void initPersistFields();
+   bool onAdd();
+   void onRemove();
+   void onDeleteNotify(SimObject*);
+   void inspectPostApply();
+
+   // NetObject
+   U32  packUpdate(NetConnection *conn, U32 mask, BitStream* stream);
+   void unpackUpdate(NetConnection *conn, BitStream* stream);
+
+   // SceneObject
+   void setTransform(const MatrixF &mat);
+   void prepRenderImage(SceneRenderState* state);
+
+   // GameBase
+   bool onNewDataBlock(GameBaseData *dptr, bool reload);
+
+   // CollisionTrigger
+   void setTriggerPolyhedron(const Polyhedron&);
+
+   void      potentialEnterObject(GameBase*);
+   U32       getNumCollisionTriggeringObjects() const;
+   GameBase* getObject(const U32);
+   const Vector<GameBase*>& getObjects() const { return mObjects; }
+
+   void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat);
+
+   bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
+};
+
+inline U32 CollisionTrigger::getNumCollisionTriggeringObjects() const
+{
+   return mObjects.size();
+}
+
+inline GameBase* CollisionTrigger::getObject(const U32 index)
+{
+   AssertFatal(index < getNumCollisionTriggeringObjects(), "Error, out of range object index");
+
+   return mObjects[index];
+}
+
+#endif // _H_CollisionTrigger
+

+ 640 - 0
Engine/source/T3D/components/component.cpp

@@ -0,0 +1,640 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "console/simBase.h"
+#include "console/consoleTypes.h"
+#include "T3D/components/component.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "core/stream/bitStream.h"
+#include "console/engineAPI.h"
+#include "sim/netConnection.h"
+#include "console/consoleInternal.h"
+
+#define DECLARE_NATIVE_COMPONENT( ComponentType )                   \
+	 Component* staticComponentTemplate = new ComponentType; \
+     Sim::gNativeComponentSet->addObject(staticComponentTemplate);
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+
+Component::Component()
+{
+   mFriendlyName = StringTable->lookup("");
+   mFromResource = StringTable->lookup("");
+   mComponentType = StringTable->lookup("");
+   mComponentGroup = StringTable->lookup("");
+   mNetworkType = StringTable->lookup("");
+   mTemplateName = StringTable->lookup("");
+   //mDependency = StringTable->lookup("");
+
+   mNetworked = false;
+
+
+   // [tom, 1/12/2007] We manage the memory for the description since it
+   // could be loaded from a file and thus massive. This is accomplished with
+   // protected fields, but since they still call Con::getData() the field
+   // needs to always be valid. This is pretty lame.
+   mDescription = new char[1];
+   ((char *)mDescription)[0] = 0;
+
+   mOwner = NULL;
+
+   mCanSaveFieldDictionary = false;
+
+   mNetFlags.set(Ghostable);
+}
+
+Component::~Component()
+{
+   for (S32 i = 0; i < mFields.size(); ++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(Component);
+
+//////////////////////////////////////////////////////////////////////////
+
+void Component::initPersistFields()
+{
+   addGroup("Component");
+   addField("componentType", TypeCaseString, Offset(mComponentType, Component), "The type of behavior.", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+   addField("networkType", TypeCaseString, Offset(mNetworkType, Component), "The type of behavior.", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+   addField("friendlyName", TypeCaseString, Offset(mFriendlyName, Component), "Human friendly name of this behavior", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+      addProtectedField("description", TypeCaseString, Offset(mDescription, Component), &setDescription, &getDescription,
+         "The description of this behavior which can be set to a \"string\" or a fileName\n", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+
+      addField("networked", TypeBool, Offset(mNetworked, Component), "Is this behavior ghosted to clients?", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+
+      addProtectedField("Owner", TypeSimObjectPtr, Offset(mOwner, Component), &setOwner, &defaultProtectedGetFn, "", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+
+      //addField("hidden", TypeBool, Offset(mHidden, Component), "Flags if this behavior is shown in the editor or not", AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
+      addProtectedField("enabled", TypeBool, Offset(mEnabled, Component), &_setEnabled, &defaultProtectedGetFn, "");
+   endGroup("Component");
+
+   Parent::initPersistFields();
+
+   //clear out irrelevent fields
+   removeField("name");
+   //removeField("internalName");
+   removeField("parentGroup");
+   //removeField("class");
+   removeField("superClass");
+   removeField("hidden");
+   removeField("canSave");
+   removeField("canSaveDynamicFields");
+   removeField("persistentId");
+}
+
+bool Component::_setEnabled(void *object, const char *index, const char *data)
+{
+   Component *c = static_cast<Component*>(object);
+
+   c->mEnabled = dAtob(data);
+   c->setMaskBits(EnableMask);
+
+   return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+bool Component::setDescription(void *object, const char *index, const char *data)
+{
+   Component *bT = static_cast<Component *>(object);
+   SAFE_DELETE_ARRAY(bT->mDescription);
+   bT->mDescription = bT->getDescriptionText(data);
+
+   // We return false since we don't want the console to mess with the data
+   return false;
+}
+
+const char * Component::getDescription(void* obj, const char* data)
+{
+   Component *object = static_cast<Component *>(obj);
+
+   return object->mDescription ? object->mDescription : "";
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool Component::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   setMaskBits(UpdateMask);
+
+   return true;
+}
+
+void Component::onRemove()
+{
+   onDataSet.removeAll();
+
+   if (mOwner)
+   {
+      //notify our removal to the owner, so we have no loose ends
+      mOwner->removeComponent(this, false);
+   }
+
+   Parent::onRemove();
+}
+
+void Component::onComponentAdd()
+{
+   if (isServerObject())
+   {
+      if (isMethod("onAdd"))
+         Con::executef(this, "onAdd");
+   }
+
+   mEnabled = true;
+}
+
+void Component::onComponentRemove()
+{
+   mEnabled = false;
+
+   if (isServerObject())
+   {
+      if (isMethod("onRemove"))
+         Con::executef(this, "onRemove");
+   }
+
+   if (mOwner)
+   {
+      mOwner->onComponentAdded.remove(this, &Component::componentAddedToOwner);
+      mOwner->onComponentRemoved.remove(this, &Component::componentRemovedFromOwner);
+      mOwner->onTransformSet.remove(this, &Component::ownerTransformSet);
+   }
+
+   mOwner = NULL;
+   setDataField("owner", NULL, "");
+}
+
+void Component::setOwner(Entity* owner)
+{
+   //first, catch if we have an existing owner, and we're changing from it
+   if (mOwner && mOwner != owner)
+   {
+      mOwner->onComponentAdded.remove(this, &Component::componentAddedToOwner);
+      mOwner->onComponentRemoved.remove(this, &Component::componentRemovedFromOwner);
+      mOwner->onTransformSet.remove(this, &Component::ownerTransformSet);
+
+      mOwner->removeComponent(this, false);
+   }
+
+   mOwner = owner;
+
+   if (mOwner != NULL)
+   {
+      mOwner->onComponentAdded.notify(this, &Component::componentAddedToOwner);
+      mOwner->onComponentRemoved.notify(this, &Component::componentRemovedFromOwner);
+      mOwner->onTransformSet.notify(this, &Component::ownerTransformSet);
+   }
+
+   if (isServerObject())
+      setMaskBits(OwnerMask);
+}
+
+void Component::componentAddedToOwner(Component *comp)
+{
+   return;
+}
+
+void Component::componentRemovedFromOwner(Component *comp)
+{
+   return;
+}
+
+void Component::ownerTransformSet(MatrixF *mat)
+{
+   return;
+}
+
+U32 Component::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (mask & OwnerMask)
+   {
+      if (mOwner != NULL)
+      {
+         S32 ghostIndex = con->getGhostIndex(mOwner);
+
+         if (ghostIndex == -1)
+         {
+            stream->writeFlag(false);
+            retMask |= OwnerMask;
+         }
+         else
+         {
+            stream->writeFlag(true);
+            stream->writeFlag(true);
+            stream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);
+         }
+      }
+      else
+      {
+         stream->writeFlag(true);
+         stream->writeFlag(false);
+      }
+   }
+   else
+      stream->writeFlag(false);
+
+   if (stream->writeFlag(mask & EnableMask))
+   {
+      stream->writeFlag(mEnabled);
+   }
+
+   return retMask;
+}
+
+void Component::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if (stream->readFlag())
+   {
+      if (stream->readFlag())
+      {
+         //we have an owner object, so fetch it
+         S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
+
+         Entity *e = dynamic_cast<Entity*>(con->resolveGhost(gIndex));
+         if (e)
+            e->addComponent(this);
+      }
+      else
+      {
+         //it's being nulled out
+         setOwner(NULL);
+      }
+   }
+
+   if (stream->readFlag())
+   {
+      mEnabled = stream->readFlag();
+   }
+}
+
+void Component::packToStream(Stream &stream, U32 tabStop, S32 behaviorID, U32 flags /* = 0  */)
+{
+   char buffer[1024];
+
+   writeFields(stream, tabStop);
+
+   // Write out the fields which the behavior template knows about
+   for (int i = 0; i < getComponentFieldCount(); i++)
+   {
+      ComponentField *field = getComponentField(i);
+      const char *objFieldValue = getDataField(field->mFieldName, NULL);
+
+      // If the field holds the same value as the template's default value than it
+      // will get initialized by the template, and so it won't be included just
+      // to try to keep the object files looking as non-horrible as possible.
+      if (dStrcmp(field->mDefaultValue, objFieldValue) != 0)
+      {
+         dSprintf(buffer, sizeof(buffer), "%s = \"%s\";\n", field->mFieldName, (dStrlen(objFieldValue) > 0 ? objFieldValue : "0"));
+
+         stream.writeTabs(tabStop);
+         stream.write(dStrlen(buffer), buffer);
+      }
+   }
+}
+
+void Component::processTick()
+{
+   if (isServerObject() && mEnabled)
+   {
+      if (mOwner != NULL && isMethod("Update"))
+         Con::executef(this, "Update");
+   }
+}
+
+void Component::setDataField(StringTableEntry slotName, const char *array, const char *value)
+{
+   Parent::setDataField(slotName, array, value);
+
+   onDataSet.trigger(this, slotName, value);
+}
+
+
+//catch any behavior field updates
+void Component::onStaticModified(const char* slotName, const char* newValue)
+{
+   Parent::onStaticModified(slotName, newValue);
+
+   //If we don't have an owner yet, then this is probably the initial setup, so we don't need the console callbacks yet.
+   if (!mOwner)
+      return;
+
+   onDataSet.trigger(this, slotName, newValue);
+
+   checkComponentFieldModified(slotName, newValue);
+}
+
+void Component::onDynamicModified(const char* slotName, const char* newValue)
+{
+   Parent::onDynamicModified(slotName, newValue);
+
+   //If we don't have an owner yet, then this is probably the initial setup, so we don't need the console callbacks yet.
+   if (!mOwner)
+      return;
+
+   checkComponentFieldModified(slotName, newValue);
+}
+
+void Component::checkComponentFieldModified(const char* slotName, const char* newValue)
+{
+   StringTableEntry slotNameEntry = StringTable->insert(slotName);
+
+   //find if it's a behavior field
+   for (int i = 0; i < mFields.size(); i++)
+   {
+      ComponentField *field = getComponentField(i);
+      if (field->mFieldName == slotNameEntry)
+      {
+         //we have a match, do the script callback that we updated a field
+         if (isMethod("onInspectorUpdate"))
+            Con::executef(this, "onInspectorUpdate", slotName);
+
+         return;
+      }
+   }
+}
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+void Component::addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue /* = NULL */, const char *userData /* = NULL */, /*const char* dependency /* = NULL *//*,*/ bool hidden /* = false */)
+{
+   StringTableEntry stFieldName = StringTable->insert(fieldName);
+
+   for (S32 i = 0; i < mFields.size(); ++i)
+   {
+      if (mFields[i].mFieldName == stFieldName)
+         return;
+   }
+
+   ComponentField field;
+   field.mFieldName = stFieldName;
+
+   //find the field type
+   S32 fieldTypeMask = -1;
+   StringTableEntry fieldType = StringTable->insert(type);
+
+   if (fieldType == StringTable->insert("int"))
+      fieldTypeMask = TypeS32;
+   else if (fieldType == StringTable->insert("float"))
+      fieldTypeMask = TypeF32;
+   else if (fieldType == StringTable->insert("vector"))
+      fieldTypeMask = TypePoint3F;
+   else if (fieldType == StringTable->insert("material"))
+      fieldTypeMask = TypeMaterialName;
+   else if (fieldType == StringTable->insert("image"))
+      fieldTypeMask = TypeImageFilename;
+   else if (fieldType == StringTable->insert("shape"))
+      fieldTypeMask = TypeShapeFilename;
+   else if (fieldType == StringTable->insert("bool"))
+      fieldTypeMask = TypeBool;
+   else if (fieldType == StringTable->insert("object"))
+      fieldTypeMask = TypeSimObjectPtr;
+   else
+      fieldTypeMask = TypeString;
+
+   field.mFieldType = fieldTypeMask;
+
+   field.mUserData = StringTable->insert(userData ? userData : "");
+   field.mDefaultValue = StringTable->insert(defaultValue ? defaultValue : "");
+   field.mFieldDescription = getDescriptionText(desc);
+
+   field.mGroup = mComponentGroup;
+
+   field.mHidden = hidden;
+
+   mFields.push_back(field);
+
+   //Before we set this, we need to do a test to see if this field was already set, like from the mission file or a taml file
+   const char* curFieldData = getDataField(field.mFieldName, NULL);
+
+   if (dStrIsEmpty(curFieldData))
+      setDataField(field.mFieldName, NULL, field.mDefaultValue);
+}
+
+ComponentField* Component::getComponentField(const char *fieldName)
+{
+   StringTableEntry stFieldName = StringTable->insert(fieldName);
+
+   for (S32 i = 0; i < mFields.size(); ++i)
+   {
+      if (mFields[i].mFieldName == stFieldName)
+         return &mFields[i];
+   }
+
+   return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+const char * Component::getDescriptionText(const char *desc)
+{
+   if (desc == NULL)
+      return NULL;
+
+   char *newDesc;
+
+   // [tom, 1/12/2007] If it isn't a file, just do it the easy way
+   if (!Platform::isFile(desc))
+   {
+      newDesc = new char[dStrlen(desc) + 1];
+      dStrcpy(newDesc, desc);
+
+      return newDesc;
+   }
+
+   FileStream str;
+   str.open(desc, Torque::FS::File::Read);
+
+   Stream *stream = &str;
+   if (stream == NULL){
+      str.close();
+      return NULL;
+   }
+
+   U32 size = stream->getStreamSize();
+   if (size > 0)
+   {
+      newDesc = new char[size + 1];
+      if (stream->read(size, (void *)newDesc))
+         newDesc[size] = 0;
+      else
+      {
+         SAFE_DELETE_ARRAY(newDesc);
+      }
+   }
+
+   str.close();
+   delete stream;
+
+   return newDesc;
+}
+//////////////////////////////////////////////////////////////////////////
+void Component::beginFieldGroup(const char* groupName)
+{
+   if (dStrcmp(mComponentGroup, ""))
+   {
+      Con::errorf("Component: attempting to begin new field group with a group already begun!");
+      return;
+   }
+
+   mComponentGroup = StringTable->insert(groupName);
+}
+
+void Component::endFieldGroup()
+{
+   mComponentGroup = StringTable->insert("");
+}
+
+void Component::addDependency(StringTableEntry name)
+{
+   mDependencies.push_back_unique(name);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Console Methods
+//////////////////////////////////////////////////////////////////////////
+ConsoleMethod(Component, beginGroup, void, 3, 3, "(groupName)\n"
+   "Starts the grouping for following fields being added to be grouped into\n"
+   "@param groupName The name of this group\n"
+   "@param desc The Description of this field\n"
+   "@param type The DataType for this field (default, int, float, Point2F, bool, enum, Object, keybind, color)\n"
+   "@param defaultValue The Default value for this field\n"
+   "@param userData An extra data field that can be used for custom data on a per-field basis<br>Usage for default types<br>"
+   "-enum: a TAB separated list of possible values<br>"
+   "-object: the T2D object type that are valid choices for the field.  The object types observe inheritance, so if you have a t2dSceneObject field you will be able to choose t2dStaticSrpites, t2dAnimatedSprites, etc.\n"
+   "@return Nothing\n")
+{
+   object->beginFieldGroup(argv[2]);
+}
+
+ConsoleMethod(Component, endGroup, void, 2, 2, "()\n"
+   "Ends the grouping for prior fields being added to be grouped into\n"
+   "@param groupName The name of this group\n"
+   "@param desc The Description of this field\n"
+   "@param type The DataType for this field (default, int, float, Point2F, bool, enum, Object, keybind, color)\n"
+   "@param defaultValue The Default value for this field\n"
+   "@param userData An extra data field that can be used for custom data on a per-field basis<br>Usage for default types<br>"
+   "-enum: a TAB separated list of possible values<br>"
+   "-object: the T2D object type that are valid choices for the field.  The object types observe inheritance, so if you have a t2dSceneObject field you will be able to choose t2dStaticSrpites, t2dAnimatedSprites, etc.\n"
+   "@return Nothing\n")
+{
+   object->endFieldGroup();
+}
+
+DefineConsoleMethod(Component, addComponentField, void, (String fieldName, String fieldDesc, String fieldType, String defValue, String userData, bool hidden),
+   ("", "", "", "", "", false),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   object->addComponentField(fieldName, fieldDesc, fieldType, defValue, userData, hidden);
+}
+
+ConsoleMethod(Component, getComponentFieldCount, S32, 2, 2, "() - Get the number of ComponentField's on this object\n"
+   "@return Returns the number of BehaviorFields as a nonnegative integer\n")
+{
+   return object->getComponentFieldCount();
+}
+
+// [tom, 1/12/2007] Field accessors split into multiple methods to allow space
+// for long descriptions and type data.
+
+ConsoleMethod(Component, getComponentField, const char *, 3, 3, "(int index) - Gets a Tab-Delimited list of information about a ComponentField specified by Index\n"
+   "@param index The index of the behavior\n"
+   "@return FieldName, FieldType and FieldDefaultValue, each separated by a TAB character.\n")
+{
+   ComponentField *field = object->getComponentField(dAtoi(argv[2]));
+   if (field == NULL)
+      return "";
+
+   char *buf = Con::getReturnBuffer(1024);
+   dSprintf(buf, 1024, "%s\t%s\t%s\t%s", field->mFieldName, field->mFieldType, field->mDefaultValue, field->mGroup);
+
+   return buf;
+}
+
+ConsoleMethod(Component, setComponentield, const char *, 3, 3, "(int index) - Gets a Tab-Delimited list of information about a ComponentField specified by Index\n"
+   "@param index The index of the behavior\n"
+   "@return FieldName, FieldType and FieldDefaultValue, each separated by a TAB character.\n")
+{
+   ComponentField *field = object->getComponentField(dAtoi(argv[2]));
+   if (field == NULL)
+      return "";
+
+   char *buf = Con::getReturnBuffer(1024);
+   dSprintf(buf, 1024, "%s\t%s\t%s", field->mFieldName, field->mFieldType, field->mDefaultValue);
+
+   return buf;
+}
+
+ConsoleMethod(Component, getBehaviorFieldUserData, const char *, 3, 3, "(int index) - Gets the UserData associated with a field by index in the field list\n"
+   "@param index The index of the behavior\n"
+   "@return Returns a string representing the user data of this field\n")
+{
+   ComponentField *field = object->getComponentField(dAtoi(argv[2]));
+   if (field == NULL)
+      return "";
+
+   return field->mUserData;
+}
+
+ConsoleMethod(Component, getComponentFieldDescription, const char *, 3, 3, "(int index) - Gets a field description by index\n"
+   "@param index The index of the behavior\n"
+   "@return Returns a string representing the description of this field\n")
+{
+   ComponentField *field = object->getComponentField(dAtoi(argv[2]));
+   if (field == NULL)
+      return "";
+
+   return field->mFieldDescription ? field->mFieldDescription : "";
+}
+
+ConsoleMethod(Component, addDependency, void, 3, 3, "(string behaviorName) - Gets a field description by index\n"
+   "@param index The index of the behavior\n"
+   "@return Returns a string representing the description of this field\n")
+{
+   object->addDependency(argv[2]);
+}
+
+ConsoleMethod(Component, setDirty, void, 2, 2, "() - Gets a field description by index\n"
+   "@param index The index of the behavior\n"
+   "@return Returns a string representing the description of this field\n")
+{
+   object->setMaskBits(Component::OwnerMask);
+}

+ 197 - 0
Engine/source/T3D/components/component.h

@@ -0,0 +1,197 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef COMPONENT_H
+#define COMPONENT_H
+
+#ifndef _NETOBJECT_H_
+#include "sim/netObject.h"
+#endif
+#ifndef ENTITY_H
+#include "T3D/entity.h"
+#endif
+#ifndef CORE_INTERFACES_H
+#include "T3D/components/coreInterfaces.h"
+#endif
+
+class Entity;
+
+struct ComponentField
+{
+   StringTableEntry mFieldName;
+   StringTableEntry mFieldDescription;
+
+   S32 mFieldType;
+   StringTableEntry mUserData;
+
+   StringTableEntry mDefaultValue;
+
+   StringTableEntry mGroup;
+
+   StringTableEntry mDependency;
+
+   bool mHidden;
+};
+
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class Component : public NetObject, public UpdateInterface
+{
+   typedef NetObject Parent;
+
+protected:
+   StringTableEntry mFriendlyName;
+   StringTableEntry mDescription;
+
+   StringTableEntry mFromResource;
+   StringTableEntry mComponentGroup;
+   StringTableEntry mComponentType;
+   StringTableEntry mNetworkType;
+   StringTableEntry mTemplateName;
+
+   Vector<StringTableEntry> mDependencies;
+   Vector<ComponentField> mFields;
+
+   bool mNetworked;
+
+   U32 componentIdx;
+
+   Entity*              mOwner;
+   bool					   mHidden;
+   bool					   mEnabled;
+
+public:
+   Component();
+   virtual ~Component();
+   DECLARE_CONOBJECT(Component);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void packToStream(Stream &stream, U32 tabStop, S32 behaviorID, U32 flags = 0);
+
+   //This is called when we are added to an entity
+   virtual void onComponentAdd();            
+   //This is called when we are removed from an entity
+   virtual void onComponentRemove();         
+
+   //This is called when a different component is added to our owner entity
+   virtual void componentAddedToOwner(Component *comp);  
+   //This is called when a different component is removed from our owner entity
+   virtual void componentRemovedFromOwner(Component *comp);  
+
+   virtual void ownerTransformSet(MatrixF *mat);
+
+   void setOwner(Entity* pOwner);
+   inline Entity *getOwner() { return mOwner ? mOwner : NULL; }
+   static bool setOwner(void *object, const char *index, const char *data) { return true; }
+
+   bool	isEnabled() { return mEnabled; }
+   void  setEnabled(bool toggle) { mEnabled = toggle; setMaskBits(EnableMask); }
+
+   bool isActive() { return mEnabled && mOwner != NULL; }
+
+   static bool _setEnabled(void *object, const char *index, const char *data);
+
+   virtual void processTick();
+   virtual void interpolateTick(F32 dt){}
+   virtual void advanceTime(F32 dt){}
+
+   /// @name Adding Named Fields
+   /// @{
+
+   /// Adds a named field to a Component that can specify a description, data type, default value and userData
+   ///
+   /// @param   fieldName    The name of the Field
+   /// @param   desc         The Description of the Field
+   /// @param   type         The Type of field that this is, example 'Text' or 'Bool'
+   /// @param   defaultValue The Default value of this field
+   /// @param   userData     An extra optional field that can be used for user data
+   void addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue = NULL, const char *userData = NULL, bool hidden = false);
+
+   /// Returns the number of ComponentField's on this template
+   inline S32 getComponentFieldCount() { return mFields.size(); };
+
+   /// Gets a ComponentField by its index in the mFields vector 
+   /// @param idx  The index of the field in the mField vector
+   inline ComponentField *getComponentField(S32 idx)
+   {
+      if (idx < 0 || idx >= mFields.size())
+         return NULL;
+
+      return &mFields[idx];
+   }
+
+   ComponentField *getComponentField(const char* fieldName);
+
+   const char* getComponentType() { return mComponentType; }
+
+   const char *getDescriptionText(const char *desc);
+
+   const char *getName() { return mTemplateName; }
+
+   const char *getFriendlyName() { return mFriendlyName; }
+
+   bool isNetworked() { return mNetworked; }
+
+   void beginFieldGroup(const char* groupName);
+   void endFieldGroup();
+
+   void addDependency(StringTableEntry name);
+   /// @}
+
+   /// @name Description
+   /// @{
+   static bool setDescription(void *object, const char *index, const char *data);
+   static const char* getDescription(void* obj, const char* data);
+
+   /// @Primary usage functions
+   /// @These are used by the various engine-based behaviors to integrate with the component classes
+   enum NetMaskBits
+   {
+      InitialUpdateMask = BIT(0),
+      OwnerMask = BIT(1),
+      UpdateMask = BIT(2),
+      EnableMask = BIT(3),
+      NextFreeMask = BIT(4)
+   };
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+   /// @}
+
+   Signal< void(SimObject*, String, String) > onDataSet;
+   virtual void setDataField(StringTableEntry slotName, const char *array, const char *value);
+
+   virtual void onStaticModified(const char* slotName, const char* newValue); ///< Called when a static field is modified.
+   virtual void onDynamicModified(const char* slotName, const char*newValue = NULL); ///< Called when a dynamic field is modified.
+
+   /// This is what we actually use to check if the modified field is one of our behavior fields. If it is, we update and make the correct callbacks
+   void checkComponentFieldModified(const char* slotName, const char* newValue);
+
+   virtual void checkDependencies(){}
+};
+
+#endif // COMPONENT_H

+ 101 - 0
Engine/source/T3D/components/coreInterfaces.h

@@ -0,0 +1,101 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef CORE_INTERFACES_H
+#define CORE_INTERFACES_H
+
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+
+template<typename T>
+class Interface
+{
+public:
+   static Vector<T*> all;
+
+   Interface()
+   {
+      all.push_back((T*)this);
+   }
+   virtual ~Interface()
+   {
+      for (U32 i = 0; i < all.size(); i++)
+      {
+         if (all[i] == (T*)this)
+         {
+            all.erase(i);
+            return;
+         }
+      }
+   }
+};
+template<typename T> Vector<T*> Interface<T>::all(0);
+
+//Basically a file for generic interfaces that many behaviors may make use of
+class SetTransformInterface// : public Interface<SetTransformInterface>
+{
+public:
+   virtual void setTransform( MatrixF transform );
+   virtual void setTransform( Point3F pos, EulerF rot );
+};
+
+class UpdateInterface : public Interface<UpdateInterface>
+{
+public:
+   virtual void processTick(){}
+   virtual void interpolateTick(F32 dt){}
+   virtual void advanceTime(F32 dt){}
+};
+
+class BehaviorFieldInterface// : public Interface<BehaviorFieldInterface>
+{
+public:
+   virtual void onFieldChange(const char* fieldName, const char* newValue){};
+};
+
+class CameraInterface// : public Interface<CameraInterface>
+{
+public:
+   virtual bool getCameraTransform(F32* pos,MatrixF* mat)=0;
+   virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)=0;
+   virtual Frustum getFrustum()=0;
+   virtual F32 getCameraFov()=0;
+   virtual void setCameraFov(F32 fov)=0;
+
+   virtual bool isValidCameraFov(F32 fov)=0;
+};
+
+class CastRayInterface// : public Interface<CastRayInterface>
+{
+public:
+   virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info)=0;
+};
+
+class EditorInspectInterface// : public Interface<EditorInspectInterface>
+{
+public:
+   virtual void onInspect()=0;
+   virtual void onEndInspect()=0;
+};
+
+#endif

+ 434 - 0
Engine/source/T3D/components/game/stateMachine.cpp

@@ -0,0 +1,434 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/game/stateMachine.h"
+
+StateMachine::StateMachine()
+{
+   mStateStartTime = -1;
+   mStateTime = 0;
+
+   mStartingState = "";
+
+   mCurCreateState = NULL;
+}
+
+StateMachine::~StateMachine()
+{
+}
+
+void StateMachine::loadStateMachineFile()
+{
+   if (!mXMLReader)
+   {
+      SimXMLDocument *xmlrdr = new SimXMLDocument();
+      xmlrdr->registerObject();
+
+      mXMLReader = xmlrdr;
+   }
+
+   bool hasStartState = false;
+
+   if (!dStrIsEmpty(mStateMachineFile))
+   {
+      //use our xml reader to parse the file!
+      SimXMLDocument *reader = mXMLReader.getObject();
+      if (!reader->loadFile(mStateMachineFile))
+         Con::errorf("Could not load state machine file: &s", mStateMachineFile);
+
+      if (!reader->pushFirstChildElement("StateMachine"))
+         return;
+
+      //find our starting state
+      if (reader->pushFirstChildElement("StartingState"))
+      {
+         mStartingState = reader->getData();
+         reader->popElement();
+         hasStartState = true;
+      }
+
+      readStates();
+   }
+
+   if (hasStartState)
+      mCurrentState = getStateByName(mStartingState);
+
+   mStateStartTime = -1;
+   mStateTime = 0;
+}
+
+void StateMachine::readStates()
+{
+   SimXMLDocument *reader = mXMLReader.getObject();
+
+   //iterate through our states now!
+   if (reader->pushFirstChildElement("State"))
+   {
+      //get our first state
+      State firstState;
+
+      readStateName(&firstState, reader);
+      readStateScriptFunction(&firstState, reader);
+
+      readTransitions(firstState);
+
+      mStates.push_back(firstState);
+
+      //now, iterate the siblings
+      while (reader->nextSiblingElement("State"))
+      {
+         State newState;
+         readStateName(&newState, reader);
+         readStateScriptFunction(&newState, reader);
+
+         readTransitions(newState);
+
+         mStates.push_back(newState);
+      }
+   }
+}
+
+void StateMachine::readTransitions(State &currentState)
+{
+   SimXMLDocument *reader = mXMLReader.getObject();
+
+   //iterate through our states now!
+   if (reader->pushFirstChildElement("Transition"))
+   {
+      //get our first state
+      StateTransition firstTransition;
+
+      readTransitonTarget(&firstTransition, reader);
+
+      readConditions(firstTransition);
+
+      currentState.mTransitions.push_back(firstTransition);
+
+      //now, iterate the siblings
+      while (reader->nextSiblingElement("Transition"))
+      {
+         StateTransition newTransition;
+         readTransitonTarget(&newTransition, reader);
+
+         readConditions(newTransition);
+
+         currentState.mTransitions.push_back(newTransition);
+      }
+
+      reader->popElement();
+   }
+}
+
+void StateMachine::readConditions(StateTransition &currentTransition)
+{
+   SimXMLDocument *reader = mXMLReader.getObject();
+
+   //iterate through our states now!
+   if (reader->pushFirstChildElement("Rule"))
+   {
+      //get our first state
+      StateTransition::Condition firstCondition;
+      StateField firstField;
+      bool fieldRead = false;
+      
+      readFieldName(&firstField, reader);
+      firstCondition.field = firstField;
+
+      readFieldComparitor(&firstCondition, reader);
+
+      readFieldValue(&firstCondition.field, reader);
+
+      currentTransition.mTransitionRules.push_back(firstCondition);
+
+      //now, iterate the siblings
+      while (reader->nextSiblingElement("Transition"))
+      {
+         StateTransition::Condition newCondition;
+         StateField newField;
+
+         readFieldName(&newField, reader);
+         newCondition.field = newField;
+
+         readFieldComparitor(&newCondition, reader);
+
+         readFieldValue(&newCondition.field, reader);
+
+         currentTransition.mTransitionRules.push_back(newCondition);
+      }
+
+      reader->popElement();
+   }
+}
+
+S32 StateMachine::parseComparitor(const char* comparitorName)
+{
+   S32 targetType = -1;
+
+   if (!dStrcmp("GreaterThan", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::GeaterThan;
+   else if (!dStrcmp("GreaterOrEqual", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::GreaterOrEqual;
+   else if (!dStrcmp("LessThan", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::LessThan;
+   else if (!dStrcmp("LessOrEqual", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::LessOrEqual;
+   else if (!dStrcmp("Equals", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::Equals;
+   else if (!dStrcmp("True", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::True;
+   else if (!dStrcmp("False", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::False;
+   else if (!dStrcmp("Negative", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::Negative;
+   else if (!dStrcmp("Positive", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::Positive;
+   else if (!dStrcmp("DoesNotEqual", comparitorName))
+      targetType = StateMachine::StateTransition::Condition::DoesNotEqual;
+
+   return targetType;
+}
+
+void StateMachine::update()
+{
+   //we always check if there's a timout transition, as that's the most generic transition possible.
+   F32 curTime = Sim::getCurrentTime();
+
+   if (mStateStartTime == -1)
+      mStateStartTime = curTime;
+
+   mStateTime = curTime - mStateStartTime;
+
+   char buffer[64];
+   dSprintf(buffer, sizeof(buffer), "%g", mStateTime);
+
+   checkTransitions("stateTime", buffer);
+}
+
+void StateMachine::checkTransitions(const char* slotName, const char* newValue)
+{
+   //because we use our current state's fields as dynamic fields on the instance
+   //we'll want to catch any fields being set so we can treat changes as transition triggers if
+   //any of the transitions on this state call for it
+
+   //One example would be in order to implement burst fire on a weapon state machine.
+   //The behavior instance has a dynamic variable set up like: GunStateMachine.burstShotCount = 0;
+
+   //We also have a transition in our fire state, as: GunStateMachine.addTransition("FireState", "burstShotCount", "DoneShooting", 3);
+   //What that does is for our fire state, we check the dynamicField burstShotCount if it's equal or greater than 3. If it is, we perform the transition.
+
+   //As state fields are handled as dynamicFields for the instance, regular dynamicFields are processed as well as state fields. So we can use the regular 
+   //dynamic fields for our transitions, to act as 'global' variables that are state-agnostic. Alternately, we can use state-specific fields, such as a transition
+   //like this:
+   //GunStateMachine.addTransition("IdleState", "Fidget", "Timeout", ">=", 5000);
+
+   //That uses the the timeout field, which is reset each time the state changes, and so state-specific, to see if it's been 5 seconds. If it has been, we transition
+   //to our fidget state
+
+   //so, lets check our current transitions
+   //now that we have the type, check our transitions!
+   for (U32 t = 0; t < mCurrentState.mTransitions.size(); t++)
+   {
+      //if (!dStrcmp(mCurrentState.mTransitions[t]., slotName))
+      {
+         //found a transition looking for this variable, so do work
+         //first, figure out what data type thie field is
+         //S32 type = getVariableType(newValue);
+
+         bool fail = false;
+         bool match = false;
+         S32 ruleCount = mCurrentState.mTransitions[t].mTransitionRules.size();
+
+         for (U32 r = 0; r < ruleCount; r++)
+         {
+            const char* fieldName = mCurrentState.mTransitions[t].mTransitionRules[r].field.name;
+            if (!dStrcmp(fieldName, slotName))
+            {
+               match = true;
+               //now, check the value with the comparitor and see if we do the transition.
+               if (!passComparitorCheck(newValue, mCurrentState.mTransitions[t].mTransitionRules[r]))
+               {
+                  fail = true;
+                  break;
+               }
+            }
+         }
+
+         //If we do have a transition rule for this field, and we didn't fail on the condition, go ahead and switch states
+         if (match && !fail)
+         {
+            setState(mCurrentState.mTransitions[t].mStateTarget);
+
+            return;
+         }
+      }
+   }
+}
+
+bool StateMachine::passComparitorCheck(const char* var, StateTransition::Condition transitionRule)
+{
+   F32 num = dAtof(var);
+   switch (transitionRule.field.fieldType)
+   {
+   case StateField::Type::VectorType:
+      switch (transitionRule.triggerComparitor)
+      {
+      case StateTransition::Condition::Equals:
+      case StateTransition::Condition::GeaterThan:
+      case StateTransition::Condition::GreaterOrEqual:
+      case StateTransition::Condition::LessThan:
+      case StateTransition::Condition::LessOrEqual:
+      case StateTransition::Condition::DoesNotEqual:
+         //do
+         break;
+      default:
+         return false;
+      };
+   case StateField::Type::StringType:
+      switch (transitionRule.triggerComparitor)
+      {
+      case StateTransition::Condition::Equals:
+         if (!dStrcmp(var, transitionRule.field.triggerStringVal))
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::DoesNotEqual:
+         if (dStrcmp(var, transitionRule.field.triggerStringVal))
+            return true;
+         else
+            return false;
+      default:
+         return false;
+      };
+   case StateField::Type::BooleanType:
+      switch (transitionRule.triggerComparitor)
+      {
+      case StateTransition::Condition::TriggerValueTarget::True:
+         if (dAtob(var))
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::False:
+         if (dAtob(var))
+            return false;
+         else
+            return true;
+      default:
+         return false;
+      };
+   case StateField::Type::NumberType:
+      switch (transitionRule.triggerComparitor)
+      {
+      case StateTransition::Condition::TriggerValueTarget::Equals:
+         if (num == transitionRule.field.triggerNumVal)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::GeaterThan:
+         if (num > transitionRule.field.triggerNumVal)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::GreaterOrEqual:
+         if (num >= transitionRule.field.triggerNumVal)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::LessThan:
+         if (num < transitionRule.field.triggerNumVal)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::LessOrEqual:
+         if (num <= transitionRule.field.triggerNumVal)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::DoesNotEqual:
+         if (num != transitionRule.field.triggerNumVal)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::Positive:
+         if (num > 0)
+            return true;
+         else
+            return false;
+      case StateTransition::Condition::TriggerValueTarget::Negative:
+         if (num < 0)
+            return true;
+         else
+            return false;
+      default:
+         return false;
+      };
+   default:
+      return false;
+   };
+}
+
+void StateMachine::setState(const char* stateName, bool clearFields)
+{
+   State oldState = mCurrentState;
+   StringTableEntry sName = StringTable->insert(stateName);
+   for (U32 i = 0; i < mStates.size(); i++)
+   {
+      //if(!dStrcmp(mStates[i]->stateName, stateName))
+      if (!dStrcmp(mStates[i].stateName,sName))
+      {
+         mCurrentState = mStates[i];
+         mStateStartTime = Sim::getCurrentTime();
+
+         onStateChanged.trigger(this, i);
+         return;
+      }
+   }
+}
+
+const char* StateMachine::getStateByIndex(S32 index)
+{
+   if (index >= 0 && mStates.size() > index)
+      return mStates[index].stateName;
+   else
+      return "";
+}
+
+StateMachine::State& StateMachine::getStateByName(const char* name)
+{
+   StringTableEntry stateName = StringTable->insert(name);
+
+   for (U32 i = 0; i < mStates.size(); i++)
+   {
+      if (!dStrcmp(stateName, mStates[i].stateName))
+         return mStates[i];
+   }
+}
+
+S32 StateMachine::findFieldByName(const char* name)
+{
+   for (U32 i = 0; i < mFields.size(); i++)
+   {
+      if (!dStrcmp(mFields[i].name, name))
+         return i;
+   }
+
+   return -1;
+}

+ 259 - 0
Engine/source/T3D/components/game/stateMachine.h

@@ -0,0 +1,259 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+#ifndef _OBJECTTYPES_H_
+#include "T3D/objectTypes.h"
+#endif
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _XMLDOC_H_
+#include "console/SimXMLDocument.h"
+#endif
+
+class StateMachine
+{
+public:
+   struct StateField
+   {
+      StringTableEntry name;
+      
+      bool 			 triggerBoolVal;
+      float 		 triggerNumVal;
+      Point3F 		 triggerVectorVal;
+      String 		 triggerStringVal;
+
+      enum Type
+      {
+         BooleanType = 0,
+         NumberType,
+         VectorType,
+         StringType
+      }fieldType;
+   };
+
+   struct UniqueReference
+   {
+      SimObject* referenceObj;
+      const char* referenceVar;
+      const char* uniqueName;
+   };
+
+   struct StateTransition
+   {
+      struct Condition
+      {
+         enum TriggerValueTarget
+         {
+            Equals = 0,
+            GeaterThan,
+            LessThan,
+            GreaterOrEqual,
+            LessOrEqual,
+            True,
+            False,
+            Positive,
+            Negative,
+            DoesNotEqual
+         };
+
+         StateField field;
+
+         TriggerValueTarget   triggerComparitor;
+
+         UniqueReference      *valUniqueRef;
+      };
+
+      StringTableEntry	mName;
+      StringTableEntry	mStateTarget;
+      Vector<Condition>	mTransitionRules;
+   };
+
+   struct State 
+   {
+      Vector<StateTransition> mTransitions;
+
+      StringTableEntry stateName;
+
+      StringTableEntry callbackName;
+   };
+
+   StringTableEntry		mStateMachineFile;
+
+protected:
+   Vector<State> mStates;
+
+   Vector<StateField> mFields;
+
+   Vector<UniqueReference> mUniqueReferences;
+
+   State mCurrentState;
+
+   F32 mStateStartTime;
+   F32 mStateTime;
+
+   StringTableEntry mStartingState;
+
+   State *mCurCreateSuperState;
+   State *mCurCreateState;
+
+   SimObjectPtr<SimXMLDocument> mXMLReader;
+
+public:
+   StateMachine();
+   virtual ~StateMachine();
+
+   void update();
+
+   void loadStateMachineFile();
+   void readStates();
+   void readTransitions(State &currentState);
+   void readConditions(StateTransition &newTransition);
+
+   void setState(const char* stateName, bool clearFields = true);
+
+   const char* getCurrentStateName() { return mCurrentState.stateName; }
+   State& getCurrentState() {
+      return mCurrentState;
+   }
+
+   S32 getStateCount() { return mStates.size(); }
+   const char* getStateByIndex(S32 index);
+   State& getStateByName(const char* name);
+
+   void checkTransitions(const char* slotName, const char* newValue);
+
+   bool passComparitorCheck(const char* var, StateTransition::Condition transitionRule);
+
+   S32 findFieldByName(const char* name);
+
+   S32 getFieldsCount() { return mFields.size(); }
+
+   StateField getField(U32 index)
+   {
+      if (index <= mFields.size())
+         return mFields[index];
+   }
+
+   Signal< void(StateMachine*, S32 stateIdx) > onStateChanged;
+
+   //
+   inline bool readStateName(State* state, SimXMLDocument* reader)
+   {
+      if (reader->pushFirstChildElement("Name"))
+      {
+         state->stateName = reader->getData();
+         reader->popElement();
+
+         return true;
+      }
+
+      return false;
+   }
+   inline bool readStateScriptFunction(State* state, SimXMLDocument* reader)
+   {
+      if (reader->pushFirstChildElement("ScriptFunction"))
+      {
+         state->callbackName = reader->getData();
+         reader->popElement();
+
+         return true;
+      }
+
+      return false;
+   }
+   inline bool readTransitonTarget(StateTransition* transition, SimXMLDocument* reader)
+   {
+      if (reader->pushFirstChildElement("StateTarget"))
+      {
+         transition->mStateTarget = reader->getData();
+         reader->popElement();
+
+         return true;
+      }
+
+      return false;
+   }
+   //
+   inline bool readFieldName(StateField* newField, SimXMLDocument* reader)
+   {
+      if (reader->pushFirstChildElement("FieldName"))
+      {
+         newField->name = reader->getData();
+         reader->popElement();
+
+         return true;
+      }
+
+      return false;
+   }
+   inline bool readFieldComparitor(StateTransition::Condition* condition, SimXMLDocument* reader)
+   {
+      if (reader->pushFirstChildElement("Comparitor"))
+      {
+         S32 compIdx = parseComparitor(reader->getData());
+         condition->triggerComparitor = static_cast<StateTransition::Condition::TriggerValueTarget>(compIdx);
+         reader->popElement();
+
+         return true;
+      }
+
+      return false;
+   }
+   inline bool readFieldValue(StateField* field, SimXMLDocument* reader)
+   {
+      if (reader->pushFirstChildElement("NumValue"))
+      {
+         field->fieldType = StateField::NumberType;
+         field->triggerNumVal = dAtof(reader->getData());
+         reader->popElement();
+         return true;
+      }
+      else if (reader->pushFirstChildElement("StringValue"))
+      {
+         field->fieldType = StateField::StringType;
+         field->triggerStringVal = reader->getData();
+         reader->popElement();
+         return true;
+      }
+      else if (reader->pushFirstChildElement("BoolValue"))
+      {
+         field->fieldType = StateField::BooleanType;
+         field->triggerBoolVal = dAtob(reader->getData());
+         reader->popElement();
+         return true;
+      }
+
+      return false;
+   }
+
+private:
+   S32 parseComparitor(const char* comparitorName);
+};
+
+#endif

+ 215 - 0
Engine/source/T3D/components/game/stateMachineComponent.cpp

@@ -0,0 +1,215 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/game/stateMachineComponent.h"
+
+#include "platform/platform.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "ts/tsShapeInstance.h"
+#include "core/stream/bitStream.h"
+#include "gfx/gfxTransformSaver.h"
+#include "console/engineAPI.h"
+#include "lighting/lightQuery.h"
+
+IMPLEMENT_CALLBACK( StateMachineComponent, onStateChange, void, (), (),
+                   "@brief Called when we collide with another object.\n\n"
+                   "@param obj The ShapeBase object\n"
+                   "@param collObj The object we collided with\n"
+                   "@param vec Collision impact vector\n"
+                   "@param len Length of the impact vector\n" );
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+
+StateMachineComponent::StateMachineComponent() : Component()
+{
+   mFriendlyName = "State Machine";
+   mComponentType = "Game";
+
+   mDescription = getDescriptionText("A generic state machine.");
+
+   mStateMachineFile = "";
+
+   //doesn't need to be networked
+   mNetworked = false;
+   mNetFlags.clear();
+}
+
+StateMachineComponent::~StateMachineComponent()
+{
+   for(S32 i = 0;i < mFields.size();++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(StateMachineComponent);
+
+bool StateMachineComponent::onAdd()
+{
+   if(! Parent::onAdd())
+      return false;
+
+   // Register for the resource change signal.
+   ResourceManager::get().getChangedSignal().notify(this, &StateMachineComponent::_onResourceChanged);
+
+   mStateMachine.onStateChanged.notify(this, &StateMachineComponent::onStateChanged);
+
+   return true;
+}
+
+void StateMachineComponent::onRemove()
+{
+   Parent::onRemove();
+}
+
+U32 StateMachineComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+   return retMask;
+}
+
+void StateMachineComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+}
+
+//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
+void StateMachineComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+}
+
+void StateMachineComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+}
+
+void StateMachineComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addProtectedField("stateMachineFile", TypeFilename, Offset(mStateMachineFile, StateMachineComponent), 
+      &_setSMFile, &defaultProtectedGetFn, "The sim time of when we started this state");
+}
+
+bool StateMachineComponent::_setSMFile(void *object, const char *index, const char *data)
+{
+   StateMachineComponent* smComp = static_cast<StateMachineComponent*>(object);
+   if (smComp)
+   {
+      smComp->setStateMachineFile(data);
+      smComp->loadStateMachineFile();
+
+      return true;
+   }
+
+   return false;
+}
+
+void StateMachineComponent::_onResourceChanged(const Torque::Path &path)
+{
+   if (path != Torque::Path(mStateMachineFile))
+      return;
+
+   loadStateMachineFile();
+}
+
+void StateMachineComponent::loadStateMachineFile()
+{
+   if (!dStrIsEmpty(mStateMachineFile))
+   {
+      mStateMachine.mStateMachineFile = mStateMachineFile;
+      mStateMachine.loadStateMachineFile();
+
+      //now that it's loaded, we need to parse the SM's fields and set them as script vars on ourselves
+      S32 smFieldCount = mStateMachine.getFieldsCount();
+
+      for (U32 i = 0; i < smFieldCount; i++)
+      {
+         StateMachine::StateField field = mStateMachine.getField(i);
+
+         char buffer[128];
+
+         if (field.fieldType == StateMachine::StateField::BooleanType)
+         {
+            dSprintf(buffer, sizeof(buffer), "%b", field.triggerBoolVal);
+            setDataField(field.name, NULL, buffer);
+         }
+         else if (field.fieldType == StateMachine::StateField::NumberType)
+         {
+            dSprintf(buffer, sizeof(buffer), "%g", field.triggerNumVal);
+            setDataField(field.name, NULL, buffer);
+         }
+         else if (field.fieldType == StateMachine::StateField::StringType)
+         {
+            setDataField(field.name, NULL, field.triggerStringVal);
+         }
+      }
+   }
+}
+
+void StateMachineComponent::processTick()
+{
+   if (!isServerObject() || !isActive())
+      return;
+
+   mStateMachine.update();
+}
+
+void StateMachineComponent::onDynamicModified( const char* slotName, const char* newValue )
+{
+   Parent::onDynamicModified(slotName, newValue);
+
+   StringTableEntry fieldName = StringTable->insert(slotName);
+   mStateMachine.checkTransitions(fieldName, newValue);
+}
+
+void StateMachineComponent::onStaticModified( const char* slotName, const char* newValue )
+{
+   Parent::onStaticModified(slotName, newValue);
+
+   StringTableEntry fieldName = StringTable->insert(slotName);
+   mStateMachine.checkTransitions(fieldName, newValue);
+}
+
+void StateMachineComponent::onStateChanged(StateMachine* sm, S32 stateIdx)
+{
+   //do a script callback, if we have one
+   //check if we have a function for that, and then also check if our owner does
+   StringTableEntry callbackName = mStateMachine.getCurrentState().callbackName;
+
+   if (isMethod(callbackName))
+         Con::executef(this, callbackName);
+
+   if (mOwner->isMethod(callbackName))
+      Con::executef(mOwner, callbackName);
+}

+ 81 - 0
Engine/source/T3D/components/game/stateMachineComponent.h

@@ -0,0 +1,81 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef STATE_MACHINE_COMPONENT_H
+#define STATE_MACHINE_COMPONENT_H
+
+#ifndef COMPONENT_H
+   #include "T3D/components/component.h"
+#endif
+#ifndef STATE_MACHINE_H
+#include "T3D/components/game/stateMachine.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class StateMachineComponent : public Component
+{
+   typedef Component Parent;
+
+public:
+   StateMachine mStateMachine;
+
+protected:
+   StringTableEntry		mStateMachineFile;
+
+public:
+   StateMachineComponent();
+   virtual ~StateMachineComponent();
+   DECLARE_CONOBJECT(StateMachineComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   void _onResourceChanged(const Torque::Path &path);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void processTick();
+
+   virtual void onDynamicModified(const char* slotName, const char* newValue);
+   virtual void onStaticModified(const char* slotName, const char* newValue);
+
+   virtual void loadStateMachineFile();
+
+   void setStateMachineFile(const char* fileName) { mStateMachineFile = StringTable->insert(fileName); }
+
+   static bool _setSMFile(void *object, const char *index, const char *data);
+
+   void onStateChanged(StateMachine* sm, S32 stateIdx);
+
+   //Callbacks
+   DECLARE_CALLBACK(void, onStateChange, ());
+};
+
+#endif

+ 359 - 0
Engine/source/T3D/components/game/triggerComponent.cpp

@@ -0,0 +1,359 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+#include "console/consoleTypes.h"
+#include "T3D/components/game/triggerComponent.h"
+#include "core/util/safeDelete.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "core/stream/bitStream.h"
+#include "console/engineAPI.h"
+#include "sim/netConnection.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "T3D/components/coreInterfaces.h"
+#include "math/mathUtils.h"
+#include "collision/concretePolyList.h"
+#include "collision/clippedPolyList.h"
+
+#include "gfx/sim/debugDraw.h"
+
+IMPLEMENT_CALLBACK( TriggerComponent, onEnterViewCmd, void, 
+   ( Entity* cameraEnt, bool firstTimeSeeing ), ( cameraEnt, firstTimeSeeing ),
+   "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
+
+   "@param trigger the Trigger instance whose volume the object entered\n"
+   "@param obj the object that entered the volume of the Trigger instance\n" );
+
+IMPLEMENT_CALLBACK( TriggerComponent, onExitViewCmd, void, 
+   ( Entity* cameraEnt ), ( cameraEnt ),
+   "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
+
+   "@param trigger the Trigger instance whose volume the object entered\n"
+   "@param obj the object that entered the volume of the Trigger instance\n" );
+
+IMPLEMENT_CALLBACK( TriggerComponent, onUpdateInViewCmd, void, 
+   ( Entity* cameraEnt ), ( cameraEnt ),
+   "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
+
+   "@param trigger the Trigger instance whose volume the object entered\n"
+   "@param obj the object that entered the volume of the Trigger instance\n" );
+
+IMPLEMENT_CALLBACK( TriggerComponent, onUpdateOutOfViewCmd, void, 
+   ( Entity* cameraEnt ), ( cameraEnt ),
+   "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
+
+   "@param trigger the Trigger instance whose volume the object entered\n"
+   "@param obj the object that entered the volume of the Trigger instance\n" );
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+
+TriggerComponent::TriggerComponent() : Component()
+{
+   mObjectList.clear();
+
+   mVisible = false;
+
+   mFriendlyName = "Trigger";
+   mComponentType = "Trigger";
+
+   mDescription = getDescriptionText("Calls trigger events when a client starts and stops seeing it. Also ticks while visible to clients.");
+}
+
+TriggerComponent::~TriggerComponent()
+{
+   for(S32 i = 0;i < mFields.size();++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(TriggerComponent);
+
+
+bool TriggerComponent::onAdd()
+{
+   if(! Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void TriggerComponent::onRemove()
+{
+   Parent::onRemove();
+}
+
+//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
+void TriggerComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+
+   if(colInt)
+   {
+      colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
+   }
+}
+
+void TriggerComponent::onComponentRemove()
+{
+   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+
+   if(colInt)
+   {
+      colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
+   }
+
+   Parent::onComponentRemove();
+}
+
+void TriggerComponent::componentAddedToOwner(Component *comp)
+{
+   if (comp->getId() == getId())
+      return;
+
+   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+
+   if (colInt)
+   {
+      colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
+   }
+}
+
+void TriggerComponent::componentRemovedFromOwner(Component *comp)
+{
+   if (comp->getId() == getId()) //?????????
+      return;
+
+   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+
+   if (colInt)
+   {
+      colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
+   }
+}
+
+void TriggerComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField("visibile",   TypeBool,  Offset( mVisible, TriggerComponent ), "" );
+
+   addField("onEnterViewCmd", TypeCommand, Offset(mEnterCommand, TriggerComponent), "");
+   addField("onExitViewCmd", TypeCommand, Offset(mOnExitCommand, TriggerComponent), "");
+   addField("onUpdateInViewCmd", TypeCommand, Offset(mOnUpdateInViewCmd, TriggerComponent), "");
+}
+
+U32 TriggerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+   return retMask;
+}
+
+void TriggerComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+}
+
+void TriggerComponent::potentialEnterObject(SceneObject *collider)
+{
+   if(testObject(collider))
+   {
+      bool found = false;
+      for(U32 i=0; i < mObjectList.size(); i++)
+      {
+         if(mObjectList[i]->getId() == collider->getId())
+         {
+            found = true;
+            break;
+         }
+      }
+
+      if (!found)
+      {
+         mObjectList.push_back(collider);
+
+         if (!mEnterCommand.isEmpty())
+         {
+            String command = String("%obj = ") + collider->getIdString() + ";" + 
+               String("%this = ") + getIdString() + ";" + mEnterCommand;
+            Con::evaluate(command.c_str());
+         }
+
+         //onEnterTrigger_callback(this, enter);
+      }
+   }
+}
+
+bool TriggerComponent::testObject(SceneObject* enter)
+{
+   //First, test to early out
+   Box3F enterBox = enter->getWorldBox();
+
+   //if(!mOwner->getWorldBox().intersect(enterBox) || !)
+   //   return false;
+
+   //We're still here, so we should do actual work
+   //We're going to be 
+   ConcretePolyList mClippedList;
+
+   SphereF sphere;
+   sphere.center = (mOwner->getWorldBox().minExtents + mOwner->getWorldBox().maxExtents) * 0.5;
+   VectorF bv = mOwner->getWorldBox().maxExtents - sphere.center;
+   sphere.radius = bv.len();
+
+   Entity* enterEntity = dynamic_cast<Entity*>(enter);
+   if(enterEntity)
+   {
+      //quick early out. If the bounds don't overlap, it cannot be colliding or inside
+      if (!mOwner->getWorldBox().isOverlapped(enterBox))
+         return false;
+
+      //check if the entity has a collision shape
+      CollisionInterface *cI = enterEntity->getComponent<CollisionInterface>();
+      if (cI)
+      {
+         cI->buildPolyList(PLC_Collision, &mClippedList, mOwner->getWorldBox(), sphere);
+
+         if (!mClippedList.isEmpty())
+         {
+            //well, it's clipped with, or inside, our bounds
+            //now to test the clipped list against our own collision mesh
+            CollisionInterface *myCI = mOwner->getComponent<CollisionInterface>();
+
+            //wait, how would we NOT have this?
+            if (myCI)
+            {
+               //anywho, build our list and then we'll check intersections
+               ClippedPolyList myList;
+
+               MatrixF ownerTransform = mOwner->getTransform();
+               myList.setTransform(&ownerTransform, mOwner->getScale());
+               myList.setObject(mOwner);
+
+               myCI->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
+
+               bool test = true;
+            }
+         }
+      }
+   }
+
+   return mClippedList.isEmpty() == false;
+}
+
+void TriggerComponent::processTick()
+{
+   Parent::processTick();
+
+   if (!isActive())
+      return;
+
+	//get our list of active clients, and see if they have cameras, if they do, build a frustum and see if we exist inside that
+   mVisible = false;
+   if(isServerObject())
+   {
+      for(U32 i=0; i < mObjectList.size(); i++)
+      {
+         if(!testObject(mObjectList[i]))
+         {
+            if (!mOnExitCommand.isEmpty())
+            {
+               String command = String("%obj = ") + mObjectList[i]->getIdString() + ";" +
+                  String("%this = ") + getIdString() + ";" + mOnExitCommand;
+               Con::evaluate(command.c_str());
+            }
+
+            mObjectList.erase(i);
+            //mDataBlock->onLeaveTrigger_callback( this, remove );
+            //onLeaveTrigger_callback(this, remove);
+         }
+      }
+
+      /*if (!mTickCommand.isEmpty())
+         Con::evaluate(mTickCommand.c_str());
+
+      if (mObjects.size() != 0)
+         onTickTrigger_callback(this);*/
+   }
+}
+
+void TriggerComponent::visualizeFrustums(F32 renderTimeMS)
+{
+   
+}
+
+GameConnection* TriggerComponent::getConnection(S32 connectionID)
+{
+   for(NetConnection *conn = NetConnection::getConnectionList(); conn; conn = conn->getNext())  
+   {  
+      GameConnection* gameConn = dynamic_cast<GameConnection*>(conn);
+  
+      if (!gameConn || (gameConn && gameConn->isAIControlled()))
+         continue; 
+
+      if(connectionID == gameConn->getId())
+         return gameConn;
+   }
+
+   return NULL;
+}
+
+void TriggerComponent::addClient(S32 clientID)
+{
+   
+}
+
+void TriggerComponent::removeClient(S32 clientID)
+{
+
+}
+
+DefineEngineMethod( TriggerComponent, addClient, void,
+                   ( S32 clientID ), ( -1 ),
+                   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+                   "@param objB  Object to mount onto us\n"
+                   "@param slot  Mount slot ID\n"
+                   "@param txfm (optional) mount offset transform\n"
+                   "@return true if successful, false if failed (objB is not valid)" )
+{
+   if(clientID == -1)
+      return;
+
+   object->addClient( clientID );
+}
+
+DefineEngineMethod( TriggerComponent, removeClient, void,
+                   ( S32 clientID ), ( -1 ),
+                   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+                   "@param objB  Object to mount onto us\n"
+                   "@param slot  Mount slot ID\n"
+                   "@param txfm (optional) mount offset transform\n"
+                   "@return true if successful, false if failed (objB is not valid)" )
+{
+   if(clientID == -1)
+      return;
+
+   object->removeClient( clientID );
+}
+
+DefineEngineMethod( TriggerComponent, visualizeFrustums, void,
+                   (F32 renderTime), (1000),
+                   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+                   "@param objB  Object to mount onto us\n"
+                   "@param slot  Mount slot ID\n"
+                   "@param txfm (optional) mount offset transform\n"
+                   "@return true if successful, false if failed (objB is not valid)" )
+{
+   object->visualizeFrustums(renderTime);
+}

+ 74 - 0
Engine/source/T3D/components/game/triggerComponent.h

@@ -0,0 +1,74 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+#ifndef _TRIGGER_COMPONENT_H_
+#define _TRIGGER_COMPONENT_H_
+
+#ifndef _COMPONENT_H_
+#include "T3D/components/component.h"
+#endif
+
+#ifndef _ENTITY_H_
+#include "T3D/entity.h"
+#endif
+
+#ifndef _COLLISION_INTERFACES_H_
+#include "T3D/components/collision/collisionInterfaces.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class TriggerComponent : public Component
+{
+   typedef Component Parent;
+
+protected:
+   Vector<SceneObject*> mObjectList;
+
+   bool mVisible;
+
+   String mEnterCommand;
+   String mOnExitCommand;
+   String mOnUpdateInViewCmd;
+
+public:
+   TriggerComponent();
+   virtual ~TriggerComponent();
+   DECLARE_CONOBJECT(TriggerComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   void potentialEnterObject(SceneObject *collider);
+
+   bool testObject(SceneObject* enter);
+
+   virtual void processTick();
+
+   GameConnection* getConnection(S32 connectionID);
+
+   void addClient(S32 clientID);
+   void removeClient(S32 clientID);
+
+   void visualizeFrustums(F32 renderTimeMS);
+
+   DECLARE_CALLBACK(void, onEnterViewCmd, (Entity* cameraEnt, bool firstTimeSeeing));
+   DECLARE_CALLBACK(void, onExitViewCmd, (Entity* cameraEnt));
+   DECLARE_CALLBACK(void, onUpdateInViewCmd, (Entity* cameraEnt));
+   DECLARE_CALLBACK(void, onUpdateOutOfViewCmd, (Entity* cameraEnt));
+};
+
+#endif // _EXAMPLEBEHAVIOR_H_

+ 368 - 0
Engine/source/T3D/components/physics/physicsBehavior.cpp

@@ -0,0 +1,368 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/physics/physicsBehavior.h"
+#include "platform/platform.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "ts/tsShapeInstance.h"
+#include "core/stream/bitStream.h"
+#include "gfx/gfxTransformSaver.h"
+#include "console/engineAPI.h"
+#include "lighting/lightQuery.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "T3D/containerQuery.h"
+#include "math/mathIO.h"
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+PhysicsComponent::PhysicsComponent() : Component()
+{
+   addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
+   addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
+   addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
+   addComponentField("mass", "The mass of the object", "float", "1", "");
+
+   mStatic = false;
+   mAtRest = false;
+   mAtRestCounter = 0;
+
+   mGravity = VectorF(0, 0, 0);
+   mVelocity = VectorF(0, 0, 0);
+   mDrag = 0.7f;
+   mMass = 1.f;
+
+   mGravityMod = 1.f;
+
+   csmAtRestTimer = 64;
+   sAtRestVelocity = 0.15f;
+
+   mDelta.pos = Point3F(0, 0, 0);
+   mDelta.posVec = Point3F(0, 0, 0);
+   mDelta.warpTicks = mDelta.warpCount = 0;
+   mDelta.dt = 1;
+   mDelta.move = NullMove;
+   mPredictionCount = 0;
+}
+
+PhysicsComponent::~PhysicsComponent()
+{
+   for(S32 i = 0;i < mFields.size();++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(PhysicsComponent);
+
+void PhysicsComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   // Initialize interpolation vars.      
+   mDelta.rot[1] = mDelta.rot[0] = QuatF(mOwner->getTransform());
+   mDelta.pos = mOwner->getPosition();
+   mDelta.posVec = Point3F(0,0,0);
+}
+
+void PhysicsComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField("gravity", TypePoint3F, Offset(mGravity, PhysicsComponent));
+   addField("velocity", TypePoint3F, Offset(mVelocity, PhysicsComponent));
+   addField("isStatic", TypeBool, Offset(mStatic, PhysicsComponent));
+}
+
+U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if(stream->writeFlag(mask & VelocityMask))
+      mathWrite( *stream, mVelocity );
+
+   if(stream->writeFlag(mask & UpdateMask))
+   {
+      stream->writeFlag(mStatic);
+      stream->writeFlag(mAtRest);
+      stream->writeInt(mAtRestCounter,8);
+
+      mathWrite( *stream, mGravity );
+
+      stream->writeFloat(mDrag, 12);
+      //stream->writeFloat(mMass, 12);
+
+      stream->writeFloat(mGravityMod, 12);
+   }
+   return retMask;
+}
+
+void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if(stream->readFlag())
+      mathRead( *stream, &mVelocity );
+
+   if(stream->readFlag())
+   {
+      mStatic = stream->readFlag();
+      mAtRest = stream->readFlag();
+      mAtRestCounter = stream->readInt(8);
+
+      mathRead( *stream, &mGravity );
+
+      mDrag = stream->readFloat(12);
+      //mMass = stream->readFloat(12);
+
+      mGravityMod = stream->readFloat(12);
+   }
+}
+
+//
+void PhysicsComponent::interpolateTick(F32 dt)
+{
+   Point3F pos = mDelta.pos + mDelta.posVec * dt;
+   //Point3F rot = mDelta.rot + mDelta.rotVec * dt;
+
+   setRenderPosition(pos,dt);
+}
+
+//
+void PhysicsComponent::updateContainer()
+{
+   PROFILE_SCOPE( PhysicsBehaviorInstance_updateContainer );
+
+   // Update container drag and buoyancy properties
+
+   // Set default values.
+   //mDrag = mDataBlock->drag;
+   //mBuoyancy = 0.0f;      
+   //mGravityMod = 1.0;
+   //mAppliedForce.set(0,0,0);
+
+   ContainerQueryInfo info;
+   info.box = mOwner->getWorldBox();
+   info.mass = mMass;
+
+   mOwner->getContainer()->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
+
+   //mWaterCoverage = info.waterCoverage;
+   //mLiquidType    = info.liquidType;
+   //mLiquidHeight  = info.waterHeight;   
+   //setCurrentWaterObject( info.waterObject );
+
+   // This value might be useful as a datablock value,
+   // This is what allows the player to stand in shallow water (below this coverage)
+   // without jiggling from buoyancy
+   if (info.waterCoverage >= 0.25f) 
+   {      
+      // water viscosity is used as drag for in water.
+      // ShapeBaseData drag is used for drag outside of water.
+      // Combine these two components to calculate this ShapeBase object's 
+      // current drag.
+      mDrag = ( info.waterCoverage * info.waterViscosity ) + 
+         ( 1.0f - info.waterCoverage ) * mDrag;
+      //mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
+   }
+
+   //mAppliedForce = info.appliedForce;
+   mGravityMod = info.gravityScale;
+}
+//
+void PhysicsComponent::_updatePhysics()
+{
+   /*SAFE_DELETE( mOwner->mPhysicsRep );
+
+   if ( !PHYSICSMGR )
+   return;
+
+   if (mDataBlock->simpleServerCollision)
+   {
+   // We only need the trigger on the server.
+   if ( isServerObject() )
+   {
+   PhysicsCollision *colShape = PHYSICSMGR->createCollision();
+   colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
+
+   PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
+   mPhysicsRep = PHYSICSMGR->createBody();
+   mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
+   mPhysicsRep->setTransform( getTransform() );
+   }
+   }
+   else
+   {
+   if ( !mShapeInstance )
+   return;
+
+   PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
+
+   if ( colShape )
+   {
+   PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
+   mPhysicsRep = PHYSICSMGR->createBody();
+   mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
+   mPhysicsRep->setTransform( getTransform() );
+   }
+   }*/
+   return;
+}
+
+PhysicsBody *PhysicsComponent::getPhysicsRep()
+{
+   /*if(mOwner)
+   {
+      Entity* ac = dynamic_cast<Entity*>(mOwner);
+      if(ac)
+         return ac->mPhysicsRep;
+   }*/
+   return NULL;
+}
+//
+void PhysicsComponent::setTransform(const MatrixF& mat)
+{
+   mOwner->setTransform(mat);
+
+   if (!mStatic)
+   {
+      mAtRest = false;
+      mAtRestCounter = 0;
+   }
+
+   if ( getPhysicsRep() )
+      getPhysicsRep()->setTransform( mOwner->getTransform() );
+
+   setMaskBits(UpdateMask);
+}
+
+void PhysicsComponent::setPosition(const Point3F& pos)
+{
+   MatrixF mat = mOwner->getTransform();
+   if (mOwner->isMounted()) {
+      // Use transform from mounted object
+      //mOwner->getObjectMount()->getMountTransform( mOwner->getMountNode(), mMount.xfm, &mat );
+      return;
+   }
+   else {
+      mat.setColumn(3,pos);
+   }
+
+   mOwner->setTransform(mat);
+
+   if ( getPhysicsRep() )
+      getPhysicsRep()->setTransform( mat );
+}
+
+
+void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
+{
+   MatrixF mat = mOwner->getRenderTransform();
+   if (mOwner->isMounted()) {
+      // Use transform from mounted object
+      //mOwner->getObjectMount()->getMountRenderTransform( dt, mOwner->getMountNode(), mMount.xfm, &mat );
+      return;
+   }
+   else {
+      mat.setColumn(3,pos);
+   }
+
+   mOwner->setRenderTransform(mat);
+}
+
+void PhysicsComponent::updateVelocity(const F32 dt)
+{
+}
+
+void PhysicsComponent::setVelocity(const VectorF& vel)
+{
+   mVelocity = vel;
+
+   mAtRest = false;
+   mAtRestCounter = 0;
+   setMaskBits(VelocityMask);
+}
+
+void PhysicsComponent::getVelocity(const Point3F& r, Point3F* v)
+{
+   *v = mVelocity;
+}
+
+void PhysicsComponent::getOriginVector(const Point3F &p,Point3F* r)
+{
+   *r = p - mOwner->getObjBox().getCenter();
+}
+
+F32 PhysicsComponent::getZeroImpulse(const Point3F& r,const Point3F& normal)
+{
+   Point3F a,b,c;
+
+   //set up our inverse matrix
+   MatrixF iv,qmat;
+   MatrixF inverse = MatrixF::Identity;
+   qmat = mOwner->getTransform();
+   iv.mul(qmat,inverse);
+   qmat.transpose();
+   inverse.mul(iv,qmat);
+
+   mCross(r, normal, &a);
+   inverse.mulV(a, &b);
+   mCross(b, r, &c);
+
+   return 1 / ((1/mMass) + mDot(c, normal));
+}
+
+void PhysicsComponent::accumulateForce(F32 dt, Point3F force)
+{
+   mVelocity += force * dt;
+}
+
+void PhysicsComponent::applyImpulse(const Point3F&,const VectorF& vec)
+{
+   // Items ignore angular velocity
+   VectorF vel;
+   vel.x = vec.x / mMass;
+   vel.y = vec.y / mMass;
+   vel.z = vec.z / mMass;
+   setVelocity(mVelocity + vel);
+}
+
+DefineEngineMethod( PhysicsComponent, applyImpulse, bool, ( Point3F pos, VectorF vel ),,
+                   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+                   "@param pos impulse world position\n"
+                   "@param vel impulse velocity (impulse force F = m * v)\n"
+                   "@return Always true\n"
+
+                   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   object->applyImpulse(pos,vel);
+   return true;
+}

+ 135 - 0
Engine/source/T3D/components/physics/physicsBehavior.h

@@ -0,0 +1,135 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _PHYSICSBEHAVIOR_H_
+#define _PHYSICSBEHAVIOR_H_
+#include "T3D/components/component.h"
+
+#ifndef __RESOURCE_H__
+#include "core/resource.h"
+#endif
+#ifndef _TSSHAPE_H_
+#include "ts/tsShape.h"
+#endif
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+#ifndef _MBOX_H_
+#include "math/mBox.h"
+#endif
+#ifndef _ENTITY_H_
+#include "T3D/entity.h"
+#endif
+#ifndef _CONVEX_H_
+#include "collision/convex.h"
+#endif
+#ifndef _BOXCONVEX_H_
+#include "collision/boxConvex.h"
+#endif
+#ifndef _RIGID_H_
+#include "T3D/rigid.h"
+#endif
+#ifndef _T3D_PHYSICS_PHYSICSBODY_H_
+#include "T3D/physics/physicsBody.h"
+#endif
+
+#ifndef _RENDER_COMPONENT_INTERFACE_H_
+#include "T3D/components/render/renderComponentInterface.h"
+#endif
+
+class TSShapeInstance;
+class SceneRenderState;
+class PhysicsBody;
+class PhysicsBehaviorInstance;
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class PhysicsComponent : public Component
+{
+   typedef Component Parent;
+
+protected:
+   bool mStatic;
+   bool mAtRest;
+   S32  mAtRestCounter;
+
+   VectorF mGravity;
+   VectorF mVelocity;
+   F32     mDrag;
+   F32		mMass;
+
+   F32		mGravityMod;
+
+   S32 csmAtRestTimer;
+   F32 sAtRestVelocity;      // Min speed after collisio
+
+public:
+   enum MaskBits {
+      PositionMask = Parent::NextFreeMask << 0,
+      FreezeMask = Parent::NextFreeMask << 1,
+      ForceMoveMask = Parent::NextFreeMask << 2,
+      VelocityMask = Parent::NextFreeMask << 3,
+      NextFreeMask = Parent::NextFreeMask << 4
+   };
+
+   struct StateDelta
+   {
+      Move move;                    ///< Last move from server
+      F32 dt;                       ///< Last interpolation time
+      // Interpolation data
+      Point3F pos;
+      Point3F posVec;
+      QuatF rot[2];
+      // Warp data
+      S32 warpTicks;                ///< Number of ticks to warp
+      S32 warpCount;                ///< Current pos in warp
+      Point3F warpOffset;
+      QuatF warpRot[2];
+   };
+
+   StateDelta mDelta;
+   S32 mPredictionCount;            ///< Number of ticks to predict
+
+public:
+   PhysicsComponent();
+   virtual ~PhysicsComponent();
+   DECLARE_CONOBJECT(PhysicsComponent);
+
+   static void initPersistFields();
+
+   virtual void interpolateTick(F32 dt);
+   virtual void updatePos(const U32 /*mask*/, const F32 dt){}
+   virtual void _updatePhysics();
+   virtual PhysicsBody *getPhysicsRep();
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void onComponentAdd();
+
+   void updateContainer();
+
+   virtual void updateVelocity(const F32 dt);
+   virtual Point3F getVelocity() { return mVelocity; }
+   virtual void getOriginVector(const Point3F &p, Point3F* r);
+   virtual void getVelocity(const Point3F& r, Point3F* v);
+   virtual void setVelocity(const VectorF& vel);
+   virtual void setTransform(const MatrixF& mat);
+   virtual void setPosition(const Point3F& pos);
+   void setRenderPosition(const Point3F& pos, F32 dt);
+
+   virtual void applyImpulse(const Point3F&, const VectorF& vec);
+   virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
+   virtual void accumulateForce(F32 dt, Point3F force);
+
+   //Rigid Body Collision Conveinence Hooks
+   virtual bool updateCollision(F32 dt, Rigid& ns, CollisionList &cList) { return false; }
+   virtual bool resolveContacts(Rigid& ns, CollisionList& cList, F32 dt) { return false; }
+   //virtual bool resolveCollision(Rigid&  ns, CollisionList& cList) { return false; }
+   virtual bool resolveCollision(const Point3F& p, const Point3F &normal) { return false; }
+};
+
+#endif // _COMPONENT_H_

+ 0 - 0
Engine/source/T3D/components/physics/physicsComponentInterface.cpp


+ 15 - 21
Engine/source/component/moreAdvancedComponent.h → Engine/source/T3D/components/physics/physicsComponentInterface.h

@@ -20,36 +20,30 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
-#ifndef _MOREADVANCEDCOMPONENT_H_
-#define _MOREADVANCEDCOMPONENT_H_
+#ifndef PHYSICS_COMPONENT_INTERFACE_H
+#define PHYSICS_COMPONENT_INTERFACE_H
 
-#ifndef _SIMPLECOMPONENT_H_
-#include "component/simpleComponent.h"
+#ifndef CORE_INTERFACES_H
+#include "T3D/components/coreInterfaces.h"
 #endif
 
-/// This is a slightly more advanced component which will be used to demonstrate
-/// components which are dependent on other components.
-class MoreAdvancedComponent : public SimComponent
+class PhysicsComponentInterface : public Interface<PhysicsComponentInterface>
 {
-   typedef SimComponent Parent;
-
 protected:
-   // This component is going to be dependent on a SimpleComponentInterface being
-   // queried off of it's parent object. This will store that interface that
-   // will get queried during onComponentRegister()
-   SimpleComponentInterface *mSCInterface;
+   VectorF  mVelocity;
+   F32      mMass;
+
+   F32		mGravityMod;
 
 public:
-   DECLARE_CONOBJECT(MoreAdvancedComponent);
+   void updateForces();
+
+   VectorF getVelocity() { return mVelocity; }
+   void setVelocity(VectorF vel) { mVelocity = vel; }
 
-   // Firstly, take a look at the documentation for this function in simComponent.h.
-   // We will be overloading this method to query the component heirarchy for our
-   // dependent interface, as noted above.
-   virtual bool onComponentRegister( SimComponent *owner );
+   F32 getMass() { return mMass; }
 
-   // This function will try to execute a function through the interface that this
-   // component is dependent on.
-   virtual bool testDependentInterface();
+   Signal< void(VectorF normal, Vector<SceneObject*> overlappedObjects) > onPhysicsCollision;
 };
 
 #endif

+ 865 - 0
Engine/source/T3D/components/physics/playerControllerComponent.cpp

@@ -0,0 +1,865 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/physics/playerControllerComponent.h"
+#include "platform/platform.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "ts/tsShapeInstance.h"
+#include "core/stream/bitStream.h"
+#include "gfx/gfxTransformSaver.h"
+#include "console/engineAPI.h"
+#include "lighting/lightQuery.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "collision/collision.h"
+#include "T3D/physics/physicsPlayer.h"
+#include "T3D/physics/physicsPlugin.h"
+#include "T3D/components/collision/collisionInterfaces.h"
+#include "T3D/trigger.h"
+#include "T3D/components/collision/collisionTrigger.h"
+
+// Movement constants
+static F32 sVerticalStepDot = 0.173f;   // 80
+static F32 sMinFaceDistance = 0.01f;
+static F32 sTractionDistance = 0.04f;
+static F32 sNormalElasticity = 0.01f;
+static U32 sMoveRetryCount = 5;
+static F32 sMaxImpulseVelocity = 200.0f;
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+IMPLEMENT_CALLBACK(PlayerControllerComponent, updateMove, void, (PlayerControllerComponent* obj), (obj),
+   "Called when the player updates it's movement, only called if object is set to callback in script(doUpdateMove).\n"
+   "@param obj the Player object\n");
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+PlayerControllerComponent::PlayerControllerComponent() : Component()
+{
+   addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
+   addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
+   addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
+   addComponentField("mass", "The mass of the object", "float", "1", "");
+
+   mBuoyancy = 0.f;
+   mFriction = 0.3f;
+   mElasticity = 0.4f;
+   mMaxVelocity = 3000.f;
+   mVelocity = VectorF::Zero;
+   mContactTimer = 0;
+   mSticky = false;
+   
+   mFalling = false;
+   mSwimming = false;
+   mInWater = false;
+
+   mDelta.pos = mDelta.posVec = Point3F::Zero;
+   mDelta.warpTicks = mDelta.warpCount = 0;
+   mDelta.rot[0].identity(); 
+   mDelta.rot[1].identity();
+   mDelta.dt = 1;
+
+   mUseDirectMoveInput = false;
+
+   mFriendlyName = "Player Controller";
+   mComponentType = "Physics";
+
+   mDescription = getDescriptionText("A general-purpose physics player controller.");
+
+   //mNetFlags.set(Ghostable | ScopeAlways);
+
+   mMass = 9.0f;         // from ShapeBase
+   mDrag = 1.0f;         // from ShapeBase
+
+   maxStepHeight = 1.0f;
+   moveSurfaceAngle = 60.0f;
+   contactSurfaceAngle = 85.0f;
+
+   fallingSpeedThreshold = -10.0f;
+
+   horizMaxSpeed = 80.0f;
+   horizMaxAccel = 100.0f;
+   horizResistSpeed = 38.0f;
+   horizResistFactor = 1.0f;
+
+   upMaxSpeed = 80.0f;
+   upMaxAccel = 100.0f;
+   upResistSpeed = 38.0f;
+   upResistFactor = 1.0f;
+
+   // Air control
+   airControl = 0.0f;
+
+   //Grav mod
+   mGravityMod = 1;
+
+   mInputVelocity = Point3F(0, 0, 0);
+
+   mPhysicsRep = NULL;
+   mPhysicsWorld = NULL;
+}
+
+PlayerControllerComponent::~PlayerControllerComponent()
+{
+   for (S32 i = 0; i < mFields.size(); ++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(PlayerControllerComponent);
+
+//////////////////////////////////////////////////////////////////////////
+
+bool PlayerControllerComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void PlayerControllerComponent::onRemove()
+{
+   Parent::onRemove();
+
+   SAFE_DELETE(mPhysicsRep);
+}
+
+void PlayerControllerComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   updatePhysics();
+}
+
+void PlayerControllerComponent::componentAddedToOwner(Component *comp)
+{
+   if (comp->getId() == getId())
+      return;
+
+   //test if this is a shape component!
+   CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
+   if (collisionInterface)
+   {
+      collisionInterface->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
+      mOwnerCollisionInterface = collisionInterface;
+      updatePhysics();
+   }
+}
+
+void PlayerControllerComponent::componentRemovedFromOwner(Component *comp)
+{
+   if (comp->getId() == getId()) //?????????
+      return;
+
+   //test if this is a shape component!
+   CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
+   if (collisionInterface)
+   {
+      collisionInterface->onCollisionChanged.remove(this, &PlayerControllerComponent::updatePhysics);
+      mOwnerCollisionInterface = NULL;
+      updatePhysics();
+   }
+}
+
+void PlayerControllerComponent::updatePhysics(PhysicsCollision *collision)
+{
+   if (!PHYSICSMGR)
+      return;
+
+   mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+
+   //first, clear the old physRep
+   SAFE_DELETE(mPhysicsRep);
+
+   mPhysicsRep = PHYSICSMGR->createPlayer();
+
+   F32 runSurfaceCos = mCos(mDegToRad(moveSurfaceAngle));
+
+   Point3F ownerBounds = mOwner->getObjBox().getExtents() * mOwner->getScale();
+
+   mPhysicsRep->init("", ownerBounds, runSurfaceCos, maxStepHeight, mOwner, mPhysicsWorld);
+
+   mPhysicsRep->setTransform(mOwner->getTransform());
+}
+
+void PlayerControllerComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField("inputVelocity", TypePoint3F, Offset(mInputVelocity, PlayerControllerComponent), "");
+   addField("useDirectMoveInput", TypePoint3F, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
+}
+
+U32 PlayerControllerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   return retMask;
+}
+
+void PlayerControllerComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+}
+
+//
+void PlayerControllerComponent::processTick()
+{
+   Parent::processTick();
+
+   if (!isServerObject() || !isActive())
+      return;
+
+   // Warp to catch up to server
+   if (mDelta.warpCount < mDelta.warpTicks)
+   {
+      mDelta.warpCount++;
+
+      // Set new pos.
+      mDelta.pos = mOwner->getPosition();
+      mDelta.pos += mDelta.warpOffset;
+      mDelta.rot[0] = mDelta.rot[1];
+      mDelta.rot[1].interpolate(mDelta.warpRot[0], mDelta.warpRot[1], F32(mDelta.warpCount) / mDelta.warpTicks);
+      
+      MatrixF trans;
+      mDelta.rot[1].setMatrix(&trans);
+      trans.setPosition(mDelta.pos);
+
+      mOwner->setTransform(trans);
+
+      // Pos backstepping
+      mDelta.posVec.x = -mDelta.warpOffset.x;
+      mDelta.posVec.y = -mDelta.warpOffset.y;
+      mDelta.posVec.z = -mDelta.warpOffset.z;
+   }
+   else
+   {
+      // Save current rigid state interpolation
+      mDelta.posVec = mOwner->getPosition();
+      mDelta.rot[0] = mOwner->getTransform();
+
+      updateMove();
+      updatePos(TickSec);
+
+      // Wrap up interpolation info
+      mDelta.pos = mOwner->getPosition();
+      mDelta.posVec -= mOwner->getPosition();
+      mDelta.rot[1]  = mOwner->getTransform();
+
+      // Update container database
+      setTransform(mOwner->getTransform());
+      
+      setMaskBits(VelocityMask);
+      setMaskBits(PositionMask);
+   }
+}
+
+void PlayerControllerComponent::interpolateTick(F32 dt)
+{
+}
+
+void PlayerControllerComponent::ownerTransformSet(MatrixF *mat)
+{
+   if (mPhysicsRep)
+      mPhysicsRep->setTransform(mOwner->getTransform());
+}
+
+void PlayerControllerComponent::setTransform(const MatrixF& mat)
+{
+   mOwner->setTransform(mat);
+
+   setMaskBits(UpdateMask);
+}
+
+//
+void PlayerControllerComponent::updateMove()
+{
+   if (!PHYSICSMGR)
+      return;
+
+   Move *move = &mOwner->lastMove;
+
+   //If we're not set to use mUseDirectMoveInput, then we allow for an override in the form of mInputVelocity
+   if (!mUseDirectMoveInput)
+   {
+      move->x = mInputVelocity.x;
+      move->y = mInputVelocity.y;
+      move->z = mInputVelocity.z;
+   }
+
+   // Is waterCoverage high enough to be 'swimming'?
+   {
+      bool swimming = mOwner->getContainerInfo().waterCoverage > 0.65f/* && canSwim()*/;
+
+      if (swimming != mSwimming)
+      {
+         mSwimming = swimming;
+      }
+   }
+
+   // Update current orientation
+   bool doStandardMove = true;
+   GameConnection* con = mOwner->getControllingClient();
+
+#ifdef TORQUE_EXTENDED_MOVE
+   // Work with an absolute rotation from the ExtendedMove class?
+   if (con && con->getControlSchemeAbsoluteRotation())
+   {
+      doStandardMove = false;
+      const ExtendedMove* emove = dynamic_cast<const ExtendedMove*>(move);
+      U32 emoveIndex = smExtendedMoveHeadPosRotIndex;
+      if (emoveIndex >= ExtendedMove::MaxPositionsRotations)
+         emoveIndex = 0;
+
+      if (emove->EulerBasedRotation[emoveIndex])
+      {
+         // Head pitch
+         mHead.x += (emove->rotX[emoveIndex] - mLastAbsolutePitch);
+
+         // Do we also include the relative yaw value?
+         if (con->getControlSchemeAddPitchToAbsRot())
+         {
+            F32 x = move->pitch;
+            if (x > M_PI_F)
+               x -= M_2PI_F;
+
+            mHead.x += x;
+         }
+
+         // Constrain the range of mHead.x
+         while (mHead.x < -M_PI_F)
+            mHead.x += M_2PI_F;
+         while (mHead.x > M_PI_F)
+            mHead.x -= M_2PI_F;
+
+         // Rotate (heading) head or body?
+         if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
+         {
+            // Rotate head
+            mHead.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
+
+            // Do we also include the relative yaw value?
+            if (con->getControlSchemeAddYawToAbsRot())
+            {
+               F32 z = move->yaw;
+               if (z > M_PI_F)
+                  z -= M_2PI_F;
+
+               mHead.z += z;
+            }
+
+            // Constrain the range of mHead.z
+            while (mHead.z < 0.0f)
+               mHead.z += M_2PI_F;
+            while (mHead.z > M_2PI_F)
+               mHead.z -= M_2PI_F;
+         }
+         else
+         {
+            // Rotate body
+            mRot.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
+
+            // Do we also include the relative yaw value?
+            if (con->getControlSchemeAddYawToAbsRot())
+            {
+               F32 z = move->yaw;
+               if (z > M_PI_F)
+                  z -= M_2PI_F;
+
+               mRot.z += z;
+            }
+
+            // Constrain the range of mRot.z
+            while (mRot.z < 0.0f)
+               mRot.z += M_2PI_F;
+            while (mRot.z > M_2PI_F)
+               mRot.z -= M_2PI_F;
+         }
+         mLastAbsoluteYaw = emove->rotZ[emoveIndex];
+         mLastAbsolutePitch = emove->rotX[emoveIndex];
+
+         // Head bank
+         mHead.y = emove->rotY[emoveIndex];
+
+         // Constrain the range of mHead.y
+         while (mHead.y > M_PI_F)
+            mHead.y -= M_2PI_F;
+      }
+   }
+#endif
+
+   MatrixF zRot;
+   zRot.set(EulerF(0.0f, 0.0f, mOwner->getRotation().asEulerF().z));
+
+   // Desired move direction & speed
+   VectorF moveVec;
+   F32 moveSpeed = mInputVelocity.len();
+
+   zRot.getColumn(0, &moveVec);
+   moveVec *= move->x;
+   VectorF tv;
+   zRot.getColumn(1, &tv);
+   moveVec += tv * move->y;
+
+   // Acceleration due to gravity
+   VectorF acc(mPhysicsWorld->getGravity() * mGravityMod * TickSec);
+
+   // Determine ground contact normal. Only look for contacts if
+   // we can move and aren't mounted.
+   mContactInfo.contactNormal = VectorF::Zero;
+   mContactInfo.jump = false;
+   mContactInfo.run = false;
+
+   bool jumpSurface = false, runSurface = false;
+   if (!mOwner->isMounted())
+      findContact(&mContactInfo.run, &mContactInfo.jump, &mContactInfo.contactNormal);
+   if (mContactInfo.jump)
+      mJumpSurfaceNormal = mContactInfo.contactNormal;
+
+   // If we don't have a runSurface but we do have a contactNormal,
+   // then we are standing on something that is too steep.
+   // Deflect the force of gravity by the normal so we slide.
+   // We could also try aligning it to the runSurface instead,
+   // but this seems to work well.
+   if (!mContactInfo.run && !mContactInfo.contactNormal.isZero())
+      acc = (acc - 2 * mContactInfo.contactNormal * mDot(acc, mContactInfo.contactNormal));
+
+   // Acceleration on run surface
+   if (mContactInfo.run && !mSwimming)
+   {
+      mContactTimer = 0;
+
+      VectorF pv = moveVec;
+
+      // Adjust the player's requested dir. to be parallel
+      // to the contact surface.
+      F32 pvl = pv.len();
+
+      // Convert to acceleration
+      if (pvl)
+         pv *= moveSpeed / pvl;
+      VectorF runAcc = pv - (mVelocity + acc);
+      F32 runSpeed = runAcc.len();
+
+      // Clamp acceleration, player also accelerates faster when
+      // in his hard landing recover state.
+      F32 maxAcc;
+
+      maxAcc = (horizMaxAccel / mMass) * TickSec;
+
+      if (runSpeed > maxAcc)
+         runAcc *= maxAcc / runSpeed;
+
+      acc += runAcc;
+   }
+   else if (!mSwimming && airControl > 0.0f)
+   {
+      VectorF pv;
+      pv = moveVec;
+      F32 pvl = pv.len();
+
+      if (pvl)
+         pv *= moveSpeed / pvl;
+
+      VectorF runAcc = pv - (mVelocity + acc);
+      runAcc.z = 0;
+      runAcc.x = runAcc.x * airControl;
+      runAcc.y = runAcc.y * airControl;
+      F32 runSpeed = runAcc.len();
+
+      // We don't test for sprinting when performing air control
+      F32 maxAcc = (horizMaxAccel / mMass) * TickSec * 0.3f;
+
+      if (runSpeed > maxAcc)
+         runAcc *= maxAcc / runSpeed;
+
+      acc += runAcc;
+
+      // There are no special air control animations 
+      // so... increment this unless you really want to 
+      // play the run anims in the air.
+      mContactTimer++;
+   }
+   else if (mSwimming)
+   {
+      // Remove acc into contact surface (should only be gravity)
+      // Clear out floating point acc errors, this will allow
+      // the player to "rest" on the ground.
+      F32 vd = -mDot(acc, mContactInfo.contactNormal);
+      if (vd > 0.0f) 
+      {
+         VectorF dv = mContactInfo.contactNormal * (vd + 0.002f);
+         acc += dv;
+         if (acc.len() < 0.0001f)
+            acc.set(0.0f, 0.0f, 0.0f);
+      }
+
+      // get the head pitch and add it to the moveVec
+      // This more accurate swim vector calc comes from Matt Fairfax
+      MatrixF xRot, zRot;
+      xRot.set(EulerF(mOwner->getRotation().asEulerF().x, 0, 0));
+      zRot.set(EulerF(0, 0, mOwner->getRotation().asEulerF().z));
+      MatrixF rot;
+      rot.mul(zRot, xRot);
+      rot.getColumn(0, &moveVec);
+
+      moveVec *= move->x;
+      VectorF tv;
+      rot.getColumn(1, &tv);
+      moveVec += tv * move->y;
+      rot.getColumn(2, &tv);
+      moveVec += tv * move->z;
+
+      // Force a 0 move if there is no energy, and only drain
+      // move energy if we're moving.
+      VectorF swimVec = moveVec;
+
+      // If we are swimming but close enough to the shore/ground
+      // we can still have a surface-normal. In this case align the
+      // velocity to the normal to make getting out of water easier.
+
+      moveVec.normalize();
+      F32 isSwimUp = mDot(moveVec, mContactInfo.contactNormal);
+
+      if (!mContactInfo.contactNormal.isZero() && isSwimUp < 0.1f)
+      {
+         F32 pvl = swimVec.len();
+
+         if (pvl)
+         {
+            VectorF nn;
+            mCross(swimVec, VectorF(0.0f, 0.0f, 1.0f), &nn);
+            nn *= 1.0f / pvl;
+            VectorF cv = mContactInfo.contactNormal;
+            cv -= nn * mDot(nn, cv);
+            swimVec -= cv * mDot(swimVec, cv);
+         }
+      }
+
+      F32 swimVecLen = swimVec.len();
+
+      // Convert to acceleration.
+      if (swimVecLen)
+         swimVec *= moveSpeed / swimVecLen;
+      VectorF swimAcc = swimVec - (mVelocity + acc);
+      F32 swimSpeed = swimAcc.len();
+
+      // Clamp acceleration.
+      F32 maxAcc = (horizMaxAccel / mMass) * TickSec;
+      if (swimSpeed > maxAcc)
+         swimAcc *= maxAcc / swimSpeed;
+
+      acc += swimAcc;
+
+      mContactTimer++;
+   }
+   else
+      mContactTimer++;
+
+   // Add in force from physical zones...
+   acc += (mOwner->getContainerInfo().appliedForce / mMass) * TickSec;
+
+   // Adjust velocity with all the move & gravity acceleration
+   // TG: I forgot why doesn't the TickSec multiply happen here...
+   mVelocity += acc;
+
+   // apply horizontal air resistance
+
+   F32 hvel = mSqrt(mVelocity.x * mVelocity.x + mVelocity.y * mVelocity.y);
+
+   if (hvel > horizResistSpeed)
+   {
+      F32 speedCap = hvel;
+      if (speedCap > horizMaxSpeed)
+         speedCap = horizMaxSpeed;
+      speedCap -= horizResistFactor * TickSec * (speedCap - horizResistSpeed);
+      F32 scale = speedCap / hvel;
+      mVelocity.x *= scale;
+      mVelocity.y *= scale;
+   }
+   if (mVelocity.z > upResistSpeed)
+   {
+      if (mVelocity.z > upMaxSpeed)
+         mVelocity.z = upMaxSpeed;
+      mVelocity.z -= upResistFactor * TickSec * (mVelocity.z - upResistSpeed);
+   }
+
+   // Apply drag
+   mVelocity -= mVelocity * mDrag * TickSec;
+
+   // Clamp very small velocity to zero
+   if (mVelocity.isZero())
+      mVelocity = Point3F::Zero;
+
+   // If we are not touching anything and have sufficient -z vel,
+   // we are falling.
+   if (mContactInfo.run)
+   {
+      mFalling = false;
+   }
+   else
+   {
+      VectorF vel;
+      mOwner->getWorldToObj().mulV(mVelocity, &vel);
+      mFalling = vel.z < fallingSpeedThreshold;
+   }
+
+   // Enter/Leave Liquid
+   if (!mInWater && mOwner->getContainerInfo().waterCoverage > 0.0f)
+   {
+      mInWater = true;
+   }
+   else if (mInWater && mOwner->getContainerInfo().waterCoverage <= 0.0f)
+   {
+      mInWater = false;
+   }
+}
+
+void PlayerControllerComponent::updatePos(const F32 travelTime)
+{
+   if (!PHYSICSMGR)
+      return;
+
+   PROFILE_SCOPE(PlayerControllerComponent_UpdatePos);
+
+   Point3F newPos;
+
+   Collision col;
+   dMemset(&col, 0, sizeof(col));
+
+   static CollisionList collisionList;
+   collisionList.clear();
+
+   newPos = mPhysicsRep->move(mVelocity * travelTime, collisionList);
+
+   bool haveCollisions = false;
+   bool wasFalling = mFalling;
+   if (collisionList.getCount() > 0)
+   {
+      mFalling = false;
+      haveCollisions = true;
+
+      //TODO: clean this up so the phys component doesn't have to tell the col interface to do this
+      CollisionInterface* colInterface = mOwner->getComponent<CollisionInterface>();
+      if (colInterface)
+      {
+         colInterface->handleCollisionList(collisionList, mVelocity);
+      }
+   }
+
+   if (haveCollisions)
+   {
+      // Pick the collision that most closely matches our direction
+      VectorF velNormal = mVelocity;
+      velNormal.normalizeSafe();
+      const Collision *collision = &collisionList[0];
+      F32 collisionDot = mDot(velNormal, collision->normal);
+      const Collision *cp = collision + 1;
+      const Collision *ep = collision + collisionList.getCount();
+      for (; cp != ep; cp++)
+      {
+         F32 dp = mDot(velNormal, cp->normal);
+         if (dp < collisionDot)
+         {
+            collisionDot = dp;
+            collision = cp;
+         }
+      }
+
+      // Modify our velocity based on collisions
+      for (U32 i = 0; i<collisionList.getCount(); ++i)
+      {
+         F32 bd = -mDot(mVelocity, collisionList[i].normal);
+         VectorF dv = collisionList[i].normal * (bd + sNormalElasticity);
+         mVelocity += dv;
+      }
+
+      // Store the last collision for use later on.  The handle collision
+      // code only expects a single collision object.
+      if (collisionList.getCount() > 0)
+         col = collisionList[collisionList.getCount() - 1];
+
+      // We'll handle any player-to-player collision, and the last collision
+      // with other obejct types.
+      for (U32 i = 0; i<collisionList.getCount(); ++i)
+      {
+         Collision& colCheck = collisionList[i];
+         if (colCheck.object)
+         {
+            col = colCheck;
+         }
+      }
+   }
+   
+   MatrixF newMat;
+   newMat.setPosition(newPos);
+   mPhysicsRep->setTransform(newMat);
+
+   mOwner->setPosition(newPos);
+}
+
+//
+void PlayerControllerComponent::setVelocity(const VectorF& vel)
+{
+   mVelocity = vel;
+
+   // Clamp against the maximum velocity.
+   if (mMaxVelocity > 0)
+   {
+      F32 len = mVelocity.magnitudeSafe();
+      if (len > mMaxVelocity)
+      {
+         Point3F excess = mVelocity * (1.0f - (mMaxVelocity / len));
+         mVelocity -= excess;
+      }
+   }
+
+   setMaskBits(VelocityMask);
+}
+
+void PlayerControllerComponent::findContact(bool *run, bool *jump, VectorF *contactNormal)
+{
+   SceneObject *contactObject = NULL;
+
+   Vector<SceneObject*> overlapObjects;
+
+   mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
+
+   F32 vd = (*contactNormal).z;
+   *run = vd > mCos(mDegToRad(moveSurfaceAngle));
+   *jump = vd > mCos(mDegToRad(contactSurfaceAngle));
+
+   // Check for triggers
+   for (U32 i = 0; i < overlapObjects.size(); i++)
+   {
+      SceneObject *obj = overlapObjects[i];
+      U32 objectMask = obj->getTypeMask();
+
+      // Check: triggers, corpses and items...
+      //
+      if (objectMask & TriggerObjectType)
+      {
+         if (Trigger* pTrigger = dynamic_cast<Trigger*>(obj))
+         {
+            pTrigger->potentialEnterObject(mOwner);
+         }
+         else if (CollisionTrigger* pTriggerEx = dynamic_cast<CollisionTrigger*>(obj))
+         {
+            if (pTriggerEx)
+               pTriggerEx->potentialEnterObject(mOwner);
+         }
+         //Add any other custom classes and the sort here that should be filtered against
+         /*else if (TriggerExample* pTriggerEx = dynamic_cast<TriggerExample*>(obj))
+         {
+            if (pTriggerEx)
+               pTriggerEx->potentialEnterObject(mOwner);
+         }*/
+      }
+   }
+
+   mContactInfo.contacted = contactObject != NULL;
+   mContactInfo.contactObject = contactObject;
+
+   if (mContactInfo.contacted)
+      mContactInfo.contactNormal = *contactNormal;
+}
+
+void PlayerControllerComponent::applyImpulse(const Point3F &pos, const VectorF &vec)
+{
+
+   AssertFatal(!mIsNaN(vec), "Player::applyImpulse() - The vector is NaN!");
+
+   // Players ignore angular velocity
+   VectorF vel;
+   vel.x = vec.x / getMass();
+   vel.y = vec.y / getMass();
+   vel.z = vec.z / getMass();
+
+   // Make sure the impulse isn't too bigg
+   F32 len = vel.magnitudeSafe();
+   if (len > sMaxImpulseVelocity)
+   {
+      Point3F excess = vel * (1.0f - (sMaxImpulseVelocity / len));
+      vel -= excess;
+   }
+
+   setVelocity(mVelocity + vel);
+}
+
+DefineEngineMethod(PlayerControllerComponent, applyImpulse, bool, (Point3F pos, VectorF vel), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   object->applyImpulse(pos, vel);
+   return true;
+}
+
+DefineEngineMethod(PlayerControllerComponent, getContactNormal, Point3F, (), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->getContactNormal();
+}
+
+DefineEngineMethod(PlayerControllerComponent, getContactObject, SceneObject*, (), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->getContactObject();
+}
+
+DefineEngineMethod(PlayerControllerComponent, isContacted, bool, (), ,
+   "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
+
+   "@param pos impulse world position\n"
+   "@param vel impulse velocity (impulse force F = m * v)\n"
+   "@return Always true\n"
+
+   "@note Not all objects that derrive from GameBase have this defined.\n")
+{
+   return object->isContacted();
+}

+ 212 - 0
Engine/source/T3D/components/physics/playerControllerComponent.h

@@ -0,0 +1,212 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef PLAYER_CONTORLLER_COMPONENT_H
+#define PLAYER_CONTORLLER_COMPONENT_H
+
+#ifndef PHYSICSBEHAVIOR_H
+#include "T3D/components/physics/physicsBehavior.h"
+#endif
+#ifndef __RESOURCE_H__
+#include "core/resource.h"
+#endif
+#ifndef _TSSHAPE_H_
+#include "ts/tsShape.h"
+#endif
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+#ifndef _MBOX_H_
+#include "math/mBox.h"
+#endif
+#ifndef ENTITY_H
+#include "T3D/entity.h"
+#endif
+#ifndef _CONVEX_H_
+#include "collision/convex.h"
+#endif
+#ifndef _BOXCONVEX_H_
+#include "collision/boxConvex.h"
+#endif
+#ifndef _T3D_PHYSICSCOMMON_H_
+#include "T3D/physics/physicsCommon.h"
+#endif
+#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
+#include "T3D/physics/physicsWorld.h"
+#endif
+#ifndef PHYSICS_COMPONENT_INTERFACE_H
+#include "T3D/components/physics/physicsComponentInterface.h"
+#endif
+#ifndef COLLISION_INTERFACES_H
+#include "T3D/components/collision/collisionInterfaces.h"
+#endif
+
+class SceneRenderState;
+class PhysicsWorld;
+class PhysicsPlayer;
+class SimplePhysicsBehaviorInstance;
+class CollisionInterface;
+
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class PlayerControllerComponent : public Component,
+   public PhysicsComponentInterface
+{
+   typedef Component Parent;
+
+   enum MaskBits {
+      VelocityMask = Parent::NextFreeMask << 0,
+      PositionMask = Parent::NextFreeMask << 1,
+      NextFreeMask = Parent::NextFreeMask << 2
+   };
+
+   struct StateDelta
+   {
+      Move move;                    ///< Last move from server
+      F32 dt;                       ///< Last interpolation time
+      // Interpolation data
+      Point3F pos;
+      Point3F posVec;
+      QuatF rot[2];
+      // Warp data
+      S32 warpTicks;                ///< Number of ticks to warp
+      S32 warpCount;                ///< Current pos in warp
+      Point3F warpOffset;
+      QuatF warpRot[2];
+   };
+
+   StateDelta mDelta;
+
+   PhysicsPlayer *mPhysicsRep;
+   PhysicsWorld  *mPhysicsWorld;
+
+   CollisionInterface* mOwnerCollisionInterface;
+
+   struct ContactInfo
+   {
+      bool contacted, jump, run;
+      SceneObject *contactObject;
+      VectorF  contactNormal;
+      F32 contactTime;
+
+      void clear()
+      {
+         contacted = jump = run = false;
+         contactObject = NULL;
+         contactNormal.set(1, 1, 1);
+      }
+
+      ContactInfo() { clear(); }
+
+   } mContactInfo;
+
+protected:
+   F32 mDrag;
+   F32 mBuoyancy;
+   F32 mFriction;
+   F32 mElasticity;
+   F32 mMaxVelocity;
+   bool mSticky;
+
+   bool mFalling;
+   bool mSwimming;
+   bool mInWater;
+
+   S32 mContactTimer;               ///< Ticks since last contact
+
+   U32 mIntegrationCount;
+
+   Point3F mJumpSurfaceNormal;      ///< Normal of the surface the player last jumped on
+
+   F32 maxStepHeight;         ///< Maximum height the player can step up
+   F32 moveSurfaceAngle;      ///< Maximum angle from vertical in degrees the player can run up
+   F32 contactSurfaceAngle;   ///< Maximum angle from vertical in degrees we consider having real 'contact'
+
+   F32 horizMaxSpeed;         ///< Max speed attainable in the horizontal
+   F32 horizMaxAccel;
+   F32 horizResistSpeed;      ///< Speed at which resistance will take place
+   F32 horizResistFactor;     ///< Factor of resistance once horizResistSpeed has been reached
+
+   F32 upMaxSpeed;            ///< Max vertical speed attainable
+   F32 upMaxAccel;
+   F32 upResistSpeed;         ///< Speed at which resistance will take place
+   F32 upResistFactor;        ///< Factor of resistance once upResistSpeed has been reached
+
+   F32 fallingSpeedThreshold; ///< Downward speed at which we consider the player falling
+
+   // Air control
+   F32 airControl;
+
+   Point3F mInputVelocity;
+
+   bool mUseDirectMoveInput;
+
+public:
+   PlayerControllerComponent();
+   virtual ~PlayerControllerComponent();
+   DECLARE_CONOBJECT(PlayerControllerComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual void ownerTransformSet(MatrixF *mat);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   void updatePhysics(PhysicsCollision *collision = NULL);
+
+   virtual void processTick();
+   virtual void interpolateTick(F32 dt);
+   virtual void updatePos(const F32 dt);
+   void updateMove();
+
+   virtual VectorF getVelocity() { return mVelocity; }
+   virtual void setVelocity(const VectorF& vel);
+   virtual void setTransform(const MatrixF& mat);
+
+   void findContact(bool *run, bool *jump, VectorF *contactNormal);
+   Point3F getContactNormal() { return mContactInfo.contactNormal; }
+   SceneObject* getContactObject() { return mContactInfo.contactObject; }
+   bool isContacted() { return mContactInfo.contacted; }
+
+   //
+   void applyImpulse(const Point3F &pos, const VectorF &vec);
+
+   //This is a weird artifact of the PhysicsReps. We want the collision component to be privvy to any events that happen
+   //so when the physics components do a findContact test during their update, they'll have a signal collision components
+   //can be listening to to update themselves with that info
+   Signal< void(SceneObject*) > onContactSignal;
+
+   //
+   DECLARE_CALLBACK(void, updateMove, (PlayerControllerComponent* obj));
+};
+
+#endif // _COMPONENT_H_

+ 467 - 0
Engine/source/T3D/components/physics/rigidBodyComponent.cpp

@@ -0,0 +1,467 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/physics/rigidBodyComponent.h"
+#include "core/util/safeDelete.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "core/stream/bitStream.h"
+#include "console/engineAPI.h"
+#include "sim/netConnection.h"
+#include "T3D/physics/physicsBody.h"
+#include "T3D/physics/physicsPlugin.h"
+#include "T3D/physics/physicsWorld.h"
+#include "T3D/physics/physicsCollision.h"
+#include "T3D/components/collision/collisionComponent.h"
+
+bool RigidBodyComponent::smNoCorrections = false;
+bool RigidBodyComponent::smNoSmoothing = false;
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+RigidBodyComponent::RigidBodyComponent() : Component()   
+{
+   mMass = 20;
+   mDynamicFriction = 1;
+   mStaticFriction = 0.1f;
+   mRestitution = 10;
+   mLinearDamping = 0;
+   mAngularDamping = 0;
+   mLinearSleepThreshold = 1;
+   mAngularSleepThreshold = 1;
+   mWaterDampingScale = 0.1f;
+   mBuoyancyDensity = 1;
+
+   mSimType = SimType_ServerOnly;
+
+   mPhysicsRep = NULL;
+   mResetPos = MatrixF::Identity;
+
+   mOwnerColComponent = NULL;
+
+   mFriendlyName = "RigidBody(Component)";
+}
+
+RigidBodyComponent::~RigidBodyComponent()
+{
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(RigidBodyComponent);
+
+bool RigidBodyComponent::onAdd()
+{
+   if(! Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void RigidBodyComponent::onRemove()
+{
+   Parent::onRemove();
+}
+void RigidBodyComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
+void RigidBodyComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   if (isServerObject())
+   {
+      storeRestorePos();
+      PhysicsPlugin::getPhysicsResetSignal().notify(this, &RigidBodyComponent::_onPhysicsReset);
+   }
+
+   CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
+   if (colComp)
+   {
+      colComp->onCollisionChanged.notify(this, &RigidBodyComponent::updatePhysics);
+      updatePhysics(colComp->getCollisionData());
+   }
+   else
+      updatePhysics();
+}
+
+void RigidBodyComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+
+   if (isServerObject())
+   {
+      PhysicsPlugin::getPhysicsResetSignal().remove(this, &RigidBodyComponent::_onPhysicsReset);
+   }
+
+   CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
+   if (colComp)
+   {
+      colComp->onCollisionChanged.remove(this, &RigidBodyComponent::updatePhysics);
+   }
+
+   SAFE_DELETE(mPhysicsRep);
+}
+
+void RigidBodyComponent::componentAddedToOwner(Component *comp)
+{
+   CollisionComponent *colComp = dynamic_cast<CollisionComponent*>(comp);
+   if (colComp)
+   {
+      colComp->onCollisionChanged.notify(this, &RigidBodyComponent::updatePhysics);
+      updatePhysics(colComp->getCollisionData());
+   }
+}
+
+void RigidBodyComponent::componentRemovedFromOwner(Component *comp)
+{
+   //test if this is a shape component!
+   CollisionComponent *colComp = dynamic_cast<CollisionComponent*>(comp);
+   if (colComp)
+   {
+      colComp->onCollisionChanged.remove(this, &RigidBodyComponent::updatePhysics);
+      updatePhysics();
+   }
+}
+
+void RigidBodyComponent::ownerTransformSet(MatrixF *mat)
+{
+   if (mPhysicsRep)
+      mPhysicsRep->setTransform(mOwner->getTransform());
+}
+
+void RigidBodyComponent::updatePhysics(PhysicsCollision* collision)
+{
+   SAFE_DELETE(mPhysicsRep);
+
+   if (!PHYSICSMGR)
+      return;
+
+   mWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+
+   if (!collision)
+      return;
+
+   mPhysicsRep = PHYSICSMGR->createBody();
+
+   mPhysicsRep->init(collision, mMass, 0, mOwner, mWorld);
+
+   mPhysicsRep->setMaterial(mRestitution, mDynamicFriction, mStaticFriction);
+
+   mPhysicsRep->setDamping(mLinearDamping, mAngularDamping);
+   mPhysicsRep->setSleepThreshold(mLinearSleepThreshold, mAngularSleepThreshold);
+
+   mPhysicsRep->setTransform(mOwner->getTransform());
+
+   // The reset position is the transform on the server
+   // at creation time... its not used on the client.
+   if (isServerObject())
+   {
+      storeRestorePos();
+      PhysicsPlugin::getPhysicsResetSignal().notify(this, &RigidBodyComponent::_onPhysicsReset);
+   }
+}
+
+U32 RigidBodyComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (stream->writeFlag(mask & StateMask))
+   {
+      // This will encode the position relative to the control
+      // object position.  
+      //
+      // This will compress the position to as little as 6.25
+      // bytes if the position is within about 30 meters of the
+      // control object.
+      //
+      // Worst case its a full 12 bytes + 2 bits if the position
+      // is more than 500 meters from the control object.
+      //
+      stream->writeCompressedPoint(mState.position);
+
+      // Use only 3.5 bytes to send the orientation.
+      stream->writeQuat(mState.orientation, 9);
+
+      // If the server object has been set to sleep then
+      // we don't need to send any velocity.
+      if (!stream->writeFlag(mState.sleeping))
+      {
+         // This gives me ~0.015f resolution in velocity magnitude
+         // while only costing me 1 bit of the velocity is zero length,
+         // <5 bytes in normal cases, and <8 bytes if the velocity is
+         // greater than 1000.
+         AssertWarn(mState.linVelocity.len() < 1000.0f,
+            "PhysicsShape::packUpdate - The linVelocity is out of range!");
+         stream->writeVector(mState.linVelocity, 1000.0f, 16, 9);
+
+         // For angular velocity we get < 0.01f resolution in magnitude
+         // with the most common case being under 4 bytes.
+         AssertWarn(mState.angVelocity.len() < 10.0f,
+            "PhysicsShape::packUpdate - The angVelocity is out of range!");
+         stream->writeVector(mState.angVelocity, 10.0f, 10, 9);
+      }
+   }
+
+   return retMask;
+}
+
+void RigidBodyComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if (stream->readFlag()) // StateMask
+   {
+      PhysicsState state;
+
+      // Read the encoded and compressed position... commonly only 6.25 bytes.
+      stream->readCompressedPoint(&state.position);
+
+      // Read the compressed quaternion... 3.5 bytes.
+      stream->readQuat(&state.orientation, 9);
+
+      state.sleeping = stream->readFlag();
+      if (!state.sleeping)
+      {
+         stream->readVector(&state.linVelocity, 1000.0f, 16, 9);
+         stream->readVector(&state.angVelocity, 10.0f, 10, 9);
+      }
+
+      if (!smNoCorrections && mPhysicsRep && mPhysicsRep->isDynamic())
+      {
+         // Set the new state on the physics object immediately.
+         mPhysicsRep->applyCorrection(state.getTransform());
+
+         mPhysicsRep->setSleeping(state.sleeping);
+         if (!state.sleeping)
+         {
+            mPhysicsRep->setLinVelocity(state.linVelocity);
+            mPhysicsRep->setAngVelocity(state.angVelocity);
+         }
+
+         mPhysicsRep->getState(&mState);
+      }
+
+      // If there is no physics object then just set the
+      // new state... the tick will take care of the 
+      // interpolation and extrapolation.
+      if (!mPhysicsRep || !mPhysicsRep->isDynamic())
+         mState = state;
+   }
+}
+
+void RigidBodyComponent::processTick()
+{
+   Parent::processTick();
+
+   if (!mPhysicsRep || !PHYSICSMGR)
+      return;
+
+   // Note that unlike TSStatic, the serverside PhysicsShape does not
+   // need to play the ambient animation because even if the animation were
+   // to move collision shapes it would not affect the physx representation.
+
+   PROFILE_START(RigidBodyComponent_ProcessTick);
+
+   if (!mPhysicsRep->isDynamic())
+      return;
+
+   // SINGLE PLAYER HACK!!!!
+   if (PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject())
+   {
+      RigidBodyComponent *servObj = (RigidBodyComponent*)getServerObject();
+      mOwner->setTransform(servObj->mState.getTransform());
+      mRenderState[0] = servObj->mRenderState[0];
+      mRenderState[1] = servObj->mRenderState[1];
+
+      return;
+   }
+
+   // Store the last render state.
+   mRenderState[0] = mRenderState[1];
+
+   // If the last render state doesn't match the last simulation 
+   // state then we got a correction and need to 
+   Point3F errorDelta = mRenderState[1].position - mState.position;
+   const bool doSmoothing = !errorDelta.isZero() && !smNoSmoothing;
+
+   const bool wasSleeping = mState.sleeping;
+
+   // Get the new physics state.
+   mPhysicsRep->getState(&mState);
+   updateContainerForces();
+
+   // Smooth the correction back into the render state.
+   mRenderState[1] = mState;
+   if (doSmoothing)
+   {
+      F32 correction = mClampF(errorDelta.len() / 20.0f, 0.1f, 0.9f);
+      mRenderState[1].position.interpolate(mState.position, mRenderState[0].position, correction);
+      mRenderState[1].orientation.interpolate(mState.orientation, mRenderState[0].orientation, correction);
+   }
+
+   //Check if any collisions occured
+   findContact();
+
+   // If we haven't been sleeping then update our transform
+   // and set ourselves as dirty for the next client update.
+   if (!wasSleeping || !mState.sleeping)
+   {
+      // Set the transform on the parent so that
+      // the physics object isn't moved.
+      mOwner->setTransform(mState.getTransform());
+
+      // If we're doing server simulation then we need
+      // to send the client a state update.
+      if (isServerObject() && mPhysicsRep && !smNoCorrections &&
+         !PHYSICSMGR->isSinglePlayer() // SINGLE PLAYER HACK!!!!
+         )
+         setMaskBits(StateMask);
+   }
+
+   PROFILE_END();
+}
+
+void RigidBodyComponent::findContact()
+{
+   SceneObject *contactObject = NULL;
+
+   VectorF *contactNormal = new VectorF(0, 0, 0);
+
+   Vector<SceneObject*> overlapObjects;
+
+   mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
+
+   if (!overlapObjects.empty())
+   {
+      //fire our signal that the physics sim said collisions happened
+      onPhysicsCollision.trigger(*contactNormal, overlapObjects);
+   }
+}
+
+void RigidBodyComponent::_onPhysicsReset(PhysicsResetEvent reset)
+{
+   if (reset == PhysicsResetEvent_Store)
+      mResetPos = mOwner->getTransform();
+
+   else if (reset == PhysicsResetEvent_Restore)
+   {
+      mOwner->setTransform(mResetPos);
+   }
+}
+
+void RigidBodyComponent::storeRestorePos()
+{
+   mResetPos = mOwner->getTransform();
+}
+
+void RigidBodyComponent::applyImpulse(const Point3F &pos, const VectorF &vec)
+{
+   if (mPhysicsRep && mPhysicsRep->isDynamic())
+      mPhysicsRep->applyImpulse(pos, vec);
+}
+
+void RigidBodyComponent::applyRadialImpulse(const Point3F &origin, F32 radius, F32 magnitude)
+{
+   if (!mPhysicsRep || !mPhysicsRep->isDynamic())
+      return;
+
+   // TODO: Find a better approximation of the
+   // force vector using the object box.
+
+   VectorF force = mOwner->getWorldBox().getCenter() - origin;
+   F32 dist = force.magnitudeSafe();
+   force.normalize();
+
+   if (dist == 0.0f)
+      force *= magnitude;
+   else
+      force *= mClampF(radius / dist, 0.0f, 1.0f) * magnitude;
+
+   mPhysicsRep->applyImpulse(origin, force);
+
+   // TODO: There is no simple way to really sync this sort of an 
+   // event with the client.
+   //
+   // The best is to send the current physics snapshot, calculate the
+   // time difference from when this event occured and the time when the
+   // client recieves it, and then extrapolate where it should be.
+   //
+   // Even then its impossible to be absolutely sure its synced.
+   //
+   // Bottom line... you shouldn't use physics over the network like this.
+   //
+}
+
+void RigidBodyComponent::updateContainerForces()
+{
+   PROFILE_SCOPE(RigidBodyComponent_updateContainerForces);
+
+   // If we're not simulating don't update forces.
+   PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+   if (!world || !world->isEnabled())
+      return;
+
+   ContainerQueryInfo info;
+   info.box = mOwner->getWorldBox();
+   info.mass = mMass;
+
+   // Find and retreive physics info from intersecting WaterObject(s)
+   mOwner->getContainer()->findObjects(mOwner->getWorldBox(), WaterObjectType | PhysicalZoneObjectType, findRouter, &info);
+
+   // Calculate buoyancy and drag
+   F32 angDrag = mAngularDamping;
+   F32 linDrag = mLinearDamping;
+   F32 buoyancy = 0.0f;
+   Point3F cmass = mPhysicsRep->getCMassPosition();
+
+   F32 density = mBuoyancyDensity;
+   if (density > 0.0f)
+   {
+      if (info.waterCoverage > 0.0f)
+      {
+         F32 waterDragScale = info.waterViscosity * mWaterDampingScale;
+         F32 powCoverage = mPow(info.waterCoverage, 0.25f);
+
+         angDrag = mLerp(angDrag, angDrag * waterDragScale, powCoverage);
+         linDrag = mLerp(linDrag, linDrag * waterDragScale, powCoverage);
+      }
+
+      buoyancy = (info.waterDensity / density) * mPow(info.waterCoverage, 2.0f);
+
+      // A little hackery to prevent oscillation
+      // Based on this blog post:
+      // (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html)
+      // JCF: disabled!
+      Point3F buoyancyForce = buoyancy * -world->getGravity() * TickSec * mMass;
+      mPhysicsRep->applyImpulse(cmass, buoyancyForce);
+   }
+
+   // Update the dampening as the container might have changed.
+   mPhysicsRep->setDamping(linDrag, angDrag);
+
+   // Apply physical zone forces.
+   if (!info.appliedForce.isZero())
+      mPhysicsRep->applyImpulse(cmass, info.appliedForce);
+}

+ 183 - 0
Engine/source/T3D/components/physics/rigidBodyComponent.h

@@ -0,0 +1,183 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef RIGID_BODY_COMPONENT_H
+#define RIGID_BODY_COMPONENT_H
+
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef _T3D_PHYSICSCOMMON_H_
+#include "T3D/physics/physicsCommon.h"
+#endif
+#ifndef COLLISION_COMPONENT_H
+#include "T3D/components/collision/collisionComponent.h"
+#endif
+#ifndef PHYSICS_COMPONENT_INTERFACE_H
+#include "T3D/components/physics/physicsComponentInterface.h"
+#endif
+
+class PhysicsBody;
+
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class RigidBodyComponent : public Component, public PhysicsComponentInterface
+{
+   typedef Component Parent;
+
+   enum SimType
+   {
+      /// This physics representation only exists on the client
+      /// world and the server only does ghosting.
+      SimType_ClientOnly,
+
+      /// The physics representation only exists on the server world
+      /// and the client gets delta updates for rendering.
+      SimType_ServerOnly,
+
+      /// The physics representation exists on the client and the server
+      /// worlds with corrections occuring when the client gets out of sync.
+      SimType_ClientServer,
+
+      /// The bits used to pack the SimType field.
+      SimType_Bits = 3,
+
+   } mSimType;
+
+   //
+   //
+   /// The current physics state.
+   PhysicsState mState;
+
+   /// The previous and current render states.
+   PhysicsState mRenderState[2];
+
+   /// The abstracted physics actor.
+   PhysicsBody *mPhysicsRep;
+
+   PhysicsWorld *mWorld;
+
+   /// The starting position to place the shape when
+   /// the level begins or is reset.
+   MatrixF mResetPos;
+   //
+   //
+
+   /// If true then no corrections are sent from the server 
+   /// and/or applied from the client.
+   ///
+   /// This is only ment for debugging.
+   ///
+   static bool smNoCorrections;
+
+   /// If true then no smoothing is done on the client when
+   /// applying server corrections.
+   ///
+   /// This is only ment for debugging.
+   ///
+   static bool smNoSmoothing;
+
+   ///
+   F32 mMass;
+
+   /// 
+   F32 mDynamicFriction;
+
+   /// 
+   F32 mStaticFriction;
+
+   ///
+   F32 mRestitution;
+
+   ///
+   F32 mLinearDamping;
+
+   ///
+   F32 mAngularDamping;
+
+   /// 
+   F32 mLinearSleepThreshold;
+
+   ///
+   F32 mAngularSleepThreshold;
+
+   // A scale applied to the normal linear and angular damping
+   // when the object enters a water volume.
+   F32 mWaterDampingScale;
+
+   // The density of this object used for water buoyancy effects.
+   F32 mBuoyancyDensity;
+
+   CollisionComponent* mOwnerColComponent;
+
+   enum MaskBits {
+      PositionMask = Parent::NextFreeMask << 0,
+      FreezeMask = Parent::NextFreeMask << 1,
+      StateMask = Parent::NextFreeMask << 2,
+      VelocityMask = Parent::NextFreeMask << 3,
+      NextFreeMask = Parent::NextFreeMask << 4
+   };
+
+public:
+   RigidBodyComponent();
+   virtual ~RigidBodyComponent();
+   DECLARE_CONOBJECT(RigidBodyComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual void ownerTransformSet(MatrixF *mat);
+
+   inline F32 getMass() { return mMass; }
+   Point3F getVelocity() const { return mState.linVelocity; }
+   void applyImpulse(const Point3F &pos, const VectorF &vec);
+   void applyRadialImpulse(const Point3F &origin, F32 radius, F32 magnitude);
+
+   void updateContainerForces();
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void processTick();
+
+   void findContact();
+
+   /// Save the current transform as where we return to when a physics reset
+   /// event occurs. This is automatically set in onAdd but some manipulators
+   /// such as Prefab need to make use of this.
+   void storeRestorePos();
+
+   void updatePhysics(PhysicsCollision *collision = NULL);
+
+   void _onPhysicsReset(PhysicsResetEvent reset);
+};
+
+#endif // _RIGID_BODY_COMPONENT_H_

+ 530 - 0
Engine/source/T3D/components/render/meshComponent.cpp

@@ -0,0 +1,530 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+#include "platform/platform.h"
+#include "console/consoleTypes.h"
+#include "T3D/components/render/meshComponent.h"
+#include "core/util/safeDelete.h"
+#include "core/resourceManager.h"
+#include "core/stream/fileStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "core/stream/bitStream.h"
+#include "sim/netConnection.h"
+#include "gfx/gfxTransformSaver.h"
+#include "console/engineAPI.h"
+#include "lighting/lightQuery.h"
+#include "scene/sceneManager.h"
+#include "gfx/bitmap/ddsFile.h"
+#include "gfx/bitmap/ddsUtils.h"
+#include "gfx/gfxTextureManager.h"
+#include "materials/materialFeatureTypes.h"
+#include "renderInstance/renderImposterMgr.h"
+#include "util/imposterCapture.h"
+#include "gfx/sim/debugDraw.h"  
+#include "gfx/gfxDrawUtil.h"
+#include "materials/materialManager.h"
+#include "materials/matInstance.h"
+#include "core/strings/findMatch.h"
+#include "T3D/components/render/meshComponent_ScriptBinding.h"
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+MeshComponent::MeshComponent() : Component()
+{
+   mShapeName = StringTable->insert("");
+   mShapeAsset = StringTable->insert("");
+   mShapeInstance = NULL;
+
+   mChangingMaterials.clear();
+
+   mMaterials.clear();
+
+   mFriendlyName = "Mesh Component";
+   mComponentType = "Render";
+
+   mDescription = getDescriptionText("Causes the object to render a non-animating 3d shape using the file provided.");
+
+   mNetworked = true;
+   mNetFlags.set(Ghostable | ScopeAlways);
+}
+
+MeshComponent::~MeshComponent(){}
+
+IMPLEMENT_CO_NETOBJECT_V1(MeshComponent);
+
+//==========================================================================================
+void MeshComponent::boneObject::addObject(SimObject* object)
+{
+   SceneObject* sc = dynamic_cast<SceneObject*>(object);
+
+   if(sc && mOwner)
+   {
+      if(TSShape* shape = mOwner->getShape())
+      {
+         S32 nodeID = shape->findNode(mBoneName);
+
+         //we may have a offset on the shape's center
+         //so make sure we accomodate for that when setting up the mount offsets
+         MatrixF mat = mOwner->getNodeTransform(nodeID);
+
+         mOwner->getOwner()->mountObject(sc, nodeID, mat);
+      }
+   }
+}
+
+bool MeshComponent::onAdd()
+{
+   if(! Parent::onAdd())
+      return false;
+
+   // Register for the resource change signal.
+   ResourceManager::get().getChangedSignal().notify( this, &MeshComponent::_onResourceChanged );
+
+   return true;
+}
+
+void MeshComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   //get the default shape, if any
+   updateShape();
+}
+
+void MeshComponent::onRemove()
+{
+   Parent::onRemove();
+
+   mMeshAsset.clear();
+
+   SAFE_DELETE(mShapeInstance);
+}
+
+void MeshComponent::onComponentRemove()
+{
+   if(mOwner)
+   {
+      Point3F pos = mOwner->getPosition(); //store our center pos
+      mOwner->setObjectBox(Box3F(Point3F(-1,-1,-1), Point3F(1,1,1)));
+      mOwner->setPosition(pos);
+   }  
+
+   Parent::onComponentRemove();  
+}
+
+void MeshComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   //create a hook to our internal variables
+   addGroup("Model");
+   addProtectedField("MeshAsset", TypeAssetId, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn, 
+      "The asset Id used for the mesh.", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
+   endGroup("Model");
+}
+
+bool MeshComponent::_setMesh(void *object, const char *index, const char *data)
+{
+   MeshComponent *rbI = static_cast<MeshComponent*>(object);
+   
+   // Sanity!
+   AssertFatal(data != NULL, "Cannot use a NULL asset Id.");
+
+   return rbI->setMeshAsset(data);
+}
+
+bool MeshComponent::_setShape( void *object, const char *index, const char *data )
+{
+   MeshComponent *rbI = static_cast<MeshComponent*>(object);
+   rbI->mShapeName = StringTable->insert(data);
+   rbI->updateShape(); //make sure we force the update to resize the owner bounds
+   rbI->setMaskBits(ShapeMask);
+
+   return true;
+}
+
+bool MeshComponent::setMeshAsset(const char* assetName)
+{
+   // Fetch the asset Id.
+   mMeshAssetId = StringTable->insert(assetName);
+
+   mMeshAsset = mMeshAssetId;
+
+   if (mMeshAsset.isNull())
+   {
+      Con::errorf("[MeshComponent] Failed to load mesh asset.");
+      return false;
+   }
+
+   mShapeName = mMeshAssetId;
+   mShapeAsset = mShapeName;
+   updateShape(); //make sure we force the update to resize the owner bounds
+   setMaskBits(ShapeMask);
+
+   return true;
+}
+
+void MeshComponent::_onResourceChanged( const Torque::Path &path )
+{
+   if ( path != Torque::Path( mShapeName ) )
+      return;
+
+   updateShape();
+   setMaskBits(ShapeMask);
+}
+
+void MeshComponent::inspectPostApply()
+{
+   Parent::inspectPostApply();
+}
+
+U32 MeshComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (!mOwner || con->getGhostIndex(mOwner) == -1)
+   {
+      stream->writeFlag(false);
+      stream->writeFlag(false);
+
+      if (mask & ShapeMask)
+         retMask |= ShapeMask;
+      if (mask & MaterialMask)
+         retMask |= MaterialMask;
+      return retMask;
+   }
+
+   if (stream->writeFlag(mask & ShapeMask))
+   {
+      stream->writeString(mShapeName);
+   }
+
+   if (stream->writeFlag( mask & MaterialMask ))
+   {
+      stream->writeInt(mChangingMaterials.size(), 16);
+
+      for(U32 i=0; i < mChangingMaterials.size(); i++)
+      {
+         stream->writeInt(mChangingMaterials[i].slot, 16);
+
+         NetStringHandle matNameStr = mChangingMaterials[i].matName.c_str();
+         con->packNetStringHandleU(stream, matNameStr);
+      }
+
+      mChangingMaterials.clear();
+    }
+
+   return retMask;
+}
+
+void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if(stream->readFlag())
+   {
+      mShapeName = stream->readSTString();
+      setMeshAsset(mShapeName);
+      updateShape();
+   }
+
+   if(stream->readFlag())
+   {
+      mChangingMaterials.clear();
+      U32 materialCount = stream->readInt(16);
+
+      for(U32 i=0; i < materialCount; i++)
+      {
+         matMap newMatMap;
+         newMatMap.slot = stream->readInt(16);
+         newMatMap.matName = String(con->unpackNetStringHandleU(stream).getString());
+
+         mChangingMaterials.push_back(newMatMap);
+      }
+
+      updateMaterials();
+   }
+}
+
+void MeshComponent::prepRenderImage( SceneRenderState *state )
+{
+   if (!mEnabled || !mOwner || !mShapeInstance)
+      return;
+
+   Point3F cameraOffset;
+   mOwner->getRenderTransform().getColumn(3, &cameraOffset);
+   cameraOffset -= state->getDiffuseCameraPosition();
+   F32 dist = cameraOffset.len();
+   if (dist < 0.01f)
+      dist = 0.01f;
+
+   Point3F objScale = getOwner()->getScale();
+   F32 invScale = (1.0f / getMax(getMax(objScale.x, objScale.y), objScale.z));
+
+   mShapeInstance->setDetailFromDistance(state, dist * invScale);
+
+   if (mShapeInstance->getCurrentDetail() < 0)
+      return;
+
+   GFXTransformSaver saver;
+
+   // Set up our TS render state.
+   TSRenderState rdata;
+   rdata.setSceneState(state);
+   rdata.setFadeOverride(1.0f);
+   rdata.setOriginSort(false);
+
+   // We might have some forward lit materials
+   // so pass down a query to gather lights.
+   LightQuery query;
+   query.init(mOwner->getWorldSphere());
+   rdata.setLightQuery(&query);
+
+   MatrixF mat = mOwner->getRenderTransform();
+   Point3F renderPos = mat.getPosition();
+   EulerF renderRot = mat.toEuler();
+   mat.scale(objScale);
+   GFX->setWorldMatrix(mat);
+
+   mShapeInstance->render(rdata);
+}
+
+void MeshComponent::updateShape()
+{
+   bool isServer = isServerObject();
+
+   if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
+   {
+      if (mMeshAsset == NULL)
+         return;
+
+      mShape = mMeshAsset->getShape();
+
+      if (!mShape)
+         return;
+
+      setupShape();
+
+      //Do this on both the server and client
+      S32 materialCount = mShape->materialList->getMaterialNameList().size();
+
+      if(isServerObject())
+      {
+         //we need to update the editor
+         for (U32 i = 0; i < mFields.size(); i++)
+         {
+            //find any with the materialslot title and clear them out
+            if (FindMatch::isMatch("MaterialSlot*", mFields[i].mFieldName, false))
+            {
+               setDataField(mFields[i].mFieldName, NULL, "");
+               mFields.erase(i);
+               continue;
+            }
+         }
+
+         //next, get a listing of our materials in the shape, and build our field list for them
+         char matFieldName[128];
+
+         if(materialCount > 0)
+            mComponentGroup = StringTable->insert("Materials");
+
+         for(U32 i=0; i < materialCount; i++)
+         {
+            String materialname = mShape->materialList->getMaterialName(i);
+            if(materialname == String("ShapeBounds"))
+               continue;
+
+            dSprintf(matFieldName, 128, "MaterialSlot%d", i);
+            
+            addComponentField(matFieldName, "A material used in the shape file", "TypeAssetId", materialname, "");
+         }
+
+         if(materialCount > 0)
+            mComponentGroup = "";
+      }
+
+      if(mOwner != NULL)
+      {
+         Point3F min, max, pos;
+         pos = mOwner->getPosition();
+
+         mOwner->getWorldToObj().mulP(pos);
+
+         min = mShape->bounds.minExtents;
+         max = mShape->bounds.maxExtents;
+
+         mShapeBounds.set(min, max);
+
+         mOwner->setObjectBox(Box3F(min, max));
+
+         if( mOwner->getSceneManager() != NULL )
+            mOwner->getSceneManager()->notifyObjectDirty( mOwner );
+      }
+
+      //finally, notify that our shape was changed
+      onShapeInstanceChanged.trigger(this);
+   }
+}
+
+void MeshComponent::setupShape()
+{
+   mShapeInstance = new TSShapeInstance(mShape, true);
+}
+
+void MeshComponent::updateMaterials()
+{
+   if (mChangingMaterials.empty() || !mShape)
+      return;
+
+   TSMaterialList* pMatList = mShapeInstance->getMaterialList();
+   pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
+
+   const Vector<String> &materialNames = pMatList->getMaterialNameList();
+   for ( S32 i = 0; i < materialNames.size(); i++ )
+   {
+      const String &pName = materialNames[i];
+
+      for(U32 m=0; m < mChangingMaterials.size(); m++)
+      {
+         if(mChangingMaterials[m].slot == i)
+         {
+            pMatList->renameMaterial( i, mChangingMaterials[m].matName );
+         }
+      }
+
+      mChangingMaterials.clear();
+   }
+
+   // Initialize the material instances
+   mShapeInstance->initMaterialList();
+}
+
+MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
+{
+   if (mShape)
+   {
+      S32 nodeCount = getShape()->nodes.size();
+
+      if(nodeIdx >= 0 && nodeIdx < nodeCount)
+      {
+         //animate();
+         MatrixF mountTransform = mShapeInstance->mNodeTransforms[nodeIdx];
+         mountTransform.mul(mOwner->getRenderTransform());
+
+         return mountTransform;
+      }
+   }
+
+   return MatrixF::Identity;
+}
+
+S32 MeshComponent::getNodeByName(String nodeName)
+{
+   if (mShape)
+   {
+      S32 nodeIdx = getShape()->findNode(nodeName);
+
+      return nodeIdx;
+   }
+
+   return -1;
+}
+
+bool MeshComponent::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info)
+{
+   return false;
+}
+
+void MeshComponent::mountObjectToNode(SceneObject* objB, String node, MatrixF txfm)
+{
+   const char* test;
+   test = node.c_str();
+   if(dIsdigit(test[0]))
+   {
+      getOwner()->mountObject(objB, dAtoi(node), txfm);
+   }
+   else
+   {
+      if(TSShape* shape = getShape())
+      {
+         S32 idx = shape->findNode(node);
+         getOwner()->mountObject(objB, idx, txfm);
+      }
+   }
+}
+
+void MeshComponent::onDynamicModified(const char* slotName, const char* newValue)
+{
+   if(FindMatch::isMatch( "materialslot*", slotName, false ))
+   {
+      if(!getShape())
+         return;
+
+      S32 slot = -1;
+      String outStr( String::GetTrailingNumber( slotName, slot ) );
+
+      if(slot == -1)
+         return;
+
+      bool found = false;
+      for(U32 i=0; i < mChangingMaterials.size(); i++)
+      {
+         if(mChangingMaterials[i].slot == slot)
+         {
+            mChangingMaterials[i].matName = String(newValue);
+            found = true;
+         }
+      }
+
+      if(!found)
+      {
+         matMap newMatMap;
+         newMatMap.slot = slot;
+         newMatMap.matName = String(newValue);
+
+         mChangingMaterials.push_back(newMatMap);
+      }
+
+      setMaskBits(MaterialMask);
+   }
+
+   Parent::onDynamicModified(slotName, newValue);
+}
+
+void MeshComponent::changeMaterial(U32 slot, const char* newMat)
+{
+   
+   char fieldName[512];
+
+   //update our respective field
+   dSprintf(fieldName, 512, "materialSlot%d", slot);
+   setDataField(fieldName, NULL, newMat);
+}
+
+void MeshComponent::onInspect()
+{
+}
+
+void MeshComponent::onEndInspect()
+{
+}

+ 183 - 0
Engine/source/T3D/components/render/meshComponent.h

@@ -0,0 +1,183 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef STATIC_MESH_COMPONENT_H
+#define STATIC_MESH_COMPONENT_H
+
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef __RESOURCE_H__
+#include "core/resource.h"
+#endif
+#ifndef _TSSHAPE_H_
+#include "ts/tsShape.h"
+#endif
+#ifndef _SCENERENDERSTATE_H_
+#include "scene/sceneRenderState.h"
+#endif
+#ifndef _MBOX_H_
+#include "math/mBox.h"
+#endif
+#ifndef ENTITY_H
+#include "T3D/entity.h"
+#endif
+#ifndef _NETSTRINGTABLE_H_
+   #include "sim/netStringTable.h"
+#endif
+#ifndef CORE_INTERFACES_H
+#include "T3D/components/coreInterfaces.h"
+#endif
+#ifndef RENDER_COMPONENT_INTERFACE_H
+#include "T3D/components/render/renderComponentInterface.h"
+#endif
+#ifndef _ASSET_PTR_H_
+#include "assets/assetPtr.h"
+#endif 
+#ifndef _SHAPE_ASSET_H_
+#include "T3D/assets/ShapeAsset.h"
+#endif 
+#ifndef _GFXVERTEXFORMAT_H_
+#include "gfx/gfxVertexFormat.h"
+#endif
+
+class TSShapeInstance;
+class SceneRenderState;
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class MeshComponent : public Component,
+   public RenderComponentInterface,
+   public CastRayRenderedInterface,
+   public EditorInspectInterface
+{
+   typedef Component Parent;
+
+protected:
+   enum
+   {
+      ShapeMask = Parent::NextFreeMask,
+      MaterialMask = Parent::NextFreeMask << 1,
+      NextFreeMask = Parent::NextFreeMask << 2,
+   };
+
+   StringTableEntry		mShapeName;
+   StringTableEntry		mShapeAsset;
+   TSShape*		         mShape;
+   Box3F						mShapeBounds;
+   Point3F					mCenterOffset;
+
+   struct matMap
+   {
+      String matName;
+      U32 slot;
+   };
+
+   Vector<matMap>  mChangingMaterials;
+   Vector<matMap>  mMaterials;
+
+   class boneObject : public SimGroup
+   {
+      MeshComponent *mOwner;
+   public:
+      boneObject(MeshComponent *owner){ mOwner = owner; }
+
+      StringTableEntry mBoneName;
+      S32 mItemID;
+
+      virtual void addObject(SimObject *obj);
+   };
+
+   Vector<boneObject*> mNodesList;
+
+public:
+   StringTableEntry       mMeshAssetId;
+   AssetPtr<ShapeAsset>   mMeshAsset;
+
+   TSShapeInstance*       mShapeInstance;
+
+public:
+   MeshComponent();
+   virtual ~MeshComponent();
+   DECLARE_CONOBJECT(MeshComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void inspectPostApply();
+
+   virtual void prepRenderImage(SceneRenderState *state);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   Box3F getShapeBounds() { return mShapeBounds; }
+
+   virtual MatrixF getNodeTransform(S32 nodeIdx);
+   S32 getNodeByName(String nodeName);
+
+   void setupShape();
+   void updateShape();
+   void updateMaterials();
+
+   virtual void onComponentRemove();
+   virtual void onComponentAdd();
+
+   static bool _setMesh(void *object, const char *index, const char *data);
+   static bool _setShape(void *object, const char *index, const char *data);
+   const char* _getShape(void *object, const char *data);
+
+   bool setMeshAsset(const char* assetName);
+
+   virtual TSShape* getShape() { if (mMeshAsset)  return mMeshAsset->getShape(); else return NULL; }
+   virtual TSShapeInstance* getShapeInstance() { return mShapeInstance; }
+
+   Resource<TSShape> getShapeResource() { return mMeshAsset->getShapeResource(); }
+
+   void _onResourceChanged(const Torque::Path &path);
+
+   virtual bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info);
+
+   void mountObjectToNode(SceneObject* objB, String node, MatrixF txfm);
+
+   virtual void onDynamicModified(const char* slotName, const char* newValue);
+
+   void changeMaterial(U32 slot, const char* newMat);
+
+   virtual void onInspect();
+   virtual void onEndInspect();
+
+   virtual Vector<MatrixF> getNodeTransforms()
+   {
+      Vector<MatrixF> bob;
+      return bob;
+   }
+
+   virtual void setNodeTransforms(Vector<MatrixF> transforms)
+   {
+      return;
+   }
+};
+
+#endif

+ 155 - 0
Engine/source/T3D/components/render/meshComponent_ScriptBinding.h

@@ -0,0 +1,155 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "console/engineAPI.h"
+#include "T3D/components/render/meshComponent.h"
+#include "scene/sceneObject.h"
+#include "math/mTransform.h"
+
+DefineEngineMethod(MeshComponent, getShapeBounds, Box3F, (), ,
+   "@brief Get the cobject we're in contact with.\n\n"
+
+   "The controlling client is the one that will send moves to us to act on.\n"
+
+   "@return the ID of the controlling GameConnection, or 0 if this object is not "
+   "controlled by any client.\n"
+
+   "@see GameConnection\n")
+{
+   return object->getShapeBounds();
+}
+
+DefineEngineMethod(MeshComponent, mountObject, bool,
+   (SceneObject* objB, String node, TransformF txfm), (MatrixF::Identity),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (objB)
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      object->mountObjectToNode(objB, node, /*MatrixF::Identity*/txfm.getMatrix());
+      return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(MeshComponent, getNodeTransform, TransformF,
+   (S32 node), (-1),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (node != -1)
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      //object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
+      MatrixF mat = object->getNodeTransform(node);
+      return mat;
+   }
+
+   return TransformF::Identity;
+}
+
+DefineEngineMethod(MeshComponent, getNodeEulerRot, EulerF,
+   (S32 node, bool radToDeg), (-1, true),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (node != -1)
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      //object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
+      MatrixF mat = object->getNodeTransform(node);
+
+      EulerF eul = mat.toEuler();
+      if (radToDeg)
+         eul = EulerF(mRadToDeg(eul.x), mRadToDeg(eul.y), mRadToDeg(eul.z));
+
+      return eul;
+   }
+
+   return EulerF(0, 0, 0);
+}
+
+DefineEngineMethod(MeshComponent, getNodePosition, Point3F,
+   (S32 node), (-1),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (node != -1)
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      //object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
+      MatrixF mat = object->getNodeTransform(node);
+
+      return mat.getPosition();
+   }
+
+   return Point3F(0, 0, 0);
+}
+
+DefineEngineMethod(MeshComponent, getNodeByName, S32,
+   (String nodeName), ,
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (!nodeName.isEmpty())
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      //object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
+      S32 node = object->getNodeByName(nodeName);
+
+      return node;
+   }
+
+   return -1;
+}
+
+DefineEngineMethod(MeshComponent, changeMaterial, void, (U32 slot, const char* newMat), (0, ""),
+   "@brief Change one of the materials on the shape.\n\n")
+{
+   object->changeMaterial(slot, newMat);
+}

+ 62 - 0
Engine/source/T3D/components/render/renderComponentInterface.h

@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef RENDER_COMPONENT_INTERFACE_H
+#define RENDER_COMPONENT_INTERFACE_H
+
+#ifndef _TSSHAPE_H_
+#include "ts/tsShape.h"
+#endif
+#ifndef _TSSHAPEINSTANCE_H_
+#include "ts/tsShapeInstance.h"
+#endif
+#ifndef CORE_INTERFACES_H
+#include "T3D/components/coreInterfaces.h"
+#endif
+
+class RenderComponentInterface : public Interface < RenderComponentInterface >
+{
+public:
+   virtual void prepRenderImage(SceneRenderState *state) = 0;
+
+   virtual TSShape* getShape() = 0;
+
+   Signal< void(RenderComponentInterface*) > onShapeChanged;
+
+   virtual TSShapeInstance* getShapeInstance() = 0;
+
+   virtual MatrixF getNodeTransform(S32 nodeIdx) = 0;
+
+   virtual Vector<MatrixF> getNodeTransforms() = 0;
+
+   virtual void setNodeTransforms(Vector<MatrixF> transforms) = 0;
+
+   Signal< void(RenderComponentInterface*) > onShapeInstanceChanged;
+};
+
+class CastRayRenderedInterface// : public Interface<CastRayRenderedInterface>
+{
+public:
+   virtual bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info)=0;
+};
+
+#endif

+ 1939 - 0
Engine/source/T3D/entity.cpp

@@ -0,0 +1,1939 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "T3D/entity.h"
+#include "core/stream/bitStream.h"
+#include "console/consoleTypes.h"
+#include "console/consoleObject.h"
+#include "sim/netConnection.h"
+#include "scene/sceneRenderState.h"
+#include "scene/sceneManager.h"
+#include "T3D/gameBase/gameProcess.h"
+#include "console/engineAPI.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "math/mathIO.h"
+#include "math/mTransform.h"
+
+#include "T3D/components/coreInterfaces.h"
+#include "T3D/components/render/renderComponentInterface.h"
+#include "T3D/components/collision/collisionInterfaces.h"
+
+#include "gui/controls/guiTreeViewCtrl.h"
+#include "assets/assetManager.h"
+#include "assets/assetQuery.h"
+#include "T3D/assets/ComponentAsset.h"
+
+#include "console/consoleInternal.h"
+#include "T3D/gameBase/std/stdMoveList.h"
+
+#include "T3D/prefab.h"
+
+//
+#include "gfx/sim/debugDraw.h"
+//
+
+extern bool gEditingMission;
+
+// Client prediction
+static F32 sMinWarpTicks = 0.5f;       // Fraction of tick at which instant warp occurs
+static S32 sMaxWarpTicks = 3;          // Max warp duration in ticks
+static S32 sMaxPredictionTicks = 30;   // Number of ticks to predict
+
+IMPLEMENT_CO_NETOBJECT_V1(Entity);
+
+ConsoleDocClass(Entity,
+   "@brief Base Entity class.\n\n"
+
+   "Entity is typically made up of a shape and up to two particle emitters.  In most cases Entity objects are "
+   "not created directly.  They are usually produced automatically by other means, such as through the Explosion "
+   "class.  When an explosion goes off, its ExplosionData datablock determines what Entity to emit.\n"
+
+   "@tsexample\n"
+   "datablock ExplosionData(GrenadeLauncherExplosion)\n"
+   "{\n"
+   "   // Assiging Entity data\n"
+   "   Entity = GrenadeEntity;\n\n"
+   "   // Adjust how Entity is ejected\n"
+   "   EntityThetaMin = 10;\n"
+   "   EntityThetaMax = 60;\n"
+   "   EntityNum = 4;\n"
+   "   EntityNumVariance = 2;\n"
+   "   EntityVelocity = 25;\n"
+   "   EntityVelocityVariance = 5;\n\n"
+   "   // Note: other ExplosionData properties are not listed for this example\n"
+   "};\n"
+   "@endtsexample\n\n"
+
+   "@note Entity are client side only objects.\n"
+
+   "@see EntityData\n"
+   "@see ExplosionData\n"
+   "@see Explosion\n"
+
+   "@ingroup FX\n"
+   );
+
+Entity::Entity()
+{
+   //mTypeMask |= DynamicShapeObjectType | StaticObjectType | ;
+   mTypeMask |= EntityObjectType;
+   mNetFlags.set(Ghostable | ScopeAlways);
+
+   mPos = Point3F(0, 0, 0);
+   mRot = Point3F(0, 0, 0);
+
+   mDelta.pos = mDelta.posVec = Point3F::Zero;
+   mDelta.rot[0].identity();
+   mDelta.rot[1].identity();
+   mDelta.warpOffset.set(0.0f, 0.0f, 0.0f);
+
+   mDelta.warpTicks = mDelta.warpCount = 0;
+   mDelta.dt = 1.0f;
+   mDelta.move = NullMove;
+
+   mComponents.clear();
+
+   mStartComponentUpdate = false;
+
+   mInitialized = false;
+
+}
+
+Entity::~Entity()
+{
+
+}
+
+void Entity::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   removeField("DataBlock");
+
+   addGroup("Transform");
+
+   removeField("Position");
+   addProtectedField("Position", TypePoint3F, Offset(mPos, Entity), &_setPosition, &_getPosition, "Object world orientation.");
+
+   removeField("Rotation");
+   addProtectedField("Rotation", TypeRotationF, Offset(mRot, Entity), &_setRotation, &_getRotation, "Object world orientation.");
+
+   //These are basically renamed mountPos/Rot. pretty much there for conveinence
+   addField("LocalPosition", TypeMatrixPosition, Offset(mMount.xfm, Entity), "Position we are mounted at ( object space of our mount object ).");
+   addField("LocalRotation", TypeMatrixRotation, Offset(mMount.xfm, Entity), "Rotation we are mounted at ( object space of our mount object ).");
+
+   endGroup("Transform");
+}
+
+//
+bool Entity::_setPosition(void *object, const char *index, const char *data)
+{
+   Entity* so = static_cast<Entity*>(object);
+   if (so)
+   {
+      Point3F pos;
+
+      if (!dStrcmp(data, ""))
+         pos = Point3F(0, 0, 0);
+      else
+         Con::setData(TypePoint3F, &pos, 0, 1, &data);
+
+      so->setTransform(pos, so->mRot);
+   }
+   return false;
+}
+
+const char * Entity::_getPosition(void* obj, const char* data)
+{
+   Entity* so = static_cast<Entity*>(obj);
+   if (so)
+   {
+      Point3F pos = so->getPosition();
+
+      static const U32 bufSize = 256;
+      char* returnBuffer = Con::getReturnBuffer(bufSize);
+      dSprintf(returnBuffer, bufSize, "%g %g %g", pos.x, pos.y, pos.z);
+      return returnBuffer;
+   }
+   return "0 0 0";
+}
+
+bool Entity::_setRotation(void *object, const char *index, const char *data)
+{
+   Entity* so = static_cast<Entity*>(object);
+   if (so)
+   {
+      RotationF rot;
+      Con::setData(TypeRotationF, &rot, 0, 1, &data);
+
+      //so->mRot = rot;
+      //MatrixF mat = rot.asMatrixF();
+      //mat.setPosition(so->getPosition());
+      //so->setTransform(mat);
+      so->setTransform(so->getPosition(), rot);
+   }
+   return false;
+}
+
+const char * Entity::_getRotation(void* obj, const char* data)
+{
+   Entity* so = static_cast<Entity*>(obj);
+   if (so)
+   {
+      EulerF eulRot = so->mRot.asEulerF();
+
+      static const U32 bufSize = 256;
+      char* returnBuffer = Con::getReturnBuffer(bufSize);
+      dSprintf(returnBuffer, bufSize, "%g %g %g", mRadToDeg(eulRot.x), mRadToDeg(eulRot.y), mRadToDeg(eulRot.z));
+      return returnBuffer;
+   }
+   return "0 0 0";
+}
+
+bool Entity::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
+
+   resetWorldBox();
+   setObjectBox(mObjBox);
+
+   addToScene();
+
+   //Make sure we get positioned
+   setMaskBits(TransformMask);
+
+   return true;
+}
+
+void Entity::onRemove()
+{
+   clearComponents(true);
+
+   removeFromScene();
+
+   onDataSet.removeAll();
+
+   Parent::onRemove();
+}
+
+void Entity::onPostAdd()
+{
+   mInitialized = true;
+
+   //everything's done and added. go ahead and initialize the components
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      mComponents[i]->onComponentAdd();
+   }
+
+   if (isMethod("onAdd"))
+      Con::executef(this, "onAdd");
+}
+
+void Entity::setDataField(StringTableEntry slotName, const char *array, const char *value)
+{
+   Parent::setDataField(slotName, array, value);
+
+   onDataSet.trigger(this, slotName, value);
+}
+
+void Entity::onStaticModified(const char* slotName, const char* newValue)
+{
+   Parent::onStaticModified(slotName, newValue);
+
+   onDataSet.trigger(this, slotName, newValue);
+}
+
+//Updating
+void Entity::processTick(const Move* move)
+{
+   if (!isHidden())
+   {
+      if (mDelta.warpCount < mDelta.warpTicks)
+      {
+         mDelta.warpCount++;
+
+         // Set new pos.
+         mObjToWorld.getColumn(3, &mDelta.pos);
+         mDelta.pos += mDelta.warpOffset;
+         mDelta.rot[0] = mDelta.rot[1];
+         mDelta.rot[1].interpolate(mDelta.warpRot[0], mDelta.warpRot[1], F32(mDelta.warpCount) / mDelta.warpTicks);
+         setTransform(mDelta.pos, mDelta.rot[1]);
+
+         // Pos backstepping
+         mDelta.posVec.x = -mDelta.warpOffset.x;
+         mDelta.posVec.y = -mDelta.warpOffset.y;
+         mDelta.posVec.z = -mDelta.warpOffset.z;
+      }
+      else
+      {
+         if (isMounted())
+         {
+            MatrixF mat;
+            mMount.object->getMountTransform(mMount.node, mMount.xfm, &mat);
+            Parent::setTransform(mat);
+            Parent::setRenderTransform(mat);
+         }
+         else
+         {
+            if (!move)
+            {
+               if (isGhost())
+               {
+                  // If we haven't run out of prediction time,
+                  // predict using the last known move.
+                  if (mPredictionCount-- <= 0)
+                     return;
+
+                  move = &mDelta.move;
+               }
+               else
+               {
+                  move = &NullMove;
+               }
+            }
+         }
+      }
+
+      Move prevMove = lastMove;
+
+      if (move != NULL)
+         lastMove = *move;
+      else
+         lastMove = NullMove;
+
+      if (move && isServerObject())
+      {
+         if ((move->y != 0 || prevMove.y != 0) 
+            || (move->x != 0 || prevMove.x != 0) 
+            || (move->z != 0 || prevMove.x != 0))
+         {
+            if (isMethod("moveVectorEvent"))
+               Con::executef(this, "moveVectorEvent", move->x, move->y, move->z);
+         }
+
+         if (move->yaw != 0)
+         {
+            if (isMethod("moveYawEvent"))
+               Con::executef(this, "moveYawEvent", move->yaw);
+         }
+
+         if (move->pitch != 0)
+         {
+            if (isMethod("movePitchEvent"))
+               Con::executef(this, "movePitchEvent", move->pitch);
+         }
+
+         if (move->roll != 0)
+         {
+            if (isMethod("moveRollEvent"))
+               Con::executef(this, "moveRollEvent", move->roll);
+         }
+
+         for (U32 i = 0; i < MaxTriggerKeys; i++)
+         {
+            if (move->trigger[i] != prevMove.trigger[i])
+            {
+               if (isMethod("moveTriggerEvent"))
+                  Con::executef(this, "moveTriggerEvent", i, move->trigger[i]);
+            }
+         }
+      }
+
+      if (isMethod("processTick"))
+         Con::executef(this, "processTick");
+   }
+}
+
+void Entity::advanceTime(F32 dt)
+{
+}
+
+void Entity::interpolateTick(F32 dt)
+{
+   if (dt == 0.0f)
+   {
+      setRenderTransform(mDelta.pos, mDelta.rot[1]);
+   }
+   else
+   {
+      QuatF rot;
+      rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt);
+      Point3F pos = mDelta.pos + mDelta.posVec * dt;
+
+      setRenderTransform(pos, rot);
+   }
+
+   mDelta.dt = dt;
+}
+
+//Render
+void Entity::prepRenderImage(SceneRenderState *state)
+{
+}
+
+//Networking
+U32 Entity::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (stream->writeFlag(mask & TransformMask))
+   {
+      //mathWrite( *stream, getScale() );
+      //stream->writeAffineTransform(mObjToWorld);
+      //mathWrite(*stream, getPosition());
+      //mathWrite(*stream, mPos);
+
+      stream->writeCompressedPoint(mPos);
+      mathWrite(*stream, getRotation());
+
+      mDelta.move.pack(stream);
+
+      stream->writeFlag(!(mask & NoWarpMask));
+   }
+
+   /*if (stream->writeFlag(mask & MountedMask))
+   {
+      mathWrite(*stream, mMount.xfm.getPosition());
+      mathWrite(*stream, mMount.xfm.toEuler());
+   }*/
+
+   if (stream->writeFlag(mask & BoundsMask))
+   {
+      mathWrite(*stream, mObjBox);
+   }
+
+   //pass our behaviors around
+   if (mask & ComponentsMask || mask & InitialUpdateMask)
+   {
+      stream->writeFlag(true);
+      //now, we run through a list of our to-be-sent behaviors and begin sending them
+      //if any fail, we keep our list and re-queue the mask
+      S32 componentCount = mToLoadComponents.size();
+
+      //build our 'ready' list
+      //This requires both the instance and the instances' template to be prepped(if the template hasn't been ghosted,
+      //then we know we shouldn't be passing the instance's ghosts around yet)
+      U32 ghostedCompCnt = 0;
+      for (U32 i = 0; i < componentCount; i++)
+      {
+         if (con->getGhostIndex(mToLoadComponents[i]) != -1)
+            ghostedCompCnt++;
+      }
+
+      if (ghostedCompCnt != 0)
+      {
+         stream->writeFlag(true);
+
+         stream->writeFlag(mStartComponentUpdate);
+
+         //if not all the behaviors have been ghosted, we'll need another pass
+         if (ghostedCompCnt != componentCount)
+            retMask |= ComponentsMask;
+
+         //write the currently ghosted behavior count
+         stream->writeInt(ghostedCompCnt, 16);
+
+         for (U32 i = 0; i < mToLoadComponents.size(); i++)
+         {
+            //now fetch them and pass the ghost
+            S32 ghostIndex = con->getGhostIndex(mToLoadComponents[i]);
+            if (ghostIndex != -1)
+            {
+               stream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);
+               mToLoadComponents.erase(i);
+               i--;
+
+               mStartComponentUpdate = false;
+            }
+         }
+      }
+      else if (componentCount)
+      {
+         //on the odd chance we have behaviors to ghost, but NONE of them have been yet, just set the flag now
+         stream->writeFlag(false);
+         retMask |= ComponentsMask;
+      }
+      else
+         stream->writeFlag(false);
+   }
+   else
+      stream->writeFlag(false);
+
+   return retMask;
+}
+
+void Entity::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   if (stream->readFlag())
+   {
+      /*Point3F scale;
+      mathRead( *stream, &scale );
+      setScale( scale);*/
+
+      //MatrixF objToWorld;
+      //stream->readAffineTransform(&objToWorld);
+
+      Point3F pos;
+
+      stream->readCompressedPoint(&pos);
+      //mathRead(*stream, &pos);
+
+      RotationF rot;
+
+      mathRead(*stream, &rot);
+
+      mDelta.move.unpack(stream);
+
+      if (stream->readFlag() && isProperlyAdded())
+      {
+         // Determine number of ticks to warp based on the average
+         // of the client and server velocities.
+         /*mDelta.warpOffset = pos - mDelta.pos;
+
+         F32 dt = mDelta.warpOffset.len() / (0.5f * TickSec);
+
+         mDelta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5f), 1.0f) : 0.0f);
+
+         //F32 as = (speed + mVelocity.len()) * 0.5f * TickSec;
+         //F32 dt = (as > 0.00001f) ? mDelta.warpOffset.len() / as : sMaxWarpTicks;
+         //mDelta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5f), 1.0f) : 0.0f);
+
+         //mDelta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5f), 1.0f) : 0.0f);
+
+         //mDelta.warpTicks = sMaxWarpTicks;
+
+         mDelta.warpTicks = 0;
+
+         if (mDelta.warpTicks)
+         {
+            // Setup the warp to start on the next tick.
+            if (mDelta.warpTicks > sMaxWarpTicks)
+               mDelta.warpTicks = sMaxWarpTicks;
+            mDelta.warpOffset /= (F32)mDelta.warpTicks;
+
+            mDelta.rot[0] = rot.asQuatF();
+            mDelta.rot[1] = rot.asQuatF();
+
+            mDelta.rotOffset = rot.asEulerF() - mDelta.rot.asEulerF();
+
+            // Ignore small rotation differences
+            if (mFabs(mDelta.rotOffset.x) < 0.001f)
+               mDelta.rotOffset.x = 0;
+
+            if (mFabs(mDelta.rotOffset.y) < 0.001f)
+               mDelta.rotOffset.y = 0;
+
+            if (mFabs(mDelta.rotOffset.z) < 0.001f)
+               mDelta.rotOffset.z = 0;
+
+            mDelta.rotOffset /= (F32)mDelta.warpTicks;
+         }
+         else
+         {
+            // Going to skip the warp, server and client are real close.
+            // Adjust the frame interpolation to move smoothly to the
+            // new position within the current tick.
+            Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
+            if (mDelta.dt == 0)
+            {
+               mDelta.posVec.set(0.0f, 0.0f, 0.0f);
+               mDelta.rotVec.set(0.0f, 0.0f, 0.0f);
+            }
+            else
+            {
+               F32 dti = 1.0f / mDelta.dt;
+               mDelta.posVec = (cp - pos) * dti;
+               mDelta.rotVec.z = mRot.z - rot.z;
+
+               mDelta.rotVec.z *= dti;
+            }
+
+            mDelta.pos = pos;
+            mDelta.rot = rot;
+
+            setTransform(pos, rot);
+         }*/
+
+         Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
+         mDelta.warpOffset = pos - cp;
+
+         // Calc the distance covered in one tick as the average of
+         // the old speed and the new speed from the server.
+         VectorF vel = pos - mDelta.pos;
+         F32 dt, as = vel.len() * 0.5 * TickSec;
+
+         // Cal how many ticks it will take to cover the warp offset.
+         // If it's less than what's left in the current tick, we'll just
+         // warp in the remaining time.
+         if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks)
+            dt = mDelta.dt + sMaxWarpTicks;
+         else
+            dt = (dt <= mDelta.dt) ? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt;
+
+         // Adjust current frame interpolation
+         if (mDelta.dt)
+         {
+            mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt));
+            mDelta.posVec = (cp - mDelta.pos) / mDelta.dt;
+            QuatF cr;
+            cr.interpolate(mDelta.rot[1], mDelta.rot[0], mDelta.dt);
+
+            mDelta.rot[1].interpolate(cr, rot.asQuatF(), mDelta.dt / dt);
+            mDelta.rot[0].extrapolate(mDelta.rot[1], cr, mDelta.dt);
+         }
+
+         // Calculated multi-tick warp
+         mDelta.warpCount = 0;
+         mDelta.warpTicks = (S32)(mFloor(dt));
+         if (mDelta.warpTicks)
+         {
+            mDelta.warpOffset = pos - mDelta.pos;
+            mDelta.warpOffset /= mDelta.warpTicks;
+            mDelta.warpRot[0] = mDelta.rot[1];
+            mDelta.warpRot[1] = rot.asQuatF();
+         }
+      }
+      else
+      {
+         // Set the entity to the server position
+         mDelta.dt = 0;
+         mDelta.pos = pos;
+         mDelta.posVec.set(0, 0, 0);
+         mDelta.rot[1] = mDelta.rot[0] = rot.asQuatF();
+         mDelta.warpCount = mDelta.warpTicks = 0;
+         setTransform(pos, rot);
+      }
+   }
+
+   /*if (stream->readFlag())
+   {
+      Point3F mountOffset;
+      EulerF mountRot;
+      mathRead(*stream, &mountOffset);
+      mathRead(*stream, &mountRot);
+
+      RotationF rot = RotationF(mountRot);
+      mountRot = rot.asEulerF(RotationF::Degrees);
+
+      setMountOffset(mountOffset);
+      setMountRotation(mountRot);
+   }*/
+
+   if (stream->readFlag())
+   {
+      mathRead(*stream, &mObjBox);
+      resetWorldBox();
+   }
+
+   if (stream->readFlag())
+   {
+      //are we passing any behaviors currently?
+      if (stream->readFlag())
+      {
+         //if we've just started the update, clear our behaviors
+         if (stream->readFlag())
+            clearComponents(false);
+
+         S32 componentCount = stream->readInt(16);
+
+         for (U32 i = 0; i < componentCount; i++)
+         {
+            S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
+            addComponent(dynamic_cast<Component*>(con->resolveGhost(gIndex)));
+         }
+      }
+   }
+}
+
+//Manipulation
+void Entity::setTransform(const MatrixF &mat)
+{
+   //setMaskBits(TransformMask);
+   setMaskBits(TransformMask | NoWarpMask);
+
+   if (isMounted())
+   {
+      // Use transform from mounted object
+      Point3F newPos = mat.getPosition();
+      Point3F parentPos = mMount.object->getTransform().getPosition();
+
+      Point3F newOffset = newPos - parentPos;
+
+      if (!newOffset.isZero())
+      {
+         //setMountOffset(newOffset);
+         mPos = newOffset;
+      }
+
+      Point3F matEul = mat.toEuler();
+
+      //mRot = Point3F(mRadToDeg(matEul.x), mRadToDeg(matEul.y), mRadToDeg(matEul.z));
+
+      if (matEul != Point3F(0, 0, 0))
+      {
+         Point3F mountEul = mMount.object->getTransform().toEuler();
+         Point3F diff = matEul - mountEul;
+
+         //setMountRotation(Point3F(mRadToDeg(diff.x), mRadToDeg(diff.y), mRadToDeg(diff.z)));
+         mRot = diff;
+      }
+      else
+      {
+         //setMountRotation(Point3F(0, 0, 0));
+         mRot = Point3F(0, 0, 0);
+      }
+
+      RotationF addRot = mRot + RotationF(mMount.object->getTransform());
+      MatrixF transf = addRot.asMatrixF();
+      transf.setPosition(mPos + mMount.object->getPosition());
+
+      Parent::setTransform(transf);
+   }
+   else
+   {
+      //Are we part of a prefab?
+      /*Prefab* p = Prefab::getPrefabByChild(this);
+      if (p)
+      {
+         //just let our prefab know we moved
+         p->childTransformUpdated(this, mat);
+      }*/
+      //else
+      {
+         //mRot.set(mat);
+         //Parent::setTransform(mat);
+
+         RotationF rot = RotationF(mat);
+
+         EulerF tempRot = rot.asEulerF(RotationF::Degrees);
+
+         Point3F pos;
+
+         mat.getColumn(3,&pos);
+
+         setTransform(pos, rot);
+      }
+   }
+}
+
+void Entity::setTransform(Point3F position, RotationF rotation)
+{
+   if (isMounted())
+   {
+      mPos = position;
+      mRot = rotation;
+
+      RotationF addRot = mRot + RotationF(mMount.object->getTransform());
+      MatrixF transf = addRot.asMatrixF();
+      transf.setPosition(mPos + mMount.object->getPosition());
+
+      Parent::setTransform(transf);
+
+      setMaskBits(TransformMask);
+   }
+   else
+   {
+      /*MatrixF newMat, imat, xmat, ymat, zmat;
+      Point3F radRot = Point3F(mDegToRad(rotation.x), mDegToRad(rotation.y), mDegToRad(rotation.z));
+      xmat.set(EulerF(radRot.x, 0, 0));
+      ymat.set(EulerF(0.0f, radRot.y, 0.0f));
+      zmat.set(EulerF(0, 0, radRot.z));
+      imat.mul(zmat, xmat);
+      newMat.mul(imat, ymat);*/
+
+      MatrixF newMat = rotation.asMatrixF();
+
+      newMat.setColumn(3, position);
+
+      mPos = position;
+      mRot = rotation;
+
+      setMaskBits(TransformMask);
+      //if (isServerObject())
+      //   setMaskBits(TransformMask);
+
+      //setTransform(temp);
+
+      // This test is a bit expensive so turn it off in release.   
+#ifdef TORQUE_DEBUG
+      //AssertFatal( mat.isAffine(), "SceneObject::setTransform() - Bad transform (non affine)!" );
+#endif
+
+      //PROFILE_SCOPE(Entity_setTransform);
+
+      // Update the transforms.
+
+      Parent::setTransform(newMat);
+
+      onTransformSet.trigger(&newMat);
+
+      /*mObjToWorld = mWorldToObj = newMat;
+      mWorldToObj.affineInverse();
+      // Update the world-space AABB.
+      resetWorldBox();
+      // If we're in a SceneManager, sync our scene state.
+      if (mSceneManager != NULL)
+      mSceneManager->notifyObjectDirty(this);
+      setRenderTransform(newMat);*/
+   }
+}
+
+void Entity::setRenderTransform(const MatrixF &mat)
+{
+   Parent::setRenderTransform(mat);
+}
+
+void Entity::setRenderTransform(Point3F position, RotationF rotation)
+{
+   if (isMounted())
+   {
+      mPos = position;
+      mRot = rotation;
+
+      RotationF addRot = mRot + RotationF(mMount.object->getTransform());
+      MatrixF transf = addRot.asMatrixF();
+      transf.setPosition(mPos + mMount.object->getPosition());
+
+      Parent::setRenderTransform(transf);
+   }
+   else
+   {
+      MatrixF newMat = rotation.asMatrixF();
+
+      newMat.setColumn(3, position);
+
+      mPos = position;
+      mRot = rotation;
+
+      Parent::setRenderTransform(newMat);
+
+      onTransformSet.trigger(&newMat);
+   }
+}
+
+MatrixF Entity::getTransform()
+{
+   if (isMounted())
+   {
+      MatrixF mat;
+
+      //Use transform from mount
+      mMount.object->getMountTransform(mMount.node, mMount.xfm, &mat);
+
+      Point3F transPos = mat.getPosition() + mPos;
+
+      mat.mul(mRot.asMatrixF());
+
+      mat.setPosition(transPos);
+
+      return mat;
+   }
+   else
+   {
+      return Parent::getTransform();
+   }
+}
+
+void Entity::setMountOffset(Point3F posOffset)
+{
+   if (isMounted())
+   {
+      mMount.xfm.setColumn(3, posOffset);
+      //mPos = posOffset;
+      setMaskBits(MountedMask);
+   }
+}
+
+void Entity::setMountRotation(EulerF rotOffset)
+{
+   if (isMounted())
+   {
+      MatrixF temp, imat, xmat, ymat, zmat;
+
+      Point3F radRot = Point3F(mDegToRad(rotOffset.x), mDegToRad(rotOffset.y), mDegToRad(rotOffset.z));
+      xmat.set(EulerF(radRot.x, 0, 0));
+      ymat.set(EulerF(0.0f, radRot.y, 0.0f));
+      zmat.set(EulerF(0, 0, radRot.z));
+
+      imat.mul(zmat, xmat);
+      temp.mul(imat, ymat);
+
+      temp.setColumn(3, mMount.xfm.getPosition());
+
+      mMount.xfm = temp;
+      //mRot = RotationF(temp);
+      setMaskBits(MountedMask);
+   }
+}
+//
+void Entity::getCameraTransform(F32* pos, MatrixF* mat)
+{
+   Vector<CameraInterface*> updaters = getComponents<CameraInterface>();
+   for (Vector<CameraInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++) 
+   {
+      if ((*it)->getCameraTransform(pos, mat)) 
+      {
+         return;
+      }
+   }
+}
+
+void Entity::getMountTransform(S32 index, const MatrixF &xfm, MatrixF *outMat)
+{
+   RenderComponentInterface* renderInterface = getComponent<RenderComponentInterface>();
+
+   if (renderInterface)
+   {
+      renderInterface->getShapeInstance()->animate();
+      S32 nodeCount = renderInterface->getShapeInstance()->getShape()->nodes.size();
+
+      if (index >= 0 && index < nodeCount)
+      {
+         MatrixF mountTransform = renderInterface->getShapeInstance()->mNodeTransforms[index];
+         mountTransform.mul(xfm);
+         const Point3F& scale = getScale();
+
+         // The position of the mount point needs to be scaled.
+         Point3F position = mountTransform.getPosition();
+         position.convolve(scale);
+         mountTransform.setPosition(position);
+
+         // Also we would like the object to be scaled to the model.
+         outMat->mul(mObjToWorld, mountTransform);
+         return;
+      }
+   }
+
+   // Then let SceneObject handle it.
+   Parent::getMountTransform(index, xfm, outMat);
+}
+
+void Entity::getRenderMountTransform(F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat)
+{
+   RenderComponentInterface* renderInterface = getComponent<RenderComponentInterface>();
+
+   if (renderInterface && renderInterface->getShapeInstance())
+   {
+      renderInterface->getShapeInstance()->animate();
+      S32 nodeCount = renderInterface->getShapeInstance()->getShape()->nodes.size();
+
+      if (index >= 0 && index < nodeCount)
+      {
+         MatrixF mountTransform = renderInterface->getShapeInstance()->mNodeTransforms[index];
+         mountTransform.mul(xfm);
+         const Point3F& scale = getScale();
+
+         // The position of the mount point needs to be scaled.
+         Point3F position = mountTransform.getPosition();
+         position.convolve(scale);
+         mountTransform.setPosition(position);
+
+         // Also we would like the object to be scaled to the model.
+         outMat->mul(getRenderTransform(), mountTransform);
+         return;
+      }
+   }
+
+   // Then let SceneObject handle it.
+   Parent::getMountTransform(index, xfm, outMat);
+}
+
+void Entity::setForwardVector(VectorF newForward, VectorF upVector)
+{
+   MatrixF mat = getTransform();
+
+   VectorF up(0.0f, 0.0f, 1.0f);
+   VectorF axisX;
+   VectorF axisY = newForward;
+   VectorF axisZ;
+
+   if (upVector != VectorF::Zero)
+      up = upVector;
+
+   // Validate and normalize input:  
+   F32 lenSq;
+   lenSq = axisY.lenSquared();
+   if (lenSq < 0.000001f)
+   {
+      axisY.set(0.0f, 1.0f, 0.0f);
+      Con::errorf("Entity::setForwardVector() - degenerate forward vector");
+   }
+   else
+   {
+      axisY /= mSqrt(lenSq);
+   }
+
+
+   lenSq = up.lenSquared();
+   if (lenSq < 0.000001f)
+   {
+      up.set(0.0f, 0.0f, 1.0f);
+      Con::errorf("SceneObject::setForwardVector() - degenerate up vector - too small");
+   }
+   else
+   {
+      up /= mSqrt(lenSq);
+   }
+
+   if (fabsf(mDot(up, axisY)) > 0.9999f)
+   {
+      Con::errorf("SceneObject::setForwardVector() - degenerate up vector - same as forward");
+      // i haven't really tested this, but i think it generates something which should be not parallel to the previous vector:  
+      F32 tmp = up.x;
+      up.x = -up.y;
+      up.y = up.z;
+      up.z = tmp;
+   }
+
+   // construct the remaining axes:  
+   mCross(axisY, up, &axisX);
+   mCross(axisX, axisY, &axisZ);
+
+   mat.setColumn(0, axisX);
+   mat.setColumn(1, axisY);
+   mat.setColumn(2, axisZ);
+
+   setTransform(mat);
+}
+//
+//These basically just redirect to any collision behaviors we have
+bool Entity::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
+{
+   Vector<CastRayInterface*> updaters = getComponents<CastRayInterface>();
+   for (Vector<CastRayInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++)
+   {
+      if ((*it)->castRay(start, end, info))
+      {
+         return true;
+      }
+   }
+   return false;
+}
+
+bool Entity::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info)
+{
+   Vector<CastRayRenderedInterface*> updaters = getComponents<CastRayRenderedInterface>();
+   for (Vector<CastRayRenderedInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++)
+   {
+      if ((*it)->castRayRendered(start, end, info))
+      {
+         return true;
+      }
+   }
+   return false;
+}
+
+bool Entity::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere)
+{
+   Vector<BuildPolyListInterface*> updaters = getComponents<BuildPolyListInterface>();
+   for (Vector<BuildPolyListInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++)
+   {
+      return (*it)->buildPolyList(context, polyList, box, sphere);
+   }
+
+   return false;
+}
+
+void Entity::buildConvex(const Box3F& box, Convex* convex)
+{
+   Vector<BuildConvexInterface*> updaters = getComponents<BuildConvexInterface>();
+   for (Vector<BuildConvexInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++)
+   {
+      (*it)->buildConvex(box, convex);
+   }
+}
+
+//
+// Mounting and heirarchy manipulation
+void Entity::mountObject(SceneObject* objB, MatrixF txfm)
+{
+   Parent::mountObject(objB, -1, txfm);
+   Parent::addObject(objB);
+}
+
+void Entity::mountObject(SceneObject *obj, S32 node, const MatrixF &xfm)
+{
+   Parent::mountObject(obj, node, xfm);
+}
+
+void Entity::onMount(SceneObject *obj, S32 node)
+{
+   deleteNotify(obj);
+
+   // Are we mounting to a GameBase object?
+   Entity *entityObj = dynamic_cast<Entity*>(obj);
+
+   if (entityObj && entityObj->getControlObject() != this)
+      processAfter(entityObj);
+
+   if (!isGhost()) {
+      setMaskBits(MountedMask);
+
+      //TODO implement this callback
+      //onMount_callback( this, obj, node );
+   }
+}
+
+void Entity::onUnmount(SceneObject *obj, S32 node)
+{
+   clearNotify(obj);
+
+   Entity *entityObj = dynamic_cast<Entity*>(obj);
+
+   if (entityObj && entityObj->getControlObject() != this)
+      clearProcessAfter();
+
+   if (!isGhost()) {
+      setMaskBits(MountedMask);
+
+      //TODO implement this callback
+      //onUnmount_callback( this, obj, node );
+   }
+}
+
+//Heirarchy stuff
+void Entity::addObject(SimObject* object)
+{
+   Component* component = dynamic_cast<Component*>(object);
+   if (component)
+   {
+      addComponent(component);
+      return;
+   }
+
+   Entity* e = dynamic_cast<Entity*>(object);
+   if (e)
+   {
+      MatrixF offset;
+
+      //offset.mul(getWorldTransform(), e->getWorldTransform());
+
+      //check if we're mounting to a node on a shape we have
+      String node = e->getDataField("mountNode", NULL);
+      if (!node.isEmpty())
+      {
+         RenderComponentInterface *renderInterface = getComponent<RenderComponentInterface>();
+         if (renderInterface)
+         {
+            TSShape* shape = renderInterface->getShape();
+            S32 nodeIdx = shape->findNode(node);
+
+            mountObject(e, nodeIdx, MatrixF::Identity);
+         }
+         else
+         {
+            mountObject(e, MatrixF::Identity);
+         }
+      }
+      else
+      {
+         /*Point3F posOffset = mPos - e->getPosition();
+         mPos = posOffset;
+
+         RotationF rotOffset = mRot - e->getRotation();
+         mRot = rotOffset;
+         setMaskBits(TransformMask);
+         mountObject(e, MatrixF::Identity);*/
+
+         mountObject(e, MatrixF::Identity);
+      }
+
+      //e->setMountOffset(e->getPosition() - getPosition());
+
+      //Point3F diff = getWorldTransform().toEuler() - e->getWorldTransform().toEuler();
+
+      //e->setMountRotation(Point3F(mRadToDeg(diff.x),mRadToDeg(diff.y),mRadToDeg(diff.z)));
+
+      //mountObject(e, offset);
+   }
+   else
+   {
+      SceneObject* so = dynamic_cast<SceneObject*>(object);
+      if (so)
+      {
+         //get the difference and build it as our offset!
+         Point3F posOffset = so->getPosition() - mPos;
+         RotationF rotOffset = RotationF(so->getTransform()) - mRot;
+
+         MatrixF offset = rotOffset.asMatrixF();
+         offset.setPosition(posOffset);
+
+         mountObject(so, offset);
+         return;
+      }
+   }
+
+   Parent::addObject(object);
+}
+
+void Entity::removeObject(SimObject* object)
+{
+   Entity* e = dynamic_cast<Entity*>(object);
+   if (e)
+   {
+      mPos = mPos + e->getPosition();
+      mRot = mRot + e->getRotation();
+      unmountObject(e);
+      setMaskBits(TransformMask);
+   }
+   else
+   {
+      SceneObject* so = dynamic_cast<SceneObject*>(object);
+      if (so)
+         unmountObject(so);
+   }
+
+   Parent::removeObject(object);
+}
+
+bool Entity::addComponent(Component *comp)
+{
+   if (comp == NULL)
+      return false;
+
+   //double-check were not re-adding anything
+   mComponents.push_back(comp);
+
+   // Register the component with this owner.
+   comp->setOwner(this);
+
+   //if we've already been added and this is being added after the fact(at runtime), 
+   //then just go ahead and call it's onComponentAdd so it can get to work
+   if (mInitialized)
+      comp->onComponentAdd();
+
+   onComponentAdded.trigger(comp);
+
+   return true;
+}
+
+SimObject* Entity::findObjectByInternalName(StringTableEntry internalName, bool searchChildren)
+{
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      if (mComponents[i]->getInternalName() == internalName)
+      {
+         return mComponents[i];
+      }
+   }
+
+   return Parent::findObjectByInternalName(internalName, searchChildren);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+bool Entity::removeComponent(Component *comp, bool deleteComponent)
+{
+   if (comp == NULL)
+      return false;
+
+   if(mComponents.remove(comp))
+   {
+      AssertFatal(comp->isProperlyAdded(), "Don't know how but a component is not registered w/ the sim");
+
+      //setComponentsDirty();
+
+      onComponentRemoved.trigger(comp);
+
+      comp->setOwner(NULL);
+
+      comp->onComponentRemove(); //in case the behavior needs to do cleanup on the owner
+
+      if (deleteComponent)
+         comp->safeDeleteObject();
+
+      return true;
+   }
+
+   return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//NOTE:
+//The actor class calls this and flags the deletion of the behaviors to false so that behaviors that should no longer be attached during
+//a network update will indeed be removed from the object. The reason it doesn't delete them is because when clearing the local behavior
+//list, it would delete them, purging the ghost, and causing a crash when the unpack update tried to fetch any existing behaviors' ghosts
+//to re-add them. Need to implement a clean clear function that will clear the local list, and only delete unused behaviors during an update.
+void Entity::clearComponents(bool deleteComponents)
+{
+   bool srv = isServerObject();
+   if (!deleteComponents)
+   {
+      while (mComponents.size() > 0)
+      {
+         removeComponent(mComponents.first(), deleteComponents);
+      }
+   }
+   else
+   {
+      while (mComponents.size() > 0)
+      {
+         Component* comp = mComponents.first();
+
+         if (comp)
+         {
+            comp->onComponentRemove(); //in case the behavior needs to do cleanup on the owner
+
+            bool removed = mComponents.remove(comp);
+
+            //we only need to delete them on the server side. they'll be cleaned up on the client side
+            //via the ghosting system for us
+            if (isServerObject())
+               comp->deleteObject();
+         }
+      }
+   }
+}
+
+//////////////////////////////////////////////////////////////////////////
+Component *Entity::getComponent(const U32 index) const
+{
+   if (index < mComponents.size())
+      return mComponents[index];
+
+   return NULL;
+}
+
+Component *Entity::getComponent(String componentType)
+{
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      Component* comp = mComponents[i];
+
+      /*String namespaceName = comp->getNamespace()->mName;
+       //check our namespace first
+      if (namespaceName == componentType)
+      {
+         return comp;
+      }
+      else
+      {*/
+         //lets scan up, just to be sure
+         Namespace *NS = comp->getNamespace();
+
+         //we shouldn't ever go past Component into net object, as we're no longer dealing with component classes
+         while (dStrcmp(NS->getName(), "NetObject"))
+         {
+            String namespaceName = NS->getName();
+
+            if (namespaceName == componentType)
+            {
+               return comp;
+            }
+            else
+            {
+               NS = NS->getParent();
+            }
+         }
+      //}
+   }
+
+   return NULL;
+}
+
+void Entity::onInspect()
+{
+   Vector<EditorInspectInterface*> updaters = getComponents<EditorInspectInterface>();
+   for (Vector<EditorInspectInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++) 
+   {
+      (*it)->onInspect();
+   }
+
+   GuiTreeViewCtrl *editorTree = dynamic_cast<GuiTreeViewCtrl*>(Sim::findObject("EditorTree"));
+   if (!editorTree)
+      return;
+
+   GuiTreeViewCtrl::Item *newItem, *parentItem;
+
+   parentItem = editorTree->getItem(editorTree->findItemByObjectId(getId()));
+
+   S32 componentID = editorTree->insertItem(parentItem->getID(), "Components");
+
+   newItem = editorTree->getItem(componentID);
+   newItem->mState.set(GuiTreeViewCtrl::Item::VirtualParent);
+   newItem->mState.set(GuiTreeViewCtrl::Item::DenyDrag);
+   //newItem->mState.set(GuiTreeViewCtrl::Item::InspectorData);
+   newItem->mState.set(GuiTreeViewCtrl::Item::ForceItemName);
+   //newItem->mInspectorInfo.mObject = this;
+
+   AssetManager *assetDB = dynamic_cast<AssetManager*>(Sim::findObject("AssetDatabase"));
+   if (!assetDB)
+      return;
+
+   //This is used in the event of script-created assets, which likely only have
+   //the name and other 'friendly' properties stored in a ComponentAsset.
+   //So we'll do a query for those assets and find the asset based on the component's
+   //class name
+   AssetQuery* qry = new AssetQuery();
+   qry->registerObject();
+
+   assetDB->findAssetType(qry, "ComponentAsset");
+
+   for (U32 i = 0; i < mComponents.size(); ++i)
+   {
+      String compName = mComponents[i]->getFriendlyName();
+
+      if (compName == String(""))
+      {
+         String componentClass = mComponents[i]->getClassNamespace();
+
+         //Means that it's a script-derived component and we should consult the asset to try
+         //to get the info for it
+         S32 compAssetCount = qry->mAssetList.size();
+         for (U32 c = 0; c < compAssetCount; ++c)
+         {
+            StringTableEntry assetID = qry->mAssetList[c];
+
+            ComponentAsset* compAsset = assetDB->acquireAsset<ComponentAsset>(assetID);
+
+            String compAssetClass = compAsset->getComponentName();
+            if (componentClass == compAssetClass)
+            {
+               compName = compAsset->getFriendlyName();
+               break;
+            }
+         }
+      }
+
+      S32 compID = editorTree->insertItem(componentID, compName);
+      newItem = editorTree->getItem(compID);
+      newItem->mInspectorInfo.mObject = mComponents[i];
+      newItem->mState.set(GuiTreeViewCtrl::Item::ForceItemName);
+      newItem->mState.set(GuiTreeViewCtrl::Item::DenyDrag);
+      newItem->mState.set(GuiTreeViewCtrl::Item::InspectorData);
+   }
+
+   editorTree->buildVisibleTree(true);
+}
+
+void Entity::onEndInspect()
+{
+   Vector<EditorInspectInterface*> updaters = getComponents<EditorInspectInterface>();
+   for (Vector<EditorInspectInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++) {
+      (*it)->onEndInspect();
+   }
+
+   GuiTreeViewCtrl *editorTree = dynamic_cast<GuiTreeViewCtrl*>(Sim::findObject("EditorTree"));
+   if (!editorTree)
+      return;
+
+   S32 componentItemIdx = editorTree->findItemByName("Components");
+
+   editorTree->removeItem(componentItemIdx, false);
+}
+
+static void writeTabs(Stream &stream, U32 count)
+{
+   char tab[] = "   ";
+   while (count--)
+      stream.write(3, (void*)tab);
+}
+
+void Entity::write(Stream &stream, U32 tabStop, U32 flags)
+{
+   // Do *not* call parent on this
+
+   /*VectorPtr<ComponentObject *> &componentList = lockComponentList();
+   // export selected only?
+   if( ( flags & SelectedOnly ) && !isSelected() )
+   {
+   for( BehaviorObjectIterator i = componentList.begin(); i != componentList.end(); i++ )
+   (*i)->write(stream, tabStop, flags);
+
+   goto write_end;
+   }*/
+
+   //catch if we have any written behavior fields already in the file, and clear them. We don't need to double-up
+   //the entries for no reason.
+   /*if(getFieldDictionary())
+   {
+   //get our dynamic field count, then parse through them to see if they're a behavior or not
+
+   //reset it
+   SimFieldDictionary* fieldDictionary = getFieldDictionary();
+   SimFieldDictionaryIterator itr(fieldDictionary);
+   for (S32 i = 0; i < fieldDictionary->getNumFields(); i++)
+   {
+   if (!(*itr))
+   break;
+
+   SimFieldDictionary::Entry* entry = *itr;
+   if(strstr(entry->slotName, "_behavior"))
+   {
+   entry->slotName = "";
+   entry->value = "";
+   }
+
+   ++itr;
+   }
+   }*/
+   //all existing written behavior fields should be cleared. now write the object block
+
+   writeTabs(stream, tabStop);
+
+   char buffer[1024];
+   dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() ? getName() : "");
+   stream.write(dStrlen(buffer), buffer);
+   writeFields(stream, tabStop + 1);
+
+   stream.write(1, "\n");
+   ////first, write out our behavior objects
+
+   // NOW we write the behavior fields proper
+   if (mComponents.size() > 0)
+   {
+      // Pack out the behaviors into fields
+      U32 i = 0;
+      for (U32 i = 0; i < mComponents.size(); i++)
+      {
+         writeTabs(stream, tabStop + 1);
+         char buffer[1024];
+         dSprintf(buffer, sizeof(buffer), "new %s() {\r\n", mComponents[i]->getClassName());
+         stream.write(dStrlen(buffer), buffer);
+         //bi->writeFields( stream, tabStop + 2 );
+
+         mComponents[i]->packToStream(stream, tabStop + 2, i - 1, flags);
+
+         writeTabs(stream, tabStop + 1);
+         stream.write(4, "};\r\n");
+      }
+   }
+
+   //
+   //if (size() > 0)
+   //   stream.write(2, "\r\n");
+
+   for (U32 i = 0; i < size(); i++)
+   {
+      SimObject* child = (*this)[i];
+      if (child->getCanSave())
+         child->write(stream, tabStop + 1, flags);
+   }
+
+   //stream.write(2, "\r\n");
+
+   writeTabs(stream, tabStop);
+   stream.write(4, "};\r\n");
+
+   //write_end:
+   //unlockComponentList();
+}
+
+SimObject* Entity::getTamlChild(const U32 childIndex) const
+{
+   // Sanity!
+   AssertFatal(childIndex < getTamlChildCount(), "SimSet::getTamlChild() - Child index is out of range.");
+
+   // For when the assert is not used.
+   if (childIndex >= getTamlChildCount())
+      return NULL;
+
+   //we always order components first, child objects second
+   if (childIndex >= getComponentCount())
+      return at(childIndex - getComponentCount());
+   else
+      return getComponent(childIndex);
+}
+//
+void Entity::onCameraScopeQuery(NetConnection* connection, CameraScopeQuery* query)
+{
+   // Object itself is in scope.
+   Parent::onCameraScopeQuery(connection, query);
+
+   if (CameraInterface* cI = getComponent<CameraInterface>())
+   {
+      cI->onCameraScopeQuery(connection, query);
+   }
+}
+//
+void Entity::setObjectBox(Box3F objBox)
+{
+   mObjBox = objBox;
+   resetWorldBox();
+
+   if (isServerObject())
+      setMaskBits(BoundsMask);
+}
+
+void Entity::updateContainer()
+{
+   PROFILE_SCOPE(Entity_updateContainer);
+
+   // Update container drag and buoyancy properties
+   containerInfo.box = getWorldBox();
+   //containerInfo.mass = mMass;
+
+   getContainer()->findObjects(containerInfo.box, WaterObjectType | PhysicalZoneObjectType, findRouter, &containerInfo);
+
+   //mWaterCoverage = info.waterCoverage;
+   //mLiquidType    = info.liquidType;
+   //mLiquidHeight  = info.waterHeight;   
+   //setCurrentWaterObject( info.waterObject );
+
+   // This value might be useful as a datablock value,
+   // This is what allows the player to stand in shallow water (below this coverage)
+   // without jiggling from buoyancy
+   /*if (info.waterCoverage >= 0.25f)
+   {
+      // water viscosity is used as drag for in water.
+      // ShapeBaseData drag is used for drag outside of water.
+      // Combine these two components to calculate this ShapeBase object's 
+      // current drag.
+      mDrag = (info.waterCoverage * info.waterViscosity) +
+         (1.0f - info.waterCoverage) * mDrag;
+      //mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
+   }
+
+   //mAppliedForce = info.appliedForce;
+   mGravityMod = info.gravityScale;*/
+}
+//
+
+void Entity::setComponentsDirty()
+{
+   if (mToLoadComponents.empty())
+      mStartComponentUpdate = true;
+
+   //we need to build a list of behaviors that need to be pushed across the network
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      // We can do this because both are in the string table
+      Component *comp = mComponents[i];
+
+      if (comp->isNetworked())
+      {
+         bool unique = true;
+         for (U32 i = 0; i < mToLoadComponents.size(); i++)
+         {
+            if (mToLoadComponents[i]->getId() == comp->getId())
+            {
+               unique = false;
+               break;
+            }
+         }
+         if (unique)
+            mToLoadComponents.push_back(comp);
+      }
+   }
+
+   setMaskBits(ComponentsMask);
+}
+
+void Entity::setComponentDirty(Component *comp, bool forceUpdate)
+{
+   bool found = false;
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      if (mComponents[i]->getId() == comp->getId())
+      {
+         mComponents[i]->setOwner(this);
+         return;
+      }
+   }
+
+   if (!found)
+      return;
+
+   //if(mToLoadComponents.empty())
+   //	mStartComponentUpdate = true;
+
+   /*if (comp->isNetworked() || forceUpdate)
+   {
+      bool unique = true;
+      for (U32 i = 0; i < mToLoadComponents.size(); i++)
+      {
+         if (mToLoadComponents[i]->getId() == comp->getId())
+         {
+            unique = false;
+            break;
+         }
+      }
+      if (unique)
+         mToLoadComponents.push_back(comp);
+   }
+
+   setMaskBits(ComponentsMask);*/
+   
+}
+
+DefineEngineMethod(Entity, mountObject, bool,
+   (SceneObject* objB, TransformF txfm), (MatrixF::Identity),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (objB)
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      object->mountObject(objB, /*MatrixF::Identity*/txfm.getMatrix());
+      return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(Entity, setMountOffset, void,
+   (Point3F posOffset), (Point3F(0, 0, 0)),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   object->setMountOffset(posOffset);
+}
+
+DefineEngineMethod(Entity, setMountRotation, void,
+   (EulerF rotOffset), (EulerF(0, 0, 0)),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   object->setMountRotation(rotOffset);
+}
+
+DefineEngineMethod(Entity, getMountTransform, TransformF, (), ,
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   MatrixF mat;
+   object->getMountTransform(0, MatrixF::Identity, &mat);
+   return mat;
+}
+
+DefineEngineMethod(Entity, setBox, void,
+   (Point3F box), (Point3F(1, 1, 1)),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   object->setObjectBox(Box3F(-box, box));
+}
+
+
+/*DefineConsoleMethod(Entity, callOnComponents, void, (const char* functionName), ,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   object->callOnComponents(functionName);
+}
+
+ConsoleMethod(Entity, callMethod, void, 3, 64, "(methodName, argi) Calls script defined method\n"
+   "@param methodName The method's name as a string\n"
+   "@param argi Any arguments to pass to the method\n"
+   "@return No return value"
+   "@note %obj.callMethod( %methodName, %arg1, %arg2, ... );\n")
+
+{
+   object->callMethodArgList(argc - 1, argv + 2);
+}
+
+ConsoleMethod(Entity, addComponents, void, 2, 2, "() - Add all fielded behaviors\n"
+   "@return No return value")
+{
+   object->addComponents();
+}*/
+
+ConsoleMethod(Entity, addComponent, bool, 3, 3, "(ComponentInstance bi) - Add a behavior to the object\n"
+   "@param bi The behavior instance to add"
+   "@return (bool success) Whether or not the behavior was successfully added")
+{
+   Component *comp = dynamic_cast<Component *>(Sim::findObject(argv[2]));
+
+   if (comp != NULL)
+   {
+      bool success = object->addComponent(comp);
+
+      if (success)
+      {
+         //Placed here so we can differentiate against adding a new behavior during runtime, or when we load all
+         //fielded behaviors on mission load. This way, we can ensure that we only call the callback
+         //once everything is loaded. This avoids any problems with looking for behaviors that haven't been added yet, etc.
+         if (comp->isMethod("onBehaviorAdd"))
+            Con::executef(comp, "onBehaviorAdd");
+
+         return true;
+      }
+   }
+
+   return false;
+}
+
+ConsoleMethod(Entity, removeComponent, bool, 3, 4, "(ComponentInstance bi, [bool deleteBehavior = true])\n"
+   "@param bi The behavior instance to remove\n"
+   "@param deleteBehavior Whether or not to delete the behavior\n"
+   "@return (bool success) Whether the behavior was successfully removed")
+{
+   bool deleteComponent = true;
+   if (argc > 3)
+      deleteComponent = dAtob(argv[3]);
+
+   return object->removeComponent(dynamic_cast<Component *>(Sim::findObject(argv[2])), deleteComponent);
+}
+
+ConsoleMethod(Entity, clearComponents, void, 2, 2, "() - Clear all behavior instances\n"
+   "@return No return value")
+{
+   object->clearComponents();
+}
+
+ConsoleMethod(Entity, getComponentByIndex, S32, 3, 3, "(int index) - Gets a particular behavior\n"
+   "@param index The index of the behavior to get\n"
+   "@return (ComponentInstance bi) The behavior instance you requested")
+{
+   Component *comp = object->getComponent(dAtoi(argv[2]));
+
+   return (comp != NULL) ? comp->getId() : 0;
+}
+
+DefineConsoleMethod(Entity, getComponent, S32, (String componentName), (""),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   Component *comp = object->getComponent(componentName);
+
+   return (comp != NULL) ? comp->getId() : 0;
+   return 0;
+}
+
+/*ConsoleMethod(Entity, getBehaviorByType, S32, 3, 3, "(string BehaviorTemplateName) - gets a behavior\n"
+   "@param BehaviorTemplateName The name of the template of the behavior instance you want\n"
+   "@return (ComponentInstance bi) The behavior instance you requested")
+{
+   ComponentInstance *bInstance = object->getComponentByType(StringTable->insert(argv[2]));
+
+   return (bInstance != NULL) ? bInstance->getId() : 0;
+}*/
+
+/*ConsoleMethod(Entity, reOrder, bool, 3, 3, "(ComponentInstance inst, [int desiredIndex = 0])\n"
+   "@param inst The behavior instance you want to reorder\n"
+   "@param desiredIndex The index you want the behavior instance to be reordered to\n"
+   "@return (bool success) Whether or not the behavior instance was successfully reordered")
+{
+   Component *inst = dynamic_cast<Component *>(Sim::findObject(argv[1]));
+
+   if (inst == NULL)
+      return false;
+
+   U32 idx = 0;
+   if (argc > 2)
+      idx = dAtoi(argv[2]);
+
+   return object->reOrder(inst, idx);
+}*/
+
+ConsoleMethod(Entity, getComponentCount, S32, 2, 2, "() - Get the count of behaviors on an object\n"
+   "@return (int count) The number of behaviors on an object")
+{
+   return object->getComponentCount();
+}
+
+DefineConsoleMethod(Entity, setComponentDirty, void, (S32 componentID, bool forceUpdate), (0, false),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   /*Component* comp;
+   if (Sim::findObject(componentID, comp))
+      object->setComponentDirty(comp, forceUpdate);*/
+}
+
+DefineConsoleMethod(Entity, getMoveVector, VectorF, (),,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getControllingClient() != NULL)
+   {
+      //fetch our last move
+      if (object->lastMove.x != 0 || object->lastMove.y != 0 || object->lastMove.z != 0)
+         return VectorF(object->lastMove.x, object->lastMove.y, object->lastMove.z);
+   }
+
+   return VectorF::Zero;
+}
+
+DefineConsoleMethod(Entity, getMoveRotation, VectorF, (), ,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if(object->getControllingClient() != NULL)
+   {
+      //fetch our last move
+      if (object->lastMove.pitch != 0 || object->lastMove.roll != 0 || object->lastMove.yaw != 0)
+         return VectorF(object->lastMove.pitch, object->lastMove.roll, object->lastMove.yaw);
+   }
+
+   return VectorF::Zero;
+}
+
+DefineConsoleMethod(Entity, getMoveTrigger, bool, (S32 triggerNum), (0),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   if (object->getControllingClient() != NULL && triggerNum < MaxTriggerKeys)
+   {
+      return object->lastMove.trigger[triggerNum];
+   }
+
+   return false;
+}
+
+DefineConsoleMethod(Entity, setForwardVector, void, (VectorF newForward), (VectorF(0,0,0)),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   object->setForwardVector(newForward);
+}
+
+DefineConsoleMethod(Entity, lookAt, void, (Point3F lookPosition),,
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   //object->setForwardVector(newForward);
+}
+
+DefineConsoleMethod(Entity, rotateTo, void, (Point3F lookPosition, F32 degreePerSecond), (1.0),
+   "Get the number of static fields on the object.\n"
+   "@return The number of static fields defined on the object.")
+{
+   //object->setForwardVector(newForward);
+}

+ 283 - 0
Engine/source/T3D/entity.h

@@ -0,0 +1,283 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef ENTITY_H
+#define ENTITY_H
+
+#ifndef _GAMEBASE_H_
+#include "T3D/gameBase/gameBase.h"
+#endif
+#ifndef _MOVEMANAGER_H_
+#include "T3D/gameBase/moveManager.h"
+#endif
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef _CONTAINERQUERY_H_
+#include "T3D/containerQuery.h"
+#endif
+
+class Component;
+
+//**************************************************************************
+// Entity
+//**************************************************************************
+class Entity : public GameBase
+{
+   typedef GameBase Parent;
+   friend class Component;
+
+private:
+   Point3F             mPos;
+   RotationF           mRot;
+
+   Vector<Component*>         mComponents;
+
+   Vector<Component*>         mToLoadComponents;
+
+   bool                       mStartComponentUpdate;
+
+   ContainerQueryInfo containerInfo;
+
+   bool mInitialized;
+
+   Signal< void(Component*) > onComponentAdded;
+   Signal< void(Component*) > onComponentRemoved;
+
+   Signal< void(MatrixF*) > onTransformSet;
+
+protected:
+
+   virtual void   processTick(const Move* move);
+   virtual void   advanceTime(F32 dt);
+   virtual void   interpolateTick(F32 delta);
+
+   void prepRenderImage(SceneRenderState *state);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+
+public:
+   struct StateDelta
+   {
+      Move move;                    ///< Last move from server
+      F32 dt;                       ///< Last interpolation time
+      // Interpolation data
+      Point3F pos;
+      Point3F posVec;
+      QuatF rot[2];
+      // Warp data
+      S32 warpTicks;                ///< Number of ticks to warp
+      S32 warpCount;                ///< Current pos in warp
+      Point3F warpOffset;
+      QuatF warpRot[2];
+   };
+
+   enum MaskBits
+   {
+      TransformMask = Parent::NextFreeMask << 0,
+      BoundsMask = Parent::NextFreeMask << 1,
+      ComponentsMask = Parent::NextFreeMask << 2,
+      NoWarpMask = Parent::NextFreeMask << 3,
+      NextFreeMask = Parent::NextFreeMask << 4
+   };
+
+   StateDelta mDelta;
+   S32 mPredictionCount;            ///< Number of ticks to predict
+
+   Move lastMove;
+
+   //
+   Entity();
+   ~Entity();
+
+   static void    initPersistFields();
+   virtual void onPostAdd();
+
+   virtual void setTransform(const MatrixF &mat);
+   virtual void setRenderTransform(const MatrixF &mat);
+
+   void setTransform(Point3F position, RotationF rotation);
+
+   void setRenderTransform(Point3F position, RotationF rotation);
+
+   virtual MatrixF getTransform();
+   virtual Point3F getPosition() const { return mPos; }
+
+   //void setTransform(Point3F position, RotationF rot);
+
+   //void setRotation(RotationF rotation);
+
+   void setRotation(RotationF rotation) {
+      mRot = rotation;
+      setMaskBits(TransformMask);
+   };
+   RotationF getRotation() { return mRot; }
+
+   void setMountOffset(Point3F posOffset);
+   void setMountRotation(EulerF rotOffset);
+
+   //static bool _setEulerRotation( void *object, const char *index, const char *data );
+   static bool _setPosition(void *object, const char *index, const char *data);
+   static const char * _getPosition(void* obj, const char* data);
+
+   static bool _setRotation(void *object, const char *index, const char *data);
+   static const char * _getRotation(void* obj, const char* data);
+
+   virtual void getMountTransform(S32 index, const MatrixF &xfm, MatrixF *outMat);
+   virtual void getRenderMountTransform(F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat);
+
+   void setForwardVector(VectorF newForward, VectorF upVector = VectorF::Zero);
+
+   virtual void mountObject(SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity);
+   void mountObject(SceneObject* objB, MatrixF txfm);
+   void onMount(SceneObject *obj, S32 node);
+   void onUnmount(SceneObject *obj, S32 node);
+
+   // NetObject
+   U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
+   void unpackUpdate(NetConnection *conn, BitStream *stream);
+
+   void setComponentsDirty();
+   void setComponentDirty(Component *comp, bool forceUpdate = false);
+
+   //Components
+   virtual bool deferAddingComponents() const { return true; }
+
+   template <class T>
+   T* getComponent();
+   template <class T>
+   Vector<T*> getComponents();
+
+   Component* getComponent(String componentType);
+
+   U32 getComponentCount() const
+   { 
+      return mComponents.size(); 
+   }
+
+   virtual void setObjectBox(Box3F objBox);
+
+   void resetWorldBox() { Parent::resetWorldBox(); }
+   void resetObjectBox() { Parent::resetObjectBox(); }
+   void resetRenderWorldBox() { Parent::resetRenderWorldBox(); }
+
+   //function redirects for collisions
+   bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
+   bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info);
+   bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
+   virtual void buildConvex(const Box3F& box, Convex* convex);
+
+   Signal< void(SimObject*, String, String) > onDataSet;
+   virtual void setDataField(StringTableEntry slotName, const char *array, const char *value);
+   virtual void onStaticModified(const char* slotName, const char* newValue);
+
+   //void pushEvent(const char* eventName, Vector<const char*> eventParams);
+
+   void updateContainer();
+
+   ContainerQueryInfo getContainerInfo() { return containerInfo; }
+
+   //camera stuff
+   virtual void getCameraTransform(F32* pos, MatrixF* mat);
+   virtual void onCameraScopeQuery(NetConnection* connection, CameraScopeQuery* query);
+
+   //Heirarchy stuff
+   virtual void addObject(SimObject* object);
+   virtual void removeObject(SimObject* object);
+
+   virtual SimObject* findObjectByInternalName(StringTableEntry internalName, bool searchChildren);
+
+   //component stuff
+   bool addComponent(Component *comp);
+   bool removeComponent(Component *comp, bool deleteComponent);
+   void clearComponents(bool deleteComponents = true);
+   Component* getComponent(const U32 index) const;
+
+   void onInspect();
+   void onEndInspect();
+
+   virtual void write(Stream &stream, U32 tabStop, U32 flags);
+
+   // TamlChildren
+   virtual U32 getTamlChildCount(void) const
+   {
+      U32 componentCount = getComponentCount();
+      U32 childSize = (U32)size();
+      return componentCount + childSize;
+   }
+
+   virtual SimObject* getTamlChild(const U32 childIndex) const;
+
+   virtual void addTamlChild(SimObject* pSimObject)
+   {
+      // Sanity!
+      AssertFatal(pSimObject != NULL, "SimSet::addTamlChild() - Cannot add a NULL child object.");
+
+      addObject(pSimObject);
+   }
+
+   Box3F getObjectBox() { return mObjBox; }
+   MatrixF getWorldToObj() { return mWorldToObj; }
+   MatrixF getObjToWorld() { return mObjToWorld; }
+
+   DECLARE_CONOBJECT(Entity);
+
+};
+
+template <class T>
+T *Entity::getComponent()
+{
+   U32 componentCount = getComponentCount();
+   for (U32 i = 0; i < componentCount; i++)
+   {
+      T* t = dynamic_cast<T *>(mComponents[i]);
+
+      if (t) 
+      {
+         return t;
+      }
+   }
+   return NULL;
+}
+
+template <class T>
+Vector<T*> Entity::getComponents()
+{
+   Vector<T*> foundObjects;
+
+   T *curObj;
+   Component* comp;
+
+   // Loop through our child objects.
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      curObj = dynamic_cast<T*>(mComponents[i]);
+
+      // Add this child object if appropriate.
+      if (curObj)
+         foundObjects.push_back(curObj);
+   }
+
+   return foundObjects;
+}
+#endif //ENTITY_H

+ 1 - 1
Engine/source/T3D/fps/guiCrossHairHud.cpp

@@ -117,7 +117,7 @@ void GuiCrossHairHud::onRender(Point2I offset, const RectI &updateRect)
    GameConnection* conn = GameConnection::getConnectionToServer();
    if (!conn)
       return;
-   ShapeBase* control = dynamic_cast<ShapeBase*>(conn->getControlObject());
+   GameBase* control = dynamic_cast<GameBase*>(conn->getCameraObject());
    if (!control || !(control->getTypeMask() & ObjectMask) || !conn->isFirstPerson())
       return;
 

+ 79 - 2
Engine/source/T3D/gameBase/gameConnection.cpp

@@ -39,6 +39,11 @@
 #include "console/engineAPI.h"
 #include "math/mTransform.h"
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+#include "T3D/entity.h"
+#include "T3D/components/coreInterfaces.h"
+#endif
+
 #ifdef TORQUE_HIFI_NET
    #include "T3D/gameBase/hifi/hifiMoveList.h"
 #elif defined TORQUE_EXTENDED_MOVE
@@ -551,7 +556,9 @@ void GameConnection::setControlObject(GameBase *obj)
       obj->setControllingClient(this);
 
       // Update the camera's FOV to match the new control object
-      setControlCameraFov( obj->getCameraFov() );
+      //but only if we don't have a specific camera object
+      if (!mCameraObject)
+         setControlCameraFov(obj->getCameraFov());
    }
 
    // Okay, set our control object.
@@ -729,7 +736,21 @@ bool GameConnection::getControlCameraFov(F32 * fov)
    }
    if (cObj)
    {
+#ifdef TORQUE_EXPERIMENTAL_EC
+      if (Entity* ent = dynamic_cast<Entity*>(cObj))
+      {
+         if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
+         {
+            *fov = camInterface->getCameraFov();
+         }
+      }
+      else
+      {
+      *fov = cObj->getCameraFov();
+      }
+#else
       *fov = cObj->getCameraFov();
+#endif
       return(true);
    }
 
@@ -747,7 +768,26 @@ bool GameConnection::isValidControlCameraFov(F32 fov)
       obj = obj->getControlObject();
    }
 
-   return cObj ? cObj->isValidCameraFov(fov) : NULL;
+   if (cObj)
+   {
+#ifdef TORQUE_EXPERIMENTAL_EC
+      if (Entity* ent = dynamic_cast<Entity*>(cObj))
+      {
+         if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
+         {
+            return camInterface->isValidCameraFov(fov);
+         }
+      }
+      else
+      {
+         return cObj->isValidCameraFov(fov);
+      }
+#else
+      return cObj->isValidCameraFov(fov);
+#endif
+   }
+
+   return NULL;
 }
 
 bool GameConnection::setControlCameraFov(F32 fov)
@@ -762,9 +802,32 @@ bool GameConnection::setControlCameraFov(F32 fov)
    }
    if (cObj)
    {
+
+#ifdef TORQUE_EXPERIMENTAL_EC
+      F32 newFov = 90.f;
+      if (Entity* ent = dynamic_cast<Entity*>(cObj))
+      {
+         if (CameraInterface* camInterface = ent->getComponent<CameraInterface>())
+         {
+            camInterface->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
+            newFov = camInterface->getCameraFov();
+         }
+         else
+         {
+            Con::errorf("Attempted to setControlCameraFov, but we don't have a camera!");
+         }
+      }
+      else
+      {
+         // allow shapebase to clamp fov to its datablock values
+         cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
+         newFov = cObj->getCameraFov();
+      }
+#else
       // allow shapebase to clamp fov to its datablock values
       cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
       F32 newFov = cObj->getCameraFov();
+#endif
 
       // server fov of client has 1degree resolution
       if( S32(newFov) != S32(mCameraFov) || newFov != fov )
@@ -1147,10 +1210,17 @@ void GameConnection::readPacket(BitStream *bstream)
 
       if (bstream->readFlag())
       {
+         bool callScript = false;
+         if (mCameraObject.isNull())
+            callScript = true;
+
          S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
          GameBase* obj = dynamic_cast<GameBase*>(resolveGhost(gIndex));
          setCameraObject(obj);
          obj->readPacketData(this, bstream);
+
+         if (callScript)
+            initialControlSet_callback();
       }
       else
          setCameraObject(0);
@@ -1727,6 +1797,13 @@ DefineEngineMethod( GameConnection, transmitDataBlocks, void, (S32 sequence),,
             // Ensure that the client knows that the datablock send is done...
             object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
         }
+
+        if (iCount == 0)
+        {
+           //if we have no datablocks to send, we still need to be able to complete the level load process
+           //so fire off our callback anyways
+           object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
+        }
     } 
     else
     {

+ 11 - 0
Engine/source/T3D/gameBase/processList.cpp

@@ -27,6 +27,10 @@
 #include "platform/profiler.h"
 #include "console/consoleTypes.h"
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+#include "T3D/components/coreInterfaces.h"
+#include "T3D/components/component.h"
+#endif
 //----------------------------------------------------------------------------
 
 ProcessObject::ProcessObject()
@@ -268,6 +272,13 @@ void ProcessList::advanceObjects()
       onTickObject(pobj);
    }
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+   for (U32 i = 0; i < UpdateInterface::all.size(); i++)
+   {
+      UpdateInterface::all[i]->processTick();
+   }
+#endif
+
    mTotalTicks++;
 
    PROFILE_END();

+ 32 - 0
Engine/source/T3D/gameBase/std/stdGameProcess.cpp

@@ -37,6 +37,11 @@
 #include "T3D/gameBase/std/stdMoveList.h"
 #include "T3D/fx/cameraFXMgr.h"
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+#include "T3D/components/coreInterfaces.h"
+#include "T3D/components/component.h"
+#endif
+
 MODULE_BEGIN( ProcessList )
 
    MODULE_INIT
@@ -132,6 +137,18 @@ bool StdClientProcessList::advanceTime( SimTime timeDelta )
       obj = obj->mProcessLink.next;
    }
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+   for (U32 i = 0; i < UpdateInterface::all.size(); i++)
+   {
+      Component *comp = dynamic_cast<Component*>(UpdateInterface::all[i]);
+
+      if (!comp->isClientObject() || !comp->isActive())
+            continue;
+
+      UpdateInterface::all[i]->interpolateTick(mLastDelta);
+   }
+#endif
+
    // Inform objects of total elapsed delta so they can advance
    // client side animations.
    F32 dt = F32(timeDelta) / 1000;
@@ -146,6 +163,21 @@ bool StdClientProcessList::advanceTime( SimTime timeDelta )
       obj = obj->mProcessLink.next;
    }
    
+#ifdef TORQUE_EXPERIMENTAL_EC
+   for (U32 i = 0; i < UpdateInterface::all.size(); i++)
+   {
+      Component *comp = dynamic_cast<Component*>(UpdateInterface::all[i]);
+
+      if (comp)
+      {
+         if (!comp->isClientObject() || !comp->isActive())
+            continue;
+      }
+
+      UpdateInterface::all[i]->advanceTime(dt);
+   }
+#endif
+
    return ret;
 }
 

+ 1 - 1
Engine/source/T3D/gameFunctions.cpp

@@ -339,7 +339,7 @@ bool GameProcessCameraQuery(CameraQuery *query)
 
    if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix))
    {
-      query->object = dynamic_cast<ShapeBase*>(connection->getControlObject());
+      query->object = dynamic_cast<GameBase*>(connection->getCameraObject());
       query->nearPlane = gClientSceneGraph->getNearClip();
 
       // Scale the normal visible distance by the performance 

+ 5 - 3
Engine/source/T3D/lightAnimData.cpp

@@ -190,13 +190,15 @@ void LightAnimData::AnimValue<COUNT>::updateKey()
 }
 
 template<U32 COUNT>
-bool LightAnimData::AnimValue<COUNT>::animate( F32 time, F32 *output )
+bool LightAnimData::AnimValue<COUNT>::animate(F32 time, F32 *output, bool multiply)
 {
    F32 scaledTime, lerpFactor, valueRange, keyFrameLerp;
    U32 posFrom, posTo;
    S32 keyFrameFrom, keyFrameTo;
    F32 initialValue = *output;
-   
+   if (!multiply)
+      initialValue = 1;
+
    bool wasAnimated = false;
 
    for ( U32 i=0; i < COUNT; i++ )
@@ -305,6 +307,6 @@ void LightAnimData::animate( LightInfo *lightInfo, LightAnimState *state )
    lightInfo->setColor( color );
 
    F32 brightness = state->brightness;
-   mBrightness.animate( time, &brightness );
+   mBrightness.animate( time, &brightness, true );
    lightInfo->setBrightness( brightness );
 }

+ 1 - 1
Engine/source/T3D/lightAnimData.h

@@ -151,7 +151,7 @@ public:
       /// Performs the animation returning the results in the output if
       /// the time scale is greater than zero.
       /// @return Returns true if the animation was performed.
-      bool animate( F32 time, F32 *output );
+      bool animate(F32 time, F32 *output, bool multiply = false);
 
       /// Called when the key string is changed to update the
       /// key length and time scale.

+ 10 - 3
Engine/source/T3D/objectTypes.h

@@ -147,21 +147,25 @@ enum SceneObjectTypes
    /// @see PhysicalZone
    PhysicalZoneObjectType = BIT( 22 ),
 
+   EntityObjectType = BIT(23),
    /// @}
 };
 
 enum SceneObjectTypeMasks
 {
-   STATIC_COLLISION_TYPEMASK = StaticShapeObjectType,
+   STATIC_COLLISION_TYPEMASK = (StaticShapeObjectType |
+   EntityObjectType),
 
    DAMAGEABLE_TYPEMASK = (   PlayerObjectType        |
+                              EntityObjectType |
                              VehicleObjectType ),
 
    /// Typemask for objects that should be rendered into shadow passes.
    /// These should be all objects that are either meant to receive or cast
    /// shadows or both.
    SHADOW_TYPEMASK = (  StaticShapeObjectType |
-                        DynamicShapeObjectType ),
+   DynamicShapeObjectType |
+   EntityObjectType),
 
    /// Typemask for objects that should be subjected to more fine-grained
    /// culling tests.  Anything that is trivial rendering stuff or doesn't
@@ -172,6 +176,7 @@ enum SceneObjectTypeMasks
    CULLING_INCLUDE_TYPEMASK = (  GameBaseObjectType | // Includes most other renderable types; but broader than we ideally want.
                                  StaticShapeObjectType |
                                  DynamicShapeObjectType |
+                                 EntityObjectType |
                                  ZoneObjectType ), // This improves the result of zone traversals.
 
    /// Mask for objects that should be specifically excluded from zone culling.
@@ -185,7 +190,9 @@ enum SceneObjectTypeMasks
                                  StaticShapeObjectType |
                                  DynamicShapeObjectType |
                                  LightObjectType | // Flares.
-                                 GameBaseObjectType ),
+                                 GameBaseObjectType |
+                                 TriggerObjectType | 
+                                 EntityObjectType),
 
    /// Typemask to use for rendering when inside the editor.
    EDITOR_RENDER_TYPEMASK = U32( -1 ),

+ 86 - 0
Engine/source/T3D/physics/bullet/btBody.cpp

@@ -378,3 +378,89 @@ void BtBody::setSimulationEnabled( bool enabled )
 
    mIsEnabled = enabled;
 }
+
+void BtBody::findContact(SceneObject **contactObject,
+   VectorF *contactNormal,
+   Vector<SceneObject*> *outOverlapObjects) const
+{
+   AssertFatal(mActor, "BtPlayer::findContact - The controller is null!");
+
+   VectorF normal;
+   F32 maxDot = -1.0f;
+
+   // Go thru the contact points... get the first contact.
+   //mWorld->getDynamicsWorld()->computeOverlappingPairs();
+   btOverlappingPairCache *pairCache = mWorld->getDynamicsWorld()->getBroadphase()->getOverlappingPairCache();
+
+   btBroadphasePairArray& pairArray = pairCache->getOverlappingPairArray();
+   U32 numPairs = pairArray.size();
+   btManifoldArray manifoldArray;
+
+   for (U32 i = 0; i < numPairs; i++)
+   {
+      const btBroadphasePair &pair = pairArray[i];
+
+      btBroadphasePair *collisionPair = pairCache->findPair(pair.m_pProxy0, pair.m_pProxy1);
+      if (!collisionPair || !collisionPair->m_algorithm)
+         continue;
+
+      btCollisionObject *other = (btCollisionObject*)pair.m_pProxy0->m_clientObject;
+      if (other == mActor)
+         other = (btCollisionObject*)pair.m_pProxy1->m_clientObject;
+
+     // AssertFatal(!outOverlapObjects->contains(PhysicsUserData::getObject(other->getUserPointer())),
+      //   "Got multiple pairs of the same object!");
+      outOverlapObjects->push_back(PhysicsUserData::getObject(other->getUserPointer()));
+
+      if (other->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE)
+         continue;
+
+      manifoldArray.clear();
+      collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
+
+      for (U32 j = 0; j < manifoldArray.size(); j++)
+      {
+         btPersistentManifold *manifold = manifoldArray[j];
+         btScalar directionSign = manifold->getBody0() == mActor ? 1.0f : -1.0f;
+
+         for (U32 p = 0; p < manifold->getNumContacts(); p++)
+         {
+            const btManifoldPoint &pt = manifold->getContactPoint(p);
+
+            // Test the normal... is it the most vertical one we got?
+            normal = btCast<Point3F>(pt.m_normalWorldOnB * directionSign);
+            F32 dot = mDot(normal, VectorF(0, 0, 1));
+            if (dot > maxDot)
+            {
+               maxDot = dot;
+
+               btCollisionObject *colObject = (btCollisionObject*)collisionPair->m_pProxy0->m_clientObject;
+               *contactObject = PhysicsUserData::getObject(colObject->getUserPointer());
+               *contactNormal = normal;
+            }
+         }
+      }
+   }
+}
+
+void BtBody::moveKinematicTo(const MatrixF &transform)
+{
+   AssertFatal(mActor, "BtBody::moveKinematicTo - The actor is null!");
+
+   U32 bodyflags = mActor->getCollisionFlags();
+   const bool isKinematic = bodyflags & BF_KINEMATIC;
+   if (!isKinematic)
+   {
+      Con::errorf("BtBody::moveKinematicTo is only for kinematic bodies.");
+      return;
+   }
+
+   if (mCenterOfMass)
+   {
+      MatrixF xfm;
+      xfm.mul(transform, *mCenterOfMass);
+      mActor->setCenterOfMassTransform(btCast<btTransform>(xfm));
+   }
+   else
+      mActor->setCenterOfMassTransform(btCast<btTransform>(transform));
+}

+ 4 - 0
Engine/source/T3D/physics/bullet/btBody.h

@@ -111,6 +111,10 @@ public:
                               F32 staticFriction );
    virtual void applyCorrection( const MatrixF &xfm );
    virtual void applyImpulse( const Point3F &origin, const Point3F &force );
+
+   virtual void findContact(SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects) const;
+   virtual void moveKinematicTo(const MatrixF &xfm);
+
 };
 
 #endif // _T3D_PHYSICS_BTBODY_H_

+ 15 - 10
Engine/source/T3D/physics/bullet/btPlayer.cpp

@@ -71,6 +71,7 @@ void BtPlayer::init( const char *type,
    mObject = obj;
    mWorld = (BtWorld*)world;
 
+   mSlopeAngle = runSurfaceCos;
    mStepHeight = stepHeight;
 
    //if ( dStricmp( type, "Capsule" ) == 0 )
@@ -102,6 +103,17 @@ Point3F BtPlayer::move( const VectorF &disp, CollisionList &outCol )
 {
    AssertFatal( mGhostObject, "BtPlayer::move - The controller is null!" );
 
+   if (!mWorld->isEnabled())
+   {
+      btTransform currentTrans = mGhostObject->getWorldTransform();
+      btVector3 currentPos = currentTrans.getOrigin();
+
+      Point3F returnPos = btCast<Point3F>(currentPos);
+     
+      returnPos.z -= mOriginOffset;
+      return returnPos;
+   }
+
    // First recover from any penetrations from the previous tick.
    U32 numPenetrationLoops = 0;
    bool touchingContact = false;
@@ -305,16 +317,9 @@ bool BtPlayer::_sweep( btVector3 *inOutCurrPos, const btVector3 &disp, Collision
          col.normal = btCast<Point3F>( callback.m_hitNormalWorld );
          col.object = PhysicsUserData::getObject( callback.m_hitCollisionObject->getUserPointer() );
 
-         if (disp.z() < 0.0f)
-         {
-            // We're sweeping down as part of the stepping routine.    In this
-            // case we want to have the collision normal only point in the opposite direction.
-            // i.e. up  If we include the sideways part of the normal then the Player class
-            // velocity calculations using this normal will affect the player's forwards
-            // momentum.  This is especially noticable on stairs as the rounded bottom of
-            // the capsule slides up the corner of a stair.
-            col.normal.set(0.0f, 0.0f, 1.0f);
-         }
+         F32 vd = col.normal.z;
+         if (vd < mSlopeAngle)
+            return false;
       }
 
       return true;

+ 4 - 0
Engine/source/T3D/physics/bullet/btPlayer.h

@@ -57,6 +57,10 @@ protected:
    ///
    F32 mOriginOffset;
 
+   ///
+   F32 mSlopeAngle;
+   ///
+
    ///
    F32 mStepHeight;
    ///

+ 8 - 0
Engine/source/T3D/physics/physicsBody.h

@@ -113,6 +113,14 @@ public:
 
    ///
    virtual void applyImpulse( const Point3F &origin, const Point3F &force ) = 0;
+
+   virtual void findContact(SceneObject **contactObject,
+      VectorF *contactNormal,
+      Vector<SceneObject*> *outOverlapObjects) const = 0;
+
+   ///
+   virtual void moveKinematicTo(const MatrixF &xfm) = 0;
+
 };
 
 

+ 70 - 0
Engine/source/T3D/physics/physx3/px3Body.cpp

@@ -417,3 +417,73 @@ void Px3Body::applyImpulse( const Point3F &origin, const Point3F &force )
 
 }
 
+void Px3Body::findContact(SceneObject **contactObject,
+   VectorF *contactNormal,
+   Vector<SceneObject*> *outOverlapObjects) const
+{
+   // Calculate the sweep motion...
+   F32 halfCapSize = mOriginOffset;
+   F32 halfSmallCapSize = halfCapSize * 0.8f;
+   F32 diff = halfCapSize - halfSmallCapSize;
+
+   F32 distance = diff + mSkinWidth + 0.01f;
+   physx::PxVec3 dir(0, 0, -1);
+
+   physx::PxScene *scene = mWorld->getScene();
+   physx::PxHitFlags hitFlags(physx::PxHitFlag::eDEFAULT);
+   physx::PxQueryFilterData filterData(physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::eSTATIC);
+   filterData.data.word0 = PX3_DEFAULT;
+   physx::PxSweepHit sweepHit;
+   physx::PxRigidDynamic *actor = mController->getActor();
+   physx::PxU32 shapeIndex;
+
+   bool hit = physx::PxRigidBodyExt::linearSweepSingle(*actor, *scene, dir, distance, hitFlags, sweepHit, shapeIndex, filterData);
+   if (hit)
+   {
+      PhysicsUserData *data = PhysicsUserData::cast(sweepHit.actor->userData);
+      if (data)
+      {
+         *contactObject = data->getObject();
+         *contactNormal = px3Cast<Point3F>(sweepHit.normal);
+      }
+   }
+
+   // Check for overlapped objects ( triggers )
+
+   if (!outOverlapObjects)
+      return;
+
+   filterData.data.word0 = PX3_TRIGGER;
+
+   const physx::PxU32 bufferSize = 10;
+   physx::PxOverlapBufferN<bufferSize> hitBuffer;
+   hit = scene->overlap(mGeometry, actor->getGlobalPose(), hitBuffer, filterData);
+   if (hit)
+   {
+      for (U32 i = 0; i < hitBuffer.nbTouches; i++)
+      {
+         PhysicsUserData *data = PhysicsUserData::cast(hitBuffer.touches[i].actor->userData);
+         if (data)
+            outOverlapObjects->push_back(data->getObject());
+      }
+   }
+
+}void Px3Body::moveKinematicTo(const MatrixF &transform)
+{
+   AssertFatal(mActor, "Px3Body::moveKinematicTo - The actor is null!");
+
+   const bool isKinematic = mBodyFlags & BF_KINEMATIC;
+   if (!isKinematic)
+   {
+      Con::errorf("Px3Body::moveKinematicTo is only for kinematic bodies.");
+      return;
+   }
+
+   mWorld->lockScene();
+
+   physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
+   actor->setKinematicTarget(px3Cast<physx::PxTransform>(transform));
+
+   mWorld->unlockScene();
+}
+

+ 5 - 0
Engine/source/T3D/physics/physx3/px3Body.h

@@ -117,6 +117,11 @@ public:
                               F32 staticFriction );
    virtual void applyCorrection( const MatrixF &xfm );
    virtual void applyImpulse( const Point3F &origin, const Point3F &force );
+
+   virtual void findContact(SceneObject **contactObject, VectorF *contactNormal,
+      Vector<SceneObject*> *outOverlapObjects) const;
+   virtual void moveKinematicTo(const MatrixF &xfm);
+
 };
 
 #endif // _PX3BODY_H_

+ 30 - 0
Engine/source/T3D/physics/physx3/px3Player.cpp

@@ -329,3 +329,33 @@ Box3F Px3Player::getWorldBounds()
    return px3Cast<Box3F>( bounds );
 }
 
+bool Px3Player::testSpacials(const Point3F &nPos, const Point3F &nSize) const
+{
+   F32 offset = nSize.z * 0.5f;
+   F32 radius = getMax(nSize.x, nSize.y) * 0.5f - mSkinWidth;
+   F32 height = (nSize.z - (radius * 2.0f)) * 0.5f;
+   height -= mSkinWidth * 2.0f;
+   physx::PxCapsuleGeometry geom(radius, height);
+
+   physx::PxVec3 pos(nPos.x, nPos.y, nPos.z + offset);
+   physx::PxQuat orientation(Float_HalfPi, physx::PxVec3(0.0f, 1.0f, 0.0f));
+
+   physx::PxOverlapBuffer hit;
+   physx::PxQueryFilterData queryFilter(physx::PxQueryFlag::eANY_HIT | physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC);
+   queryFilter.data.word0 = PX3_DEFAULT;
+   bool hasHit = mWorld->getScene()->overlap(geom, physx::PxTransform(pos, orientation), hit, queryFilter);
+
+   return !hasHit;   // Return true if there are no overlapping objects
+}
+
+void Px3Player::setSpacials(const Point3F &nPos, const Point3F &nSize)
+{
+   mOriginOffset = nSize.z * 0.5f;
+   F32 radius = getMax(nSize.x, nSize.y) * 0.5f - mSkinWidth;
+   F32 height = nSize.z - (radius * 2.0f);
+   height -= mSkinWidth * 2.0f;
+
+   mWorld->releaseWriteLock();
+   mController->resize(height);
+   px3GetFirstShape(mController->getActor())->getCapsuleGeometry(mGeometry);
+}

+ 2 - 2
Engine/source/T3D/physics/physx3/px3Player.h

@@ -94,8 +94,8 @@ public:
                         PhysicsWorld *world );
    virtual Point3F move( const VectorF &displacement, CollisionList &outCol );
    virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects ) const;
-   virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; }
-   virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {}
+   virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const;
+   virtual void setSpacials( const Point3F &nPos, const Point3F &nSize );
    virtual void enableCollision();
    virtual void disableCollision();
 };

+ 58 - 1
Engine/source/T3D/physics/physx3/px3World.cpp

@@ -62,7 +62,8 @@ Px3World::Px3World(): mScene( NULL ),
    mIsEnabled( false ),
    mEditorTimeScale( 1.0f ),
    mAccumulator( 0 ),
-   mControllerManager( NULL )
+   mControllerManager(NULL),
+   mIsSceneLocked(false)
 {
 }
 
@@ -335,6 +336,62 @@ void Px3World::releaseWriteLock()
 	//AssertFatal( mScene->isWritable(), "PhysX3World::releaseWriteLock() - We should have been writable now!" );
 }
 
+void Px3World::lockScenes()
+{
+   Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
+
+   if (world)
+      world->lockScene();
+
+   world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
+
+   if (world)
+      world->lockScene();
+}
+
+void Px3World::unlockScenes()
+{
+   Px3World *world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("server"));
+
+   if (world)
+      world->unlockScene();
+
+   world = dynamic_cast<Px3World*>(PHYSICSMGR->getWorld("client"));
+
+   if (world)
+      world->unlockScene();
+}
+
+void Px3World::lockScene()
+{
+   if (!mScene)
+      return;
+
+   if (mIsSceneLocked)
+   {
+      Con::printf("Px3World: Attempting to lock a scene that is already locked.");
+      return;
+   }
+
+   mScene->lockWrite();
+   mIsSceneLocked = true;
+}
+
+void Px3World::unlockScene()
+{
+   if (!mScene)
+      return;
+
+   if (!mIsSceneLocked)
+   {
+      Con::printf("Px3World: Attempting to unlock a scene that is not locked.");
+      return;
+   }
+
+   mScene->unlockWrite();
+   mIsSceneLocked = false;
+}
+
 bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
 {
     

+ 5 - 0
Engine/source/T3D/physics/physx3/px3World.h

@@ -56,6 +56,7 @@ protected:
 	bool mIsEnabled;
 	bool mIsSimulating;
 	bool mIsServer;
+   bool mIsSceneLocked;
 	U32 mTickCount;
 	ProcessList *mProcessList;
 	F32 mEditorTimeScale;
@@ -96,11 +97,15 @@ public:
 	void releaseWriteLock();
 	bool isServer(){return mIsServer;}
 	physx::PxController* createController( physx::PxControllerDesc &desc );
+   void lockScene();
+   void unlockScene();
 	//static
 	static bool restartSDK( bool destroyOnly = false, Px3World *clientWorld = NULL, Px3World *serverWorld = NULL );
 	static void releaseWriteLocks();
 	static physx::PxCooking *getCooking();
    static void setTiming(F32 stepTime,U32 maxIterations);
+   static void lockScenes();
+   static void unlockScenes();
 };
 
 #endif // _PX3WORLD_H_

+ 10 - 9
Engine/source/T3D/shapeBase.cpp

@@ -4881,17 +4881,18 @@ DefineEngineMethod( ShapeBase, getTargetCount, S32, (),,
    
    "@see getTargetName()\n")
 {
-	ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
-	if(obj)
-	{
-		// Try to use the client object (so we get the reskinned targets in the Material Editor)
-		if ((ShapeBase*)obj->getClientObject())
-			obj = (ShapeBase*)obj->getClientObject();
+   ShapeBase *obj = dynamic_cast< ShapeBase* > ( object );
+   if(obj)
+   {
+      // Try to use the client object (so we get the reskinned targets in the Material Editor)
+      if ((ShapeBase*)obj->getClientObject())
+         obj = (ShapeBase*)obj->getClientObject();
 
-		return obj->getShapeInstance()->getTargetCount();
+      if (obj->getShapeInstance() != NULL)
+         return obj->getShapeInstance()->getTargetCount();
 	}
-
-	return -1;
+   
+   return -1;
 }
 
 DefineEngineMethod( ShapeBase, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),,

+ 42 - 1
Engine/source/T3D/vehicles/vehicle.cpp

@@ -46,6 +46,9 @@
 #include "gfx/primBuilder.h"
 #include "gfx/gfxDrawUtil.h"
 #include "materials/materialDefinition.h"
+#include "T3D/physics/physicsPlugin.h"
+#include "T3D/physics/physicsBody.h"
+#include "T3D/physics/physicsCollision.h"
 
 
 namespace {
@@ -203,7 +206,8 @@ VehicleData::VehicleData()
    dMemset(waterSound, 0, sizeof(waterSound));
 
    collDamageThresholdVel = 20;
-   collDamageMultiplier   = 0.05f;
+   collDamageMultiplier = 0.05f;
+   enablePhysicsRep = true;
 }
 
 
@@ -315,6 +319,7 @@ void VehicleData::packData(BitStream* stream)
    stream->write(softSplashSoundVel);
    stream->write(medSplashSoundVel);
    stream->write(hardSplashSoundVel);
+   stream->write(enablePhysicsRep);
 
    // write the water sound profiles
    for(i = 0; i < MaxSounds; i++)
@@ -411,6 +416,7 @@ void VehicleData::unpackData(BitStream* stream)
    stream->read(&softSplashSoundVel);
    stream->read(&medSplashSoundVel);
    stream->read(&hardSplashSoundVel);
+   stream->read(&enablePhysicsRep);
 
    // write the water sound profiles
    for(i = 0; i < MaxSounds; i++)
@@ -465,6 +471,11 @@ void VehicleData::unpackData(BitStream* stream)
 
 void VehicleData::initPersistFields()
 {
+   addGroup("Physics");
+   addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, VehicleData),
+      "@brief Creates a representation of the object in the physics plugin.\n");
+   endGroup("Physics");
+
    addField( "jetForce", TypeF32, Offset(jetForce, VehicleData),
       "@brief Additional force applied to the vehicle when it is jetting.\n\n"
       "For WheeledVehicles, the force is applied in the forward direction. For "
@@ -682,6 +693,8 @@ Vehicle::Vehicle()
    mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
    mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
    mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
+
+   mPhysicsRep = NULL;
 }
 
 U32 Vehicle::getCollisionMask()
@@ -695,6 +708,25 @@ Point3F Vehicle::getVelocity() const
    return mRigid.linVelocity;
 }
 
+void Vehicle::_createPhysics()
+{
+   SAFE_DELETE(mPhysicsRep);
+
+   if (!PHYSICSMGR || !mDataBlock->enablePhysicsRep)
+      return;
+
+   TSShape *shape = mShapeInstance->getShape();
+   PhysicsCollision *colShape = NULL;
+   colShape = shape->buildColShape(false, getScale());
+
+   if (colShape)
+   {
+      PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+      mPhysicsRep = PHYSICSMGR->createBody();
+      mPhysicsRep->init(colShape, 0, PhysicsBody::BF_KINEMATIC, this, world);
+      mPhysicsRep->setTransform(getTransform());
+   }
+}
 //----------------------------------------------------------------------------
 
 bool Vehicle::onAdd()
@@ -776,11 +808,15 @@ bool Vehicle::onAdd()
    mConvex.box.maxExtents.convolve(mObjScale);
    mConvex.findNodeTransform();
 
+   _createPhysics();
+
    return true;
 }
 
 void Vehicle::onRemove()
 {
+   SAFE_DELETE(mPhysicsRep);
+
    U32 i=0;
    for( i=0; i<VehicleData::VC_NUM_DUST_EMITTERS; i++ )
    {
@@ -880,6 +916,11 @@ void Vehicle::processTick(const Move* move)
       setPosition(mRigid.linPosition, mRigid.angPosition);
       setMaskBits(PositionMask);
       updateContainer();
+
+      //TODO: Only update when position has actually changed
+      //no need to check if mDataBlock->enablePhysicsRep is false as mPhysicsRep will be NULL if it is
+      if (mPhysicsRep)
+         mPhysicsRep->moveKinematicTo(getTransform());
    }
 }
 

+ 7 - 0
Engine/source/T3D/vehicles/vehicle.h

@@ -127,6 +127,8 @@ struct VehicleData: public ShapeBaseData
    F32 splashFreqMod;
    F32 splashVelEpsilon;
 
+   bool enablePhysicsRep;
+
    //
    VehicleData();
    bool preload(bool server, String &errorStr);
@@ -142,6 +144,7 @@ struct VehicleData: public ShapeBaseData
 
 
 //----------------------------------------------------------------------------
+class PhysicsBody;
 
 class Vehicle: public ShapeBase
 {
@@ -177,6 +180,8 @@ class Vehicle: public ShapeBase
       Point3F cameraRotVec;
    };
 
+   PhysicsBody *mPhysicsRep;
+
    StateDelta mDelta;
    S32 mPredictionCount;            ///< Number of ticks to predict
    VehicleData* mDataBlock;
@@ -262,6 +267,8 @@ public:
    bool onAdd();
    void onRemove();
 
+   void _createPhysics();
+
    /// Interpolates between move ticks @see processTick
    /// @param   dt   Change in time between the last call and this call to the function
    void interpolateTick(F32 dt);

+ 1 - 1
Engine/source/assets/assetManager.cpp

@@ -76,7 +76,7 @@ AssetManager::AssetManager() :
     mMaxLoadedPrivateAssetsCount( 0 ),
     mAcquiredReferenceCount( 0 ),
     mEchoInfo( false ),
-    mIgnoreAutoUnload( false )
+    mIgnoreAutoUnload( true )
 {
 }
 

+ 0 - 82
Engine/source/component/componentInterface.cpp

@@ -1,82 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#include "component/simComponent.h"
-#include "component/componentInterface.h"
-#include "core/strings/findMatch.h"
-#include "core/stringTable.h"
-
-bool ComponentInterfaceCache::add( const char *type, const char *name, const SimComponent *owner, ComponentInterface *cinterface )
-{
-   if( ( mInterfaceList.size() == 0 ) || ( enumerate( NULL, type, name, owner ) == 0 ) )
-   {
-      mInterfaceList.increment();
-      // CodeReview [tom, 3/9/2007] Seems silly to keep calling last(), why not cache the var? Yes, I know I am pedantic.
-      mInterfaceList.last().type = ( type == NULL ? NULL : StringTable->insert( type ) );
-      mInterfaceList.last().name = ( name == NULL ? NULL : StringTable->insert( name ) );
-      mInterfaceList.last().owner = owner;
-      mInterfaceList.last().iface = cinterface;
-
-      return true;
-   }
-
-   return false;
-}
-
-//------------------------------------------------------------------------------
-
-void ComponentInterfaceCache::clear()
-{
-   mInterfaceList.clear();
-}
-
-//------------------------------------------------------------------------------
-
-U32 ComponentInterfaceCache::enumerate( ComponentInterfaceList *list, const char *type /* = NULL */, 
-                                       const char *name /* = NULL  */, const SimComponent *owner /* = NULL */, bool notOwner /* = false */ ) const
-{
-   U32 numMatches = 0;
-
-   for( _InterfaceEntryItr i = mInterfaceList.begin(); i != mInterfaceList.end(); i++ )
-   {
-      // Early out if limiting results by component owner
-      if( owner != NULL && (
-         ( (*i).owner == owner && notOwner ) ||
-         ( (*i).owner != owner && !notOwner ) ) )
-         continue;
-
-      // Match the type, short circuit if type == NULL
-      if( type == NULL || FindMatch::isMatch( type, (*i).type ) )
-      {
-         // Match the name
-         if( name == NULL || FindMatch::isMatch( name, (*i).name ) )
-         {
-            numMatches++;
-
-            if( list != NULL )
-               list->push_back( (*i).iface );
-         }
-      }
-   }
-
-   return numMatches;
-}

+ 0 - 234
Engine/source/component/componentInterface.h

@@ -1,234 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifndef _COMPONENTINTERFACE_H_
-#define _COMPONENTINTERFACE_H_
-
-#ifndef _TVECTOR_H_
-#include "core/util/tVector.h"
-#endif
-
-#ifndef _SIMOBJECT_H_
-#include "console/simObject.h"
-#endif
-
-#include "core/util/safeDelete.h"
-
-
-class SimComponent;
-
-
-// CodeReview [patw, 2, 13, 2007] The issue I have not addressed in this class is
-// interface locking. I think that we want to do this, for sure, but I also want
-// to keep it as light-weight as possible. For the most part, there should only
-// ever be one thing doing something with a component at one time, but I can see
-// many situations where this wouldn't be the case. When we decide to address
-// the issues of locking, I believe it should be done here, at the ComponentInterface
-// level. I would like lock functionality to be as centralized as possible, and 
-// so this is the place for it. The functionality is critical for safe useage of
-// the ComponentProperty class, so implementation here would also be ideal.
-
-// CodeReview [patw, 2, 14, 2007] This really should be a ref-counted object
-class ComponentInterface
-{
-   friend class SimComponent;
-private:
-   SimObjectPtr<SimComponent> mOwner; ///< SimComponent will directly modify this value
-
-public:
-   /// Default constructor
-   ComponentInterface() : mOwner(NULL) {};
-
-   /// Destructor
-   virtual ~ComponentInterface() 
-   { 
-      mOwner = NULL;
-   }
-
-   /// This will return true if the interface is valid
-   virtual bool isValid() const 
-   { 
-      return mOwner != NULL; 
-   }
-
-   /// Get the owner of this interface
-   SimComponent *getOwner() { return mOwner; }
-   const SimComponent *getOwner() const { return mOwner; }
-};
-
-typedef VectorPtr<ComponentInterface *> ComponentInterfaceList;
-typedef VectorPtr<ComponentInterface *>::iterator ComponentInterfaceListIterator;
-
-// These two asserts I found myself wanting a lot when doing interface methods
-#ifdef TORQUE_ENABLE_ASSERTS
-#  define VALID_INTERFACE_ASSERT(OwningClassType) \
-   AssertFatal( isValid(), "Interface validity check failed." ); \
-   AssertFatal( dynamic_cast<const OwningClassType *>( getOwner() ) != NULL, avar( "Owner is not an instance of %s", #OwningClassType ) )
-#else
-#  define VALID_INTERFACE_ASSERT(OwningClassType)
-#endif
-
-/// This class is designed to wrap an existing class or type easily to allow
-/// a SimComponent to expose a property with custom processing code in an efficient
-/// and safe way. Specialized templates could be written which include validation
-/// on sets, and processing on gets. 
-///
-/// This class has a lot of "blow your leg off" potential, if you have bad aim.
-/// I think that a lot of very intuitive functionality can be gained from using
-/// this properly, however when implementing a specialized template, be mindful
-/// of what you are doing, and 
-
-// CodeReview [patw, 2, 13, 2007] I am very interested in making this as thin as
-// possible. I really like the possibilities that it exposes as far as exposing
-// "properties" to other components. I want it to be performant, however, so
-// if anyone has notes on this, mark up the source, e-mail me, whatever.
-template<class T>
-class ComponentProperty : public ComponentInterface
-{
-   typedef ComponentInterface Parent;
-
-protected:
-   T *mValuePtr;
-
-   // ComponentInterface Overrides
-public:
-
-   // Override this to add a check for valid memory.
-   virtual bool isValid() const 
-   {
-      return ( mValuePtr != NULL ) && Parent::isValid(); 
-   }
-
-   // Operator overloads
-public:
-   /// Dereferencing a value interface will allow get to do any processing and
-   /// return the reference to that 
-   const T &operator*()
-   {
-      return get();
-   }
-
-   /// Assignment operator will invoke set.
-   const T &operator=( const T &lval )
-   {
-      return set( lval );
-   }
-
-   // Constructors/Destructors, specialize these if needed
-public:
-   /// Default Constructor.
-   ComponentProperty() : mValuePtr( NULL )
-   {
-      mValuePtr = new T;
-   }
-
-   /// Copy constructor
-   ComponentProperty( const T &copy )
-   {
-      ComponentProperty();
-
-      // CodeReview [patw, 2, 13, 2007] So, the reasoning here is that I want to
-      // use the functionality that a specialized template implements in the set
-      // method. See the notes on the set method implementation.
-      set( copy );
-   }
-
-   /// Destructor, destroy memory
-   virtual ~ComponentProperty()
-   {
-      SAFE_DELETE( mValuePtr );
-   }
-
-   // This is the ComponentProperty interface that specializations of the class 
-   // will be interested in.
-public:
-
-   /// Get the value associated with this interface. Processing code can be done
-   /// here for specialized implementations. 
-   virtual const T &get() // 'const' is intentionally not used as a modifier here 
-   { 
-      return *mValuePtr; 
-   }
-
-   /// Set the value associated with this interface. Validation/processing code
-   /// can be done here. The copy-constructor uses the set method to do it's copy
-   /// so be mindful of that, or specialize the copy-constructor.
-   virtual const T &set( const T &t )
-   { 
-      // CodeReview [patw, 2, 13, 2007] So I am using the = operator here. Do you
-      // guys think that this should be the default behavior? I am trying to keep
-      // everything as object friendly as possible, so I figured I'd use this.
-      *mValuePtr = t; 
-      return *mValuePtr; 
-   }
-};
-
-/// This class is just designed to isolate the functionality of querying for, and
-/// managing interfaces. 
-class ComponentInterfaceCache
-{
-   // CodeReview [patw, 2, 14, 2007] When we move this whole system to Juggernaught
-   // we may want to consider making safe pointers for ComponentInterfaces. Not 
-   // sure why I put this note here.
-private:
-   struct _InterfaceEntry
-   {
-      ComponentInterface *iface;
-      StringTableEntry type;
-      StringTableEntry name;
-      const SimComponent *owner;
-   };
-
-   Vector<_InterfaceEntry> mInterfaceList;
-   typedef Vector<_InterfaceEntry>::const_iterator _InterfaceEntryItr;
-
-public:
-   /// Add an interface to the cache. This function will return true if the interface
-   /// is added successfully. An interface will not be added successfully if an entry
-   /// in the list with the same values for 'type' and 'name' is present in the list.
-   /// 
-   /// @param type Type of the interface being added. If NULL is passed, it will match any type string queried.
-   /// @param name Name of interface being added. If NULL is passed, it will match any name string queried.
-   /// @param owner The owner of the ComponentInterface being cached
-   /// @param cinterface The ComponentInterface being cached
-   virtual bool add( const char *type, const char *name, const SimComponent *owner, ComponentInterface *cinterface );
-
-   /// Clear the interface cache. This does not perform any operations on the contents
-   /// of the list. 
-   virtual void clear();
-
-   /// Query the list for all of the interfaces it stores references to that match
-   /// the 'type' and 'name' parameters. The results of the query will be appended 
-   /// to the list specified. Pattern matching is done using core/findMatch.h; for
-   /// more information on matching, see that code/header pair. Passing NULL for
-   /// one of these fields will match all values for that field. The return value
-   /// for the method will be the number of interfaces which match the query.
-   ///
-   /// @param list The list that this method will append search results on to. It is possible to pass NULL here and just receive the return value.
-   /// @param type An expression which the 'type' field on an added object must match to be included in results
-   /// @param name An expression which the 'name' field on an added object must match to be included in results
-   /// @param owner Limit results to components owned/not-owned by this SimComponent (see next param)
-   /// @param notOwner If set to true, this will enumerate only interfaces NOT owned by 'owner'
-   virtual U32 enumerate( ComponentInterfaceList *list, const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ) const;
-};
-
-#endif

+ 0 - 209
Engine/source/component/dynamicConsoleMethodComponent.cpp

@@ -1,209 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#include "component/dynamicConsoleMethodComponent.h"
-#include "console/stringStack.h"
-
-extern StringStack STR;
-extern ConsoleValueStack CSTK;
-
-IMPLEMENT_CO_NETOBJECT_V1(DynamicConsoleMethodComponent);
-
-ConsoleDocClass( DynamicConsoleMethodComponent,
-				"@brief Console object used for calling methods defined in script, from within other classes.\n\n"
-				"Not intended for game development, for editors or internal use only.\n\n "
-				"@internal");
-
-//-----------------------------------------------------------
-// Function name:  SimComponent::handlesConsoleMethod
-// Summary:
-//-----------------------------------------------------------
-bool DynamicConsoleMethodComponent::handlesConsoleMethod( const char *fname, S32 *routingId )
-{
-   // CodeReview: Host object is now given priority over components for method
-   // redirection. [6/23/2007 Pat]
-
-   // On this object?
-   if( isMethod( fname ) )
-   {
-      *routingId = -1; // -1 denotes method on object
-#ifdef TORQUE_DEBUG
-      // Inject Method.
-      injectMethodCall( fname );
-#endif
-      return true;
-   }
-
-   // on this objects components?
-   S32 nI = 0;
-   VectorPtr<SimComponent*> &componentList = lockComponentList();
-   for( SimComponentIterator nItr = componentList.begin(); nItr != componentList.end(); nItr++, nI++ )
-   {
-      SimObject *pComponent = dynamic_cast<SimObject*>(*nItr);
-      if( pComponent != NULL && pComponent->isMethod( fname ) )
-      {
-         *routingId = -2; // -2 denotes method on component
-         unlockComponentList();
-
-#ifdef TORQUE_DEBUG
-         // Inject Method.
-         injectMethodCall( fname );
-#endif
-         return true;
-      }
-   }
-   unlockComponentList();
-
-   return false;
-}
-
-const char *DynamicConsoleMethodComponent::callMethod( S32 argc, const char* methodName, ... )
-{
-   const char *argv[128];
-   methodName = StringTable->insert( methodName );
-
-   argc++;
-
-   va_list args;
-   va_start(args, methodName);
-   for(S32 i = 0; i < argc; i++)
-      argv[i+2] = va_arg(args, const char *);
-   va_end(args);
-
-   // FIXME: the following seems a little excessive. I wonder why it's needed?
-   argv[0] = methodName;
-   argv[1] = methodName;
-   argv[2] = methodName;
-
-   StringStackConsoleWrapper argsw(argc, argv);
-
-   return callMethodArgList( argsw.count() , argsw );
-}
-
-#ifdef TORQUE_DEBUG
-/// Inject Method Call.
-void DynamicConsoleMethodComponent::injectMethodCall( const char* method )
-{
-   // Get Call Method.
-   StringTableEntry callMethod = StringTable->insert( method );
-
-   // Find Call Method Metric.
-   callMethodMetricType::Iterator itr = mCallMethodMetrics.find( callMethod );
-
-   // Did we find the method?
-   if ( itr == mCallMethodMetrics.end() )
-   {
-      // No, so set the call count to initially be 1.
-      itr = mCallMethodMetrics.insert( callMethod, 1 );
-   }
-   else
-   {
-      // Increment Call Count.
-      itr->value++;
-   }
-}
-#endif
-
-const char* DynamicConsoleMethodComponent::callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis /* = true  */ )
-{
-#ifdef TORQUE_DEBUG
-   injectMethodCall( argv[0] );
-#endif
-
-   return _callMethod( argc, argv, callThis );
-}
-
-// Call all components that implement methodName giving them a chance to operate
-// Components are called in reverse order of addition
-const char *DynamicConsoleMethodComponent::_callMethod( U32 argc, ConsoleValueRef argv[], bool callThis /* = true  */ )
-{
-   // Set Owner
-   SimObject *pThis = dynamic_cast<SimObject *>( this );
-   AssertFatal( pThis, "DynamicConsoleMethodComponent::callMethod : this should always exist!" );
-
-   const char *cbName = StringTable->insert(argv[0]);
-
-   if( getComponentCount() > 0 )
-   {
-      lockComponentList();
-      for( S32 i = getComponentCount() - 1; i >= 0; i-- )
-      //for( SimComponentIterator nItr = componentList.end();  nItr != componentList.begin(); nItr-- )
-      {
-         argv[0] = cbName;
-
-         SimComponent *pComponent = dynamic_cast<SimComponent *>( getComponent( i ) );
-         AssertFatal( pComponent, "DynamicConsoleMethodComponent::callMethod - NULL component in list!" );
-
-         DynamicConsoleMethodComponent *pThisComponent = dynamic_cast<DynamicConsoleMethodComponent*>( pComponent );
-         AssertFatal( pThisComponent, "DynamicConsoleMethodComponent::callMethod - Non DynamicConsoleMethodComponent component attempting to callback!");
-
-         // Prevent stack corruption
-         STR.pushFrame();
-         CSTK.pushFrame();
-         // --
-
-         // Only call on first depth components
-         // Should isMethod check these calls?  [11/22/2006 justind]
-         if(pComponent->isEnabled())
-            Con::execute( pThisComponent, argc, argv );
-
-         // Prevent stack corruption
-         STR.popFrame();
-         CSTK.popFrame();
-         // --
-
-         // Bail if this was the first element
-         //if( nItr == componentList.begin() )
-         //   break;
-      }
-      unlockComponentList();
-   }
-
-   // Prevent stack corruption
-   STR.pushFrame();
-   CSTK.pushFrame();
-   // --
-   
-   // Set Owner Field
-   const char* result = "";
-   if(callThis)
-      result = Con::execute( pThis, argc, argv, true ); // true - exec method onThisOnly, not on DCMCs
-
-   // Prevent stack corruption
-   STR.popFrame();
-   CSTK.popFrame();
-   // --
-   return result;
-}
-
-ConsoleMethod( DynamicConsoleMethodComponent, callMethod, void, 3, 64 , "(methodName, argi) Calls script defined method\n"
-			  "@param methodName The method's name as a string\n"
-			  "@param argi Any arguments to pass to the method\n"
-			  "@return No return value"
-			  "@note %obj.callMethod( %methodName, %arg1, %arg2, ... );\n")
-
-{
-   object->callMethodArgList( argc - 1, argv + 2 );
-}
-
-//////////////////////////////////////////////////////////////////////////
-

+ 0 - 89
Engine/source/component/dynamicConsoleMethodComponent.h

@@ -1,89 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifndef _DYNAMIC_CONSOLEMETHOD_COMPONENT_H_
-#define _DYNAMIC_CONSOLEMETHOD_COMPONENT_H_
-
-#ifndef _SIMCOMPONENT_H_
-#include "component/simComponent.h"
-#endif
-
-#ifndef _CONSOLEINTERNAL_H_
-#include "console/consoleInternal.h"
-#endif
-
-#ifndef _ICALLMETHOD_H_
-#include "console/ICallMethod.h"
-#endif
-
-#ifdef TORQUE_DEBUG
-#ifndef _TDICTIONARY_H_
-#include "core/util/tDictionary.h"
-#endif
-#endif
-
-//-----------------------------------------------------------------------------
-
-class DynamicConsoleMethodComponent : public SimComponent, public ICallMethod
-{
-#ifdef TORQUE_DEBUG
-public:
-   typedef Map<StringTableEntry, S32> callMethodMetricType;
-#endif
-
-private:
-   typedef SimComponent Parent;
-
-#ifdef TORQUE_DEBUG
-   // Call Method Debug Stat.
-   callMethodMetricType mCallMethodMetrics;
-#endif
-
-protected:
-   /// Internal callMethod : Actually does component notification and script method execution
-   ///  @attention This method does some magic to the argc argv to make Con::execute act properly
-   ///   as such it's internal and should not be exposed or used except by this class
-   virtual const char* _callMethod( U32 argc, ConsoleValueRef argv[], bool callThis = true );
-
-public:
-
-#ifdef TORQUE_DEBUG
-   /// Call Method Metrics.
-   const callMethodMetricType& getCallMethodMetrics( void ) const { return mCallMethodMetrics; };
-
-   /// Inject Method Call.
-   void injectMethodCall( const char* method );
-#endif
-
-   /// Call Method
-   virtual const char* callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis = true );
-
-   /// Call Method format string
-   const char* callMethod( S32 argc, const char* methodName, ... );
-
-   // query for console method data
-   virtual bool handlesConsoleMethod(const char * fname, S32 * routingId);
-
-   DECLARE_CONOBJECT(DynamicConsoleMethodComponent);
-};
-
-#endif

+ 0 - 452
Engine/source/component/simComponent.cpp

@@ -1,452 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#include "platform/platform.h"
-#include "console/simObject.h"
-#include "console/consoleTypes.h"
-#include "component/simComponent.h"
-#include "core/stream/stream.h"
-#include "console/engineAPI.h"
-
-SimComponent::SimComponent() : mOwner( NULL )
-{
-   mComponentList.clear();
-   mMutex = Mutex::createMutex();
-   
-   mEnabled = true;
-   mTemplate = false;
-}
-
-SimComponent::~SimComponent()
-{
-   Mutex::destroyMutex( mMutex );
-   mMutex = NULL;
-}
-
-IMPLEMENT_CO_NETOBJECT_V1(SimComponent);
-
-ConsoleDocClass( SimComponent,
-				"@brief Legacy component system, soon to be deprecated.\n\n"
-				"Not intended for game development, for editors or internal use only.\n\n "
-				"@internal");
-
-bool SimComponent::onAdd()
-{
-   if( !Parent::onAdd() )
-      return false;
-
-   // Register
-   _registerInterfaces( this );
-
-   if( !_registerComponents( this ) )
-      return false;
-
-   //Con::executef( this, 1, "onAdd" );
-
-   return true;
-}
-
-void SimComponent::_registerInterfaces( SimComponent *owner )
-{
-   // First call this to expose the interfaces that this component will cache
-   // before examining the list of subcomponents
-   registerInterfaces( owner );
-
-   // Early out to avoid mutex lock and such
-   if( !hasComponents() )
-      return;
-
-   VectorPtr<SimComponent *> &components = lockComponentList();
-   for( SimComponentIterator i = components.begin(); i != components.end(); i++ )
-   {
-      (*i)->mOwner = owner;
-
-      // Tell the component itself to register it's interfaces
-      (*i)->registerInterfaces( owner );
-
-      (*i)->mOwner = NULL; // This tests to see if the object's onComponentRegister call will call up to the parent.
-
-      // Recurse
-      (*i)->_registerInterfaces( owner );
-   }
-
-   unlockComponentList();
-}
-
-bool SimComponent::_registerComponents( SimComponent *owner )
-{
-   // This method will return true if the object contains no components. See the
-   // documentation for SimComponent::onComponentRegister for more information
-   // on this behavior.
-   bool ret =  true;
-
-   // If this doesn't contain components, don't even lock the list.
-   if( hasComponents() )
-   {
-      VectorPtr<SimComponent *> &components = lockComponentList();
-      for( SimComponentIterator i = components.begin(); i != components.end(); i++ )
-      {
-         if( !(*i)->onComponentRegister( owner ) )
-         {
-            ret = false;
-            break;
-         }
-
-         AssertFatal( (*i)->mOwner == owner, "Component failed to call parent onComponentRegister!" );
-
-         // Recurse
-         if( !(*i)->_registerComponents( owner ) )
-         {
-            ret = false;
-            break;
-         }
-      }
-
-      unlockComponentList();
-   }
-
-   return ret;
-}
-
-void SimComponent::_unregisterComponents()
-{
-   if( !hasComponents() )
-      return;
-
-   VectorPtr<SimComponent *> &components = lockComponentList();
-   for( SimComponentIterator i = components.begin(); i != components.end(); i++ )
-   {
-      (*i)->onComponentUnRegister();
-
-      AssertFatal( (*i)->mOwner == NULL, "Component failed to call parent onUnRegister" );
-
-      // Recurse
-      (*i)->_unregisterComponents();
-   }
-
-   unlockComponentList();
-}
-
-void SimComponent::onRemove()
-{
-   //Con::executef(this, 1, "onRemove");
-
-   _unregisterComponents();
-
-   // Delete all components
-   VectorPtr<SimComponent *>&componentList = lockComponentList();
-   while(componentList.size() > 0)
-   {
-      SimComponent *c = componentList[0];
-      componentList.erase( componentList.begin() );
-
-      if( c->isProperlyAdded() )
-         c->deleteObject();
-      else if( !c->isRemoved() && !c->isDeleted() )
-         delete c;
-      // else, something else is deleting this, don't mess with it
-   }
-   unlockComponentList();
-
-   Parent::onRemove();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-bool SimComponent::processArguments(S32 argc, ConsoleValueRef *argv)
-{
-   for(S32 i = 0; i < argc; i++)
-   {
-      SimComponent *obj = dynamic_cast<SimComponent*> (Sim::findObject(argv[i]) );
-      if(obj)
-         addComponent(obj);
-      else
-         Con::printf("SimComponent::processArguments - Invalid Component Object \"%s\"", (const char*)argv[i]);
-   }
-   return true;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void SimComponent::initPersistFields()
-{
-    addGroup("Component");
-
-        addProtectedField( "Template", TypeBool, Offset(mTemplate, SimComponent), 
-           &setIsTemplate, &defaultProtectedGetFn, 
-           "Places the object in a component set for later use in new levels." );
-
-    endGroup("Component");
-
-    // Call Parent.
-    Parent::initPersistFields();
-} 
-
-//------------------------------------------------------------------------------
-
-bool SimComponent::getInterfaces( ComponentInterfaceList *list, const char *type /* = NULL */, const char *name /* = NULL  */,
-                                 const SimComponent *owner /* = NULL */, bool notOwner /* = false */ )
-{
-   AssertFatal( list != NULL, "Passing NULL for a list is not supported functionality for SimComponents." );
-   return ( mInterfaceCache.enumerate( list, type, name, owner, notOwner ) > 0 );
-}
-
-bool SimComponent::registerCachedInterface( const char *type, const char *name, SimComponent *interfaceOwner, ComponentInterface *cinterface )
-{
-   if( mInterfaceCache.add( type, name, interfaceOwner, cinterface ) )
-   {
-      cinterface->mOwner = interfaceOwner;
-
-      // Recurse
-      if( mOwner != NULL )
-         return mOwner->registerCachedInterface( type, name, interfaceOwner, cinterface );
-
-      return true;
-   }
-
-   // So this is not a good assert, because it will get triggered due to the recursive
-   // nature of interface caching. I want to keep it here, though, just so nobody
-   // else thinks, "Oh I'll add an assert here."
-   //
-   //AssertFatal( false, avar( "registerCachedInterface failed, probably because interface with type '%s', name '%s' and owner with SimObjectId '%d' already exists",
-   //   type, name, interfaceOwner->getId() ) );
-
-   return false;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Component Management
-//////////////////////////////////////////////////////////////////////////
-
-bool SimComponent::addComponentFromField( void* obj, const char* data )
-{
-   SimComponent *pComponent = dynamic_cast<SimComponent*>( Sim::findObject( data ) );
-   if( pComponent != NULL )
-      static_cast<SimComponent*>(obj)->addComponent( pComponent ); 
-   return false;
-}
-
-// Add Component to this one
-bool SimComponent::addComponent( SimComponent *component )
-{
-   AssertFatal( dynamic_cast<SimObject*>(component), "SimComponent - Cannot add non SimObject derived components!" );
-
-   MutexHandle mh;
-   if( mh.lock( mMutex, true ) )
-   {
-      for( SimComponentIterator nItr = mComponentList.begin(); nItr != mComponentList.end(); nItr++ )
-      {
-         SimComponent *pComponent = dynamic_cast<SimComponent*>(*nItr);
-         AssertFatal( pComponent, "SimComponent::addComponent - NULL component in list!" );
-         if( pComponent == component )
-            return true;
-      }
-
-      if(component->onComponentAdd(this))
-      {
-         component->mOwner = this;
-         mComponentList.push_back( component );
-         return true;
-      }
-   }
-
-   return false;
-}
-
-// Remove Component from this one
-bool SimComponent::removeComponent( SimComponent *component )
-{
-   MutexHandle mh;
-   if( mh.lock( mMutex, true ) )
-   {
-      for( SimComponentIterator nItr = mComponentList.begin(); nItr != mComponentList.end(); nItr++ )
-      {
-         SimComponent *pComponent = dynamic_cast<SimComponent*>(*nItr);
-         AssertFatal( pComponent, "SimComponent::removeComponent - NULL component in list!" );
-         if( pComponent == component )
-         {
-            AssertFatal( component->mOwner == this, "Somehow we contain a component who doesn't think we are it's owner." );
-            (*nItr)->onComponentRemove(this);
-            component->mOwner = NULL;
-            mComponentList.erase( nItr );
-            return true;
-         }
-      }
-   }
-   return false;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-bool SimComponent::onComponentAdd(SimComponent *target)
-{
-   Con::executef(this, "onComponentAdd", Con::getIntArg(target->getId()));
-   return true;
-}
-
-void SimComponent::onComponentRemove(SimComponent *target)
-{
-   Con::executef(this, "onComponentRemove", Con::getIntArg(target->getId()));
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-ComponentInterface *SimComponent::getInterface(const char *type /* = NULL */, const char *name /* = NULL */, 
-                                               const SimComponent *owner /* = NULL */, bool notOwner /* = false  */)
-{
-   ComponentInterfaceList iLst;
-
-   if( getInterfaces( &iLst, type, name, owner, notOwner ) )
-      return iLst[0];
-
-   return NULL;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-bool SimComponent::writeField(StringTableEntry fieldname, const char* value)
-{
-   if (!Parent::writeField(fieldname, value))
-      return false;
-
-   if( fieldname == StringTable->insert("owner") )
-      return false;
-
-   return true;
-}
-
-void SimComponent::write(Stream &stream, U32 tabStop, U32 flags /* = 0 */)
-{
-   MutexHandle handle;
-   handle.lock(mMutex); // When this goes out of scope, it will unlock it
-
-   // export selected only?
-   if((flags & SelectedOnly) && !isSelected())
-   {
-      for(U32 i = 0; i < mComponentList.size(); i++)
-         mComponentList[i]->write(stream, tabStop, flags);
-
-      return;
-   }
-
-   stream.writeTabs(tabStop);
-   char buffer[1024];
-   dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() ? getName() : "");
-   stream.write(dStrlen(buffer), buffer);
-   writeFields(stream, tabStop + 1);
-
-   if(mComponentList.size())
-   {
-      stream.write(2, "\r\n");
-
-      stream.writeTabs(tabStop+1);
-      stream.writeLine((U8 *)"// Note: This is a list of behaviors, not arbitrary SimObjects as in a SimGroup or SimSet.\r\n");
-
-      for(U32 i = 0; i < mComponentList.size(); i++)
-         mComponentList[i]->write(stream, tabStop + 1, flags);
-   }
-
-   stream.writeTabs(tabStop);
-   stream.write(4, "};\r\n");
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Console Methods
-//////////////////////////////////////////////////////////////////////////
-
-ConsoleMethod( SimComponent, addComponents, bool, 3, 64, "%obj.addComponents( %compObjName, %compObjName2, ... );\n"
-			  "Adds additional components to current list.\n"
-			  "@param Up to 62 component names\n"
-			  "@return Returns true on success, false otherwise.")
-{
-   for(S32 i = 2; i < argc; i++)
-   {
-      SimComponent *obj = dynamic_cast<SimComponent*> (Sim::findObject(argv[i]) );
-      if(obj)
-         object->addComponent(obj);
-      else
-         Con::printf("SimComponent::addComponents - Invalid Component Object \"%s\"", (const char*)argv[i]);
-   }
-   return true;
-}
-
-ConsoleMethod( SimComponent, removeComponents, bool, 3, 64, "%obj.removeComponents( %compObjName, %compObjName2, ... );\n"
-			  "Removes components by name from current list.\n"
-			  "@param objNamex Up to 62 component names\n"
-			  "@return Returns true on success, false otherwise.")
-{
-   for(S32 i = 2; i < argc; i++)
-   {
-      SimComponent *obj = dynamic_cast<SimComponent*> (Sim::findObject(argv[i]) );
-      if(obj)
-         object->removeComponent(obj);
-      else
-         Con::printf("SimComponent::removeComponents - Invalid Component Object \"%s\"", (const char*)argv[i]);
-   }
-   return true;
-}
-
-DefineConsoleMethod( SimComponent, getComponentCount, S32, (), , "() Get the current component count\n"
-			  "@return The number of components in the list as an integer")
-{
-   return object->getComponentCount();
-}
-
-DefineConsoleMethod( SimComponent, getComponent, S32, (S32 idx), , "(idx) Get the component corresponding to the given index.\n"
-			  "@param idx An integer index value corresponding to the desired component.\n"
-			  "@return The id of the component at the given index as an integer")
-{
-   if(idx < 0 || idx >= object->getComponentCount())
-   {
-      Con::errorf("SimComponent::getComponent - Invalid index %d", idx);
-      return 0;
-   }
-
-   SimComponent *c = object->getComponent(idx);
-   return c ? c->getId() : 0;
-}
-
-DefineConsoleMethod(SimComponent, setEnabled, void, (bool enabled), , "(enabled) Sets or unsets the enabled flag\n"
-			  "@param enabled Boolean value\n"
-			  "@return No return value")
-{
-   object->setEnabled(enabled);
-}
-
-DefineConsoleMethod(SimComponent, isEnabled, bool, (), , "() Check whether SimComponent is currently enabled\n"
-			  "@return true if enabled and false if not")
-{
-   return object->isEnabled();
-}
-
-DefineConsoleMethod(SimComponent, setIsTemplate, void, (bool templateFlag), , "(template) Sets or unsets the template flag\n"
-			  "@param template Boolean value\n"
-			  "@return No return value")
-{
-   object->setIsTemplate(templateFlag);
-}
-
-DefineConsoleMethod(SimComponent, getIsTemplate, bool, (), , "() Check whether SimComponent is currently a template\n"
-			  "@return true if is a template and false if not")
-{
-   return object->getIsTemplate();
-}

+ 0 - 256
Engine/source/component/simComponent.h

@@ -1,256 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifndef _SIMCOMPONENT_H_
-#define _SIMCOMPONENT_H_
-
-#ifndef _TVECTOR_H_
-#include "core/util/tVector.h"
-#endif
-#ifndef _STRINGTABLE_H_
-#include "core/stringTable.h"
-#endif
-#ifndef _NETOBJECT_H_
-#include "sim/netObject.h"
-#endif
-#ifndef _COMPONENTINTERFACE_H_
-#include "component/componentInterface.h"
-#endif
-#ifndef _PLATFORM_THREADS_MUTEX_H_
-#include "platform/threads/mutex.h"
-#endif
-#ifndef _STRINGFUNCTIONS_H_
-#include "core/strings/stringFunctions.h"
-#endif
-
-// Forward refs
-class Stream;
-class ComponentInterface;
-class ComponentInterfaceCache;
-
-class SimComponent : public NetObject
-{
-   typedef NetObject Parent;
-
-private:
-   VectorPtr<SimComponent *> mComponentList; ///< The Component List
-   void *mMutex;                             ///< Component List Mutex
-
-   SimObjectPtr<SimComponent> mOwner;        ///< The component which owns this one.
-
-   /// This is called internally to instruct the component to iterate over it's
-   // list of components and recursively call _registerInterfaces on their lists
-   // of components.
-   void _registerInterfaces( SimComponent *owner );
-
-   bool _registerComponents( SimComponent *owner );
-   void _unregisterComponents();
-
-protected:
-   ComponentInterfaceCache mInterfaceCache;  ///< Stores the interfaces exposed by this component. 
-   
-   bool mEnabled;
-   
-   bool mTemplate;
-
-   // Non-const getOwner for derived classes
-   SimComponent *_getOwner() { return mOwner; }
-
-   /// Returns a const reference to private mComponentList
-   typedef VectorPtr<SimComponent *>::iterator SimComponentIterator;
-   VectorPtr<SimComponent *> &lockComponentList()
-   {
-      Mutex::lockMutex( mMutex );
-      return mComponentList;
-   };
-
-   void unlockComponentList()
-   {
-      Mutex::unlockMutex( mMutex );
-   }
-
-   /// onComponentRegister is called on each component by it's owner. If a component
-   /// has no owner, onComponentRegister will not be called on it. The purpose
-   /// of onComponentRegister is to allow a component to check for any external
-   /// interfaces, or other dependencies which it needs to function. If any component
-   /// in a component hierarchy returns false from it's onComponentRegister call
-   /// the entire hierarchy is invalid, and SimObject::onAdd will fail on the
-   /// top-level component. To put it another way, if a component contains other
-   /// components, it will be registered successfully with Sim iff each subcomponent
-   /// returns true from onComponentRegister. If a component does not contain
-   /// other components, it will not receive an onComponentRegister call.
-   ///
-   /// Overloads of this method must pass the call along to their parent, as is
-   /// shown in the example below. 
-   ///
-   /// @code
-   /// bool FooComponent::onComponentRegister( SimComponent *owner )
-   /// {
-   ///    if( !Parent::onComponentRegister( owner ) )
-   ///       return false;
-   ///    ...
-   /// }
-   /// @endcode
-   virtual bool onComponentRegister( SimComponent *owner )
-   {
-      mOwner = owner;
-      return true;
-   }
-
-   /// onUnregister is called when the owner is unregistering. Your object should
-   /// do cleanup here, as well as pass a call up the chain to the parent.
-   virtual void onComponentUnRegister()
-   {
-      mOwner = NULL;
-   }
-
-   /// registerInterfaces is called on each component as it's owner is registering
-   /// it's interfaces. This is called before onComponentRegister, and should be used to
-   /// register all interfaces exposed by your component, as well as all callbacks
-   /// needed by your component.
-   virtual void registerInterfaces( SimComponent *owner )
-   {
-
-   }
-
-public:
-   DECLARE_CONOBJECT(SimComponent);
-
-   /// Constructor
-   /// Add this component
-   SimComponent();
-
-   /// Destructor
-   /// Remove this component and destroy child references
-   virtual ~SimComponent();
-
-public:
-
-   virtual bool onAdd();
-   virtual void onRemove();
-
-   static void initPersistFields();
-
-   virtual bool processArguments(S32 argc, ConsoleValueRef *argv);
-   
-   bool isEnabled() const { return mEnabled; }
-   
-   void setEnabled( bool value ) { mEnabled = value; }
-
-   /// Will return true if this object contains components.
-   bool hasComponents() const { return ( mComponentList.size() > 0 ); };
-
-   /// The component which owns this object
-   const SimComponent *getOwner() const { return mOwner; };
-
-   // Component Information
-   inline virtual StringTableEntry  getComponentName() { return StringTable->insert( getClassName() ); };
-
-   /// Protected 'Component' Field setter that will add a component to the list.
-   static bool addComponentFromField(void* obj, const char* data);
-
-   /// Add Component to this one
-   virtual bool addComponent( SimComponent *component );
-
-   /// Remove Component from this one
-   virtual bool removeComponent( SimComponent *component );
-
-   /// Clear Child components of this one
-   virtual bool clearComponents() { mComponentList.clear(); return true; };
-
-   virtual bool onComponentAdd(SimComponent *target);
-   virtual void onComponentRemove(SimComponent *target);
-
-   S32 getComponentCount()                   { return mComponentList.size(); }
-   SimComponent *getComponent(S32 idx)       { return mComponentList[idx]; }
-
-   SimComponentIterator find(SimComponentIterator first, SimComponentIterator last, SimComponent *value)
-   {
-      return ::find(first, last, value);
-   }
-
-   static bool setIsTemplate( void *object, const char *index, const char *data ) 
-      { static_cast<SimComponent*>(object)->setIsTemplate( dAtob( data ) ); return false; };
-   virtual void setIsTemplate( const bool pTemplate ) { mTemplate = pTemplate; }
-   bool getIsTemplate() const { return mTemplate; }
-
-   virtual void write(Stream &stream, U32 tabStop, U32 flags = 0);
-   virtual bool writeField(StringTableEntry fieldname, const char* value);
-
-
-   /// getInterfaces allows the caller to enumerate the interfaces exposed by
-   /// this component. This method can be overwritten to expose interfaces
-   /// which are not cached on the object, before passing the call to the Parent.
-   /// This can be used delay interface creation until it is queried for, instead
-   /// of creating it on initialization, and caching it. Returns false if no results
-   /// were found
-   ///
-   /// @param list The list that this method will append search results on to.
-   /// @param type An expression which the 'type' field on an added object must match to be included in results
-   /// @param name An expression which the 'name' field on an added object must match to be included in results
-   /// @param owner Limit results to components owned/not-owned by this SimComponent (see next param)
-   /// @param notOwner If set to true, this will enumerate only interfaces NOT owned by 'owner'
-   virtual bool getInterfaces( ComponentInterfaceList *list, const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ); // const omission intentional
-
-   
-   /// These two methods allow for easy query of component interfaces if you know
-   /// exactly what you are looking for, and don't mind being passed back the first
-   /// matching result.
-   ComponentInterface *getInterface( const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false );
-
-   template <class T>
-   T *getInterface( const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false );
-
-   /// Add an interface to the cache. This function will return true if the interface
-   /// is added successfully. An interface will not be added successfully if an entry
-   /// in this components cache with the same values for 'type' and 'name' is present.
-   /// 
-   /// @param type Type of the interface being added. If NULL is passed, it will match any type string queried.
-   /// @param name Name of interface being added. If NULL is passed, it will match any name string queried.
-   /// @param interfaceOwner The component which owns the interface being cached
-   /// @param cinterface The ComponentInterface being cached
-   bool registerCachedInterface( const char *type, const char *name, SimComponent *interfaceOwner, ComponentInterface *cinterface );
-};
-
-//////////////////////////////////////////////////////////////////////////
-
-template <class T>
-T *SimComponent::getInterface( const char *type /* = NULL */, const char *name /* = NULL */, 
-                              const SimComponent *owner /* = NULL */, bool notOwner /* = false  */ )
-{
-   ComponentInterfaceList iLst;
-
-   if( getInterfaces( &iLst, type, name, owner, notOwner ) )
-   {
-      ComponentInterfaceListIterator itr = iLst.begin();
-
-      while( dynamic_cast<T *>( *itr ) == NULL )
-         itr++;
-
-      if( itr != iLst.end() )
-         return static_cast<T *>( *itr );
-   }
-
-   return NULL;
-}
-
-#endif // _SIMCOMPONENT_H_

+ 0 - 159
Engine/source/component/simpleComponent.h

@@ -1,159 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifndef _SIMPLECOMPONENT_H_
-#define _SIMPLECOMPONENT_H_
-
-#ifndef _SIMCOMPONENT_H_
-#include "component/simComponent.h"
-#endif
-
-#ifndef _COMPONENTINTERFACE_H_
-#include "component/componentInterface.h"
-#endif
-
-/// This is a very simple interface. Interfaces provide ways for components to 
-/// interact with each-other, and query each-other for functionality. It makes it
-/// possible for two components to be interdependent on one another, as well. An
-/// interface should make accessor calls to it's owner for functionality, and 
-/// generally be as thin of a layer as possible. 
-class SimpleComponentInterface : public ComponentInterface
-{
-public:
-   bool isFortyTwo( const U32 test );
-};
-
-//////////////////////////////////////////////////////////////////////////
-/// The purpose of this component is to provide a minimalistic component that 
-/// exposes a simple, cached interface
-class SimpleComponent : public SimComponent
-{
-   typedef SimComponent Parent;
-
-protected:
-   SimpleComponentInterface mSCInterface;
-
-public:
-   // Components are still SimObjects, and need to be declared and implemented
-   // with the standard macros
-   DECLARE_CONOBJECT(SimpleComponent);
-
-   //////////////////////////////////////////////////////////////////////////
-   // SimComponent overloads. 
-
-   // This method is called on each component as it's parent is getting onAdd
-   // called. The purpose of overloading this method is to expose cached interfaces
-   // before onComponentRegister is called, so that other components can depend on the
-   // interfaces you expose in order to register properly. This functionality
-   // will be demonstrated in a more advanced example.
-   virtual void registerInterfaces( SimComponent *owner )
-   {
-      // While it is not imperative that we pass this call to the Parent in this
-      // example, if there existed a class-heirarchy of components, it would be
-      // critical, so for good practice, call up to the Parent.
-      Parent::registerInterfaces( owner );
-
-      // Now we should go ahead and register our cached interface. What we are doing
-      // is telling the component which contains this component (if it exists)
-      // all of the interfaces that we expose. When this call is made, it will 
-      // recurse up the owner list. 
-      //
-      // For example, there exists components A, B, and C. 
-      // A owns B, and B owns C.
-      // 
-      // If C exposes a cached interface, it will expose it via registerCachedInterface
-      // when registerInterfaces is recursively called. It will add it's interface to 
-      // it's cache list, and then pass the register call up to it's parent. The parent
-      // will also cache the interface, and continue to pass the cache call up the 
-      // child->parent chain until there exists no parent. 
-      //
-      // The result is that, if C exposes an interface 'foo', and A owns B, and 
-      // B owns C, an interface request for 'foo' given to component A will result
-      // in 'foo' being returned, even though A does not expose 'foo'. This makes
-      // it possible for a component to query it's owner for an interface, and
-      // not care where that interface is exposed. It also allows for game code
-      // to work with any SimComponent and query that component for any interface
-      // it wants without knowing or caring exactly where it is coming from.
-      //
-      // registerCachedInterface returns a boolean value if it was successful. 
-      // Success results in the caching of this interface throughout the full
-      // child->parent chain. An interface will be added to a cache list 
-      // successfully iff there exists no entry in that list that has matching
-      // values for 'type', 'name' and 'owner'. 
-      owner->registerCachedInterface( 
-         // The first parameter is the 'type' of the interface, this is not to be
-         // confused with any kind of existing console, or c++ type. It is simply
-         // a string which is can be set to any value
-         "example", 
-
-         // The next parameter is the 'name' of the interface. This is also a string
-         // which can be set to any value
-         "isfortytwo", 
-         
-         // The owner of the interface. Note that the value being assigned here
-         // is this instance of SimpleComponent, and not the 'owner' argument
-         // of the function registerInterfaces that we are calling from.
-         this, 
-         
-         // And finally the interface; a pointer to an object with type ComponentInterface
-         &mSCInterface );
-   }
-
-   //////////////////////////////////////////////////////////////////////////
-   // Specific functionality
-
-   /// This is the test method, it will return true if the number provided
-   /// is forty two
-   bool isFortyTwo( const U32 test ) const
-   {
-      return ( test == 42 );
-   }
-};
-
-//////////////////////////////////////////////////////////////////////////
-// Interface implementation
-//
-// Since interfaces themselves implement very little functionality, it is a good
-// idea to inline them if at all possible. Interdependent components will be using
-// these interfaces constantly, and so putting as thin of a layer between the 
-// functionality they expose, and the functionality the component implements is
-// a good design practice.
-inline bool SimpleComponentInterface::isFortyTwo( const U32 test )
-{
-   // This code block will test for a valid owner in a debug build before
-   // performing operations on it's owner. It is worth noting that the
-   // ComponentInterface::isValid() method can be overridden to include
-   // validation specific to your interface and/or component.
-   AssertFatal( isValid(), "SimpleComponentInterface has not been registered properly by the component which exposes it." );
-
-   // This is a sanity check. The owner of this interface should have the type
-   // SimpleComponent, otherwise this won't work. (See further interface examples
-   // for some ways around this)
-   AssertFatal( dynamic_cast<SimpleComponent *>( getOwner() ) != NULL, "Owner of SimpleComponentInterface is not a SimpleComponent" );
-
-   // Component interfaces rely on being registered to set their mOwner
-   // field. This field is initialized to NULL, and then gets set by
-   // SimComponent when the interface is registered.
-   return static_cast<const SimpleComponent *>( getOwner() )->isFortyTwo( test );
-}
-
-#endif

+ 0 - 68
Engine/source/component/test/moreAdvancedComponentTest.cpp

@@ -1,68 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2014 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifdef TORQUE_TESTS_ENABLED
-#include "testing/unitTesting.h"
-#include "component/moreAdvancedComponent.h"
-
-TEST(MoreAdvancedComponent, MoreAdvancedComponent)
-{
-   // Create component instances and compose them.
-   SimComponent *parentComponent = new SimComponent();
-   SimpleComponent *simpleComponent = new SimpleComponent();
-   MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent();
-   // CodeReview note that the interface pointer isn't initialized in a ctor
-   //  on the components, so it's bad memory against which you might
-   //  be checking in testDependentInterface [3/3/2007 justind]
-   parentComponent->addComponent( simpleComponent );
-   parentComponent->addComponent( moreAdvComponent );
-
-   simpleComponent->registerObject();
-   moreAdvComponent->registerObject();
-
-   // Put a break-point here, follow the onAdd call, and observe the order in
-   // which the SimComponent::onAdd function executes. You will see the interfaces
-   // get cached, and the dependent interface query being made.
-   parentComponent->registerObject();
-
-   // If the MoreAdvancedComponent found an interface, than the parentComponent
-   // should have returned true, from onAdd, and should therefore be registered
-   // properly with the Sim
-   EXPECT_TRUE( parentComponent->isProperlyAdded() )
-      << "Parent component not properly added!";
-
-   // Now lets test the interface. You can step through this, as well.
-   EXPECT_TRUE( moreAdvComponent->testDependentInterface() )
-      << "Dependent interface test failed.";
-
-   // CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind]
-   //
-   // Clean up
-   parentComponent->removeComponent( simpleComponent );
-   parentComponent->removeComponent( moreAdvComponent );
-
-   parentComponent->deleteObject();
-   moreAdvComponent->deleteObject();
-   simpleComponent->deleteObject();
-};
-
-#endif

+ 0 - 149
Engine/source/component/test/simComponentTest.cpp

@@ -1,149 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2014 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifdef TORQUE_TESTS_ENABLED
-#include "testing/unitTesting.h"
-#include "component/simComponent.h"
-
-class CachedInterfaceExampleComponent : public SimComponent
-{
-   typedef SimComponent Parent;
-
-   ComponentProperty<U32> mMyId;
-   static U32 smNumInstances;
-   ComponentProperty<U32> *mpU32; // CodeReview [patw, 2, 17, 2007] Make ref objects when this is in Jugg
-
-public:
-   DECLARE_CONOBJECT( CachedInterfaceExampleComponent );
-
-   CachedInterfaceExampleComponent() : mpU32( NULL )
-   {
-      mMyId = ( ( 1 << 24 ) | smNumInstances++ );
-   }
-   virtual ~CachedInterfaceExampleComponent()
-   {
-      smNumInstances--;
-   }
-
-public:
-   //////////////////////////////////////////////////////////////////////////
-
-   virtual void registerInterfaces( SimComponent *owner )
-   {
-      // Register a cached interface for this 
-      owner->registerCachedInterface( NULL, "aU32", this, &mMyId );
-   }
-
-   //////////////////////////////////////////////////////////////////////////
-
-   bool onComponentRegister( SimComponent *owner )
-   {
-      // Call up to the parent first
-      if( !Parent::onComponentRegister( owner ) )
-         return false;
-
-      // We want to get an interface from another object in our containing component
-      // to simulate component interdependency. 
-      ComponentInterfaceList list;
-
-      // Enumerate the interfaces on the owner, only ignore interfaces that this object owns
-      if( !owner->getInterfaces( &list, NULL, "aU32", this, true ) )
-         return false;
-
-      // Sanity check before just assigning all willy-nilly
-      for( ComponentInterfaceListIterator i = list.begin(); i != list.end(); i++ )
-      {
-         mpU32 = dynamic_cast<ComponentProperty<U32> *>( (*i) );
-
-         if( mpU32 != NULL )
-            return true;
-      }
-
-      return false;
-   }
-
-   //////////////////////////////////////////////////////////////////////////
-
-   // CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface
-   // for this functionality later
-   void unit_test()
-   {
-      EXPECT_TRUE( mpU32 != NULL )
-         << "Pointer to dependent interface is NULL";
-      if( mpU32 )
-      {
-         EXPECT_TRUE( *(*mpU32) & ( 1 << 24 ) )
-            << "Pointer to interface data is bogus.";
-         EXPECT_TRUE( *(*mpU32) != *mMyId )
-            << "Two of me have the same ID, bad!";
-      }
-   }
-};
-
-IMPLEMENT_CONOBJECT( CachedInterfaceExampleComponent );
-U32 CachedInterfaceExampleComponent::smNumInstances = 0;
-
-ConsoleDocClass( CachedInterfaceExampleComponent,
-				"@brief Legacy from older component system.\n\n"
-				"Not intended for game development, for editors or internal use only.\n\n "
-				"@internal");
-
-TEST(SimComponent, Composition)
-{
-   SimComponent *testComponent = new SimComponent();
-   CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent();
-   CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent();
-
-   // Register sub-components
-   EXPECT_TRUE( componentA->registerObject() )
-      << "Failed to register componentA";
-   EXPECT_TRUE( componentB->registerObject() )
-      << "Failed to register componentB";
-
-   // Add the components
-   EXPECT_TRUE( testComponent->addComponent( componentA ) )
-      << "Failed to add component a to testComponent";
-   EXPECT_TRUE( testComponent->addComponent( componentB ) )
-      << "Failed to add component b to testComponent";
-
-   EXPECT_EQ( componentA->getOwner(), testComponent )
-      << "testComponent did not properly set the mOwner field of componentA to NULL.";
-   EXPECT_EQ( componentB->getOwner(), testComponent )
-      << "testComponent did not properly set the mOwner field of componentB to NULL.";
-
-   // Register the object with the simulation, kicking off the interface registration
-   ASSERT_TRUE( testComponent->registerObject() )
-      << "Failed to register testComponent";
-
-   {
-      SCOPED_TRACE("componentA");
-      componentA->unit_test();
-   }
-   {
-      SCOPED_TRACE("componentB");
-      componentB->unit_test();
-   }
-
-   testComponent->deleteObject();
-};
-
-#endif

+ 0 - 131
Engine/source/component/test/simpleComponentTest.cpp

@@ -1,131 +0,0 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2014 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifdef TORQUE_TESTS_ENABLED
-#include "testing/unitTesting.h"
-#include "component/simpleComponent.h"
-
-TEST(SimpleComponent, SimpleComponent)
-{
-   // When instantiating, and working with a SimObject in C++ code, such as
-   // a unit test, you *may not* allocate a SimObject off of the stack. 
-   //
-   // For example:
-   //    SimpleComponent sc; 
-   // is a stack allocation. This memory is allocated off of the program stack
-   // when the function is called. SimObject deletion is done via SimObject::deleteObject()
-   // and the last command of this method is 'delete this;' That command will
-   // cause an assert if it is called on stack-allocated memory. Therefor, when
-   // instantiating SimObjects in C++ code, it is imperitive that you keep in
-   // mind that if any script calls 'delete()' on that SimObject, or any other
-   // C++ code calls 'deleteObject()' on that SimObject, it will crash.
-   SimpleComponent *sc = new SimpleComponent();
-
-   // SimObject::registerObject must be called on a SimObject before it is
-   // fully 'hooked in' to the engine. 
-   //
-   // Tracing execution of this function will let you see onAdd get called on
-   // the component, and you will see it cache the interface we exposed.
-   sc->registerObject();
-
-   // It is *not* required that a component always be owned by a component (obviously)
-   // however I am using an owner so that you can trace execution of recursive
-   // calls to cache interfaces and such.
-   SimComponent *testOwner = new SimComponent();
-
-   // Add the test component to it's owner. This will set the 'mOwner' field
-   // of 'sc' to the address of 'testOwner'
-   testOwner->addComponent( sc );
-
-   // If you step-into this registerObject the same way as the previous one, 
-   // you will be able to see the recursive caching of the exposed interface.
-   testOwner->registerObject();
-
-   // Now to prove that object composition is working properly, lets ask 
-   // both of these components for their interface lists...
-
-   // The ComponentInterfaceList is a typedef for type 'VectorPtr<ComponentInterface *>'
-   // and it will be used by getInterfaces() to store the results of the interface
-   // query. This is the "complete" way to obtain an interface, and it is too
-   // heavy-weight for most cases. A simplified query will be performed next, 
-   // to demonstrate the usage of both.
-   ComponentInterfaceList iLst;
-
-   // This query requests all interfaces, on all components, regardless of name
-   // or owner.
-   sc->getInterfaces( &iLst, 
-      // This is the type field. I am passing NULL here to signify that the query
-      // should match all values of 'type' in the list.
-      NULL,
-
-      // The name field, let's pass NULL again just so when you trace execution
-      // you can see how queries work in the simple case, first.
-      NULL );
-
-   // Lets process the list that we've gotten back, and find the interface that
-   // we want.
-   SimpleComponentInterface *scQueriedInterface = NULL;
-
-   for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ )
-   {
-      scQueriedInterface = dynamic_cast<SimpleComponentInterface *>( *i );
-
-      if( scQueriedInterface != NULL )
-         break;
-   }
-
-   AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
-
-   // Lets do it again, only we will execute the query on the parent instead,
-   // in a simplified way. Remember the parent component doesn't expose any 
-   // interfaces at all, so the success of this behavior is entirely dependent 
-   // on the recursive registration that occurs in registerInterfaces()
-   SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface<SimpleComponentInterface>();
-
-   AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" );
-
-   // We should now have two pointers to the same interface obtained by querying
-   // different components.
-   EXPECT_EQ( ownerQueriedInterface, scQueriedInterface )
-      << "This really shouldn't be possible to fail given the setup of the test";
-
-   // Lets call the method that was exposed on the component via the interface.
-   // Trace the execution of this function, if you wish.
-   EXPECT_TRUE( ownerQueriedInterface->isFortyTwo( 42 ) )
-      << "Don't panic, but it's a bad day in the component system.";
-   EXPECT_TRUE( scQueriedInterface->isFortyTwo( 42 ) )
-      << "Don't panic, but it's a bad day in the component system.";
-
-   // So there you have it. Writing a simple component that exposes a cached
-   // interface, and testing it. It's time to clean up.
-   testOwner->removeComponent( sc );
-
-   sc->deleteObject();
-   testOwner->deleteObject();
-
-   // Interfaces do not need to be freed. In Juggernaught, these will be ref-counted
-   // for more robust behavior. Right now, however, the values of our two interface
-   // pointers, scQueriedInterface and ownerQueriedInterface, reference invalid
-   // memory. 
-};
-
-#endif

+ 4 - 0
Engine/source/console/compiledEval.cpp

@@ -994,6 +994,7 @@ breakContinue:
                   // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields().
                   Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName());
                   delete currentNewObject;
+                  currentNewObject = NULL;
                   ip = failJump;
                   // Prevent stack value corruption
                   CSTK.popFrame();
@@ -1094,6 +1095,9 @@ breakContinue:
 
          case OP_FINISH_OBJECT:
          {
+            if (currentNewObject)
+               currentNewObject->onPostAdd();
+
             //Assert( objectCreationStackIndex >= 0 );
             // Restore the object info from the stack [7/9/2007 Black]
             currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject;

+ 2 - 0
Engine/source/console/consoleObject.h

@@ -473,6 +473,8 @@ public:
    enum FieldFlags
    {
       FIELD_HideInInspectors     = BIT( 0 ),    ///< Do not show the field in inspectors.
+      FIELD_ComponentInspectors = BIT(1),       ///< Custom fields used by components. They are likely to be non-standard size/configuration, so 
+                                                ///< They are handled specially
    };
 
    struct Field

+ 25 - 0
Engine/source/console/consoleTypes.cpp

@@ -710,6 +710,31 @@ ConsoleSetType( TypeColorI )
       Con::printf("Color must be set as { r, g, b [,a] }, { r g b [b] }  or { stockColorName }");
 }
 
+//-----------------------------------------------------------------------------
+// TypeSimObjectPtr
+//-----------------------------------------------------------------------------
+ConsoleType(SimObject, TypeSimObjectPtr, SimObject*, "")
+
+ConsoleSetType(TypeSimObjectPtr)
+{
+   if (argc == 1)
+   {
+      SimObject **obj = (SimObject **)dptr;
+      *obj = Sim::findObject(argv[0]);
+   }
+   else
+      Con::printf("(TypeSimObjectPtr) Cannot set multiple args to a single S32.");
+}
+
+ConsoleGetType(TypeSimObjectPtr)
+{
+   SimObject **obj = (SimObject**)dptr;
+   static const U32 bufSize = 128;
+   char* returnBuffer = Con::getReturnBuffer(bufSize);
+   dSprintf(returnBuffer, bufSize, "%s", *obj ? (*obj)->getName() ? (*obj)->getName() : (*obj)->getIdString() : "");
+   return returnBuffer;
+}
+
 //-----------------------------------------------------------------------------
 // TypeSimObjectName
 //-----------------------------------------------------------------------------

+ 4 - 0
Engine/source/console/consoleTypes.h

@@ -74,6 +74,8 @@ DefineConsoleType( TypeCommand, String )
 DefineConsoleType( TypeFilename, const char * )
 DefineConsoleType( TypeStringFilename, String )
 
+DefineConsoleType(TypeRotationF, RotationF)
+
 /// A universally unique identifier.
 DefineConsoleType( TypeUUID, Torque::UUID )
 
@@ -123,6 +125,8 @@ DefineConsoleType( TypeColorF, ColorF )
 DefineConsoleType( TypeSimObjectName, SimObject* )
 DefineConsoleType( TypeShader, GFXShader * )
 
+DefineConsoleType(TypeSimObjectPtr, SimObject*)
+
 /// A persistent reference to an object.  This reference indirectly goes
 /// through the referenced object's persistent ID.
 DefineConsoleType( TypeSimPersistId, SimPersistID* )

+ 4 - 0
Engine/source/console/simObject.h

@@ -606,6 +606,10 @@ class SimObject: public ConsoleObject, public TamlCallbacks
       /// Called when the object's name is changed.
       virtual void onNameChange(const char *name);
       
+      /// Called when the adding of the object to the sim is complete, all sub-objects have been processed as well
+      // This is a special-case function that only really gets used with Entities/BehaviorObjects.
+      virtual void onPostAdd() {}
+
       ///
       ///  Specifically, these are called by setDataField
       ///  when a static or dynamic field is modified, see

+ 1 - 1
Engine/source/gfx/gfxDrawUtil.cpp

@@ -1015,7 +1015,7 @@ void GFXDrawUtil::_drawSolidPolyhedron( const GFXStateBlockDesc &desc, const Any
 
    // Allocate a temp buffer for the face indices.
 
-   const U32 numIndices = poly.getNumEdges() * 2;
+   const U32 numIndices = poly.getNumEdges() * 3;
    const U32 numPlanes = poly.getNumPlanes();
 
    GFXPrimitiveBufferHandle prims( mDevice, numIndices, 0, GFXBufferTypeVolatile );

+ 18 - 40
Engine/source/gfx/gl/gfxGLCardProfiler.cpp

@@ -56,54 +56,32 @@ void GFXGLCardProfiler::setupCardCapabilities()
 {
    GLint maxTexSize;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
-
-   const char* versionString = reinterpret_cast<const char*>(glGetString(GL_VERSION));
-   F32 glVersion = dAtof(versionString);
    
    // OpenGL doesn't have separate maximum width/height.
    setCapability("maxTextureWidth", maxTexSize);
    setCapability("maxTextureHeight", maxTexSize);
    setCapability("maxTextureSize", maxTexSize);
 
-   // If extensions haven't been inited, we're in trouble here.
-   bool suppVBO = (gglHasExtension(ARB_vertex_buffer_object) || glVersion >= 1.499f);
-   setCapability("GL::suppVertexBufferObject", suppVBO);
+   // Check for anisotropic filtering support.
+   setCapability("GL_EXT_texture_filter_anisotropic", gglHasExtension(EXT_texture_filter_anisotropic));
 
-   // check if render to texture supported is available
-   bool suppRTT = gglHasExtension(EXT_framebuffer_object);
-   setCapability("GL::suppRenderTexture", suppRTT);
-   
-   bool suppBlit = gglHasExtension(EXT_framebuffer_blit);
-   setCapability("GL::suppRTBlit", suppBlit);
-   
-   bool suppFloatTex = gglHasExtension(ARB_texture_float);
-   setCapability("GL::suppFloatTexture", suppFloatTex);
+   // Check for buffer storage
+   setCapability("GL_ARB_buffer_storage", gglHasExtension(ARB_buffer_storage));
 
-   // Check for anisotropic filtering support.
-   bool suppAnisotropic = gglHasExtension( EXT_texture_filter_anisotropic );
-   setCapability( "GL::suppAnisotropic", suppAnisotropic );
-
-   // check to see if we have the fragment shader extension or the gl version is high enough for glsl to be core
-   // also check to see if the language version is high enough
-   F32 glslVersion = dAtof(reinterpret_cast<const char*>(glGetString( GL_SHADING_LANGUAGE_VERSION)));
-   bool suppSPU = (gglHasExtension(ARB_fragment_shader) || glVersion >= 1.999f) && glslVersion >= 1.0999;
-   setCapability("GL::suppFragmentShader", suppSPU);
-   
-   bool suppAppleFence = gglHasExtension(APPLE_fence);
-   setCapability("GL::APPLE::suppFence", suppAppleFence);
-   
-   // When enabled, call glGenerateMipmapEXT() to generate mipmaps instead of relying on GL_GENERATE_MIPMAP
-   setCapability("GL::Workaround::needsExplicitGenerateMipmap", false);
-   // When enabled, binds and unbinds a texture target before doing the depth buffer copy.  Failure to do
-   // so will cause a hard freeze on Mac OS 10.4 with a Radeon X1600
-   setCapability("GL::Workaround::X1600DepthBufferCopy", false);
-   // When enabled, does not copy the last column and row of the depth buffer in a depth buffer copy.  Failure
-   // to do so will cause a kernel panic on Mac OS 10.5(.1) with a Radeon HD 2600 (fixed in 10.5.2)
-   setCapability("GL::Workaround::HD2600DepthBufferCopy", false);
-   
-   // Certain Intel drivers have a divide by 0 crash if mipmaps are specified with
-   // glTexSubImage2D.
-   setCapability("GL::Workaround::noManualMips", false);
+   // Check for shader model 5.0
+   setCapability("GL_ARB_gpu_shader5", gglHasExtension(ARB_gpu_shader5));
+
+   // Check for texture storage
+   setCapability("GL_ARB_texture_storage", gglHasExtension(ARB_texture_storage));
+
+   // Check for sampler objects
+   setCapability("GL_ARB_sampler_objects", gglHasExtension(ARB_sampler_objects));
+
+   // Check for copy image support
+   setCapability("GL_ARB_copy_image", gglHasExtension(ARB_copy_image));
+
+   // Check for vertex attrib binding
+   setCapability("GL_ARB_vertex_attrib_binding", gglHasExtension(ARB_vertex_attrib_binding));
 }
 
 bool GFXGLCardProfiler::_queryCardCap(const String& query, U32& foundResult)

+ 7 - 5
Engine/source/gfx/gl/gfxGLCircularVolatileBuffer.h

@@ -20,7 +20,8 @@ public:
    }
 
    void init(U32 start, U32 end)
-   {         
+   {  
+      PROFILE_SCOPE(GFXGLQueryFence_issue);
       mStart = start;
       mEnd = end;
       mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
@@ -35,7 +36,8 @@ public:
    }
 
    void wait()
-   {      
+   {   
+      PROFILE_SCOPE(GFXGLQueryFence_block);
       GLbitfield waitFlags = 0;
       GLuint64 waitDuration = 0;
       while( 1 ) 
@@ -158,7 +160,7 @@ public:
       const U32 cSizeInMB = 10;
       mBufferSize = (cSizeInMB << 20);
 
-      if( gglHasExtension(ARB_buffer_storage) )
+      if( GFXGL->mCapabilities.bufferStorage )
       {      
          const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
          glBufferStorage(mBinding, mBufferSize, NULL, flags);
@@ -198,7 +200,7 @@ public:
 
       outOffset = mBufferFreePos;
 
-      if( gglHasExtension(ARB_buffer_storage) )
+      if( GFXGL->mCapabilities.bufferStorage )
       {         
          outPtr = (U8*)(mBufferPtr) + mBufferFreePos; 
       }
@@ -227,7 +229,7 @@ public:
 
    void unlock()
    {
-      if( gglHasExtension(ARB_buffer_storage) )
+      if( GFXGL->mCapabilities.bufferStorage )
       {
          return;
       }

+ 17 - 1
Engine/source/gfx/gl/gfxGLDevice.cpp

@@ -140,10 +140,18 @@ void GFXGLDevice::initGLState()
    
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
+   // [JTH 5/6/2016] GLSL 1.50 is really SM 4.0
    // Setting mPixelShaderVersion to 3.0 will allow Advanced Lighting to run.   
    mPixelShaderVersion = 3.0;
 
-   mSupportsAnisotropic = mCardProfiler->queryProfile( "GL::suppAnisotropic" );
+	// Set capability extensions.
+   mCapabilities.anisotropicFiltering = mCardProfiler->queryProfile("GL_EXT_texture_filter_anisotropic");
+   mCapabilities.bufferStorage = mCardProfiler->queryProfile("GL_ARB_buffer_storage");
+   mCapabilities.shaderModel5 = mCardProfiler->queryProfile("GL_ARB_gpu_shader5");
+   mCapabilities.textureStorage = mCardProfiler->queryProfile("GL_ARB_texture_storage");
+   mCapabilities.samplerObjects = mCardProfiler->queryProfile("GL_ARB_sampler_objects");
+   mCapabilities.copyImage = mCardProfiler->queryProfile("GL_ARB_copy_image");
+   mCapabilities.vertexAttributeBinding = mCardProfiler->queryProfile("GL_ARB_vertex_attrib_binding");
 
    String vendorStr = (const char*)glGetString( GL_VENDOR );
    if( vendorStr.find("NVIDIA", 0, String::NoCase | String::Left) != String::NPos)
@@ -216,6 +224,9 @@ GFXGLDevice::GFXGLDevice(U32 adapterIndex) :
       mCurrentVB_Divisor[i] = 0;
    }
 
+   // Initiailize capabilities to false.
+   memset(&mCapabilities, 0, sizeof(GLCapabilities));
+
    loadGLCore();
 
    GFXGLEnumTranslate::init();
@@ -325,6 +336,7 @@ void GFXGLDevice::resurrect()
 
 GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize)
 {
+   PROFILE_SCOPE(GFXGLDevice_findVBPool);
    for(U32 i = 0; i < mVolatileVBs.size(); i++)
       if (  mVolatileVBs[i]->mNumVerts >= numVerts &&
             mVolatileVBs[i]->mVertexFormat.isEqual( *vertexFormat ) &&
@@ -333,6 +345,7 @@ GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexForma
          return mVolatileVBs[i];
 
    // No existing VB, so create one
+   PROFILE_SCOPE(GFXGLDevice_createVBPool);
    StrongRefPtr<GFXGLVertexBuffer> buf(new GFXGLVertexBuffer(GFX, numVerts, vertexFormat, vertSize, GFXBufferTypeVolatile));
    buf->registerResourceWithDevice(this);
    mVolatileVBs.push_back(buf);
@@ -358,6 +371,7 @@ GFXVertexBuffer *GFXGLDevice::allocVertexBuffer(   U32 numVerts,
                                                    GFXBufferType bufferType,
                                                    void* data )  
 {
+   PROFILE_SCOPE(GFXGLDevice_allocVertexBuffer);
    if(bufferType == GFXBufferTypeVolatile)
       return findVolatileVBO(numVerts, vertexFormat, vertSize);
          
@@ -523,6 +537,7 @@ inline GLsizei GFXGLDevice::primCountToIndexCount(GFXPrimitiveType primType, U32
 
 GFXVertexDecl* GFXGLDevice::allocVertexDecl( const GFXVertexFormat *vertexFormat ) 
 {
+   PROFILE_SCOPE(GFXGLDevice_allocVertexDecl);
    typedef Map<void*, GFXGLVertexDecl> GFXGLVertexDeclMap;
    static GFXGLVertexDeclMap declMap;   
    GFXGLVertexDeclMap::Iterator itr = declMap.find( (void*)vertexFormat->getDescription().c_str() ); // description string are interned, safe to use c_str()
@@ -855,6 +870,7 @@ void GFXGLDevice::setShader(GFXShader *shader, bool force)
 
 void GFXGLDevice::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer)
 {
+   PROFILE_SCOPE(GFXGLDevice_setShaderConstBufferInternal);
    static_cast<GFXGLShaderConstBuffer*>(buffer)->activate();
 }
 

+ 12 - 0
Engine/source/gfx/gl/gfxGLDevice.h

@@ -45,6 +45,18 @@ class GFXGLVertexDecl;
 class GFXGLDevice : public GFXDevice
 {
 public:
+   struct GLCapabilities
+   {
+      bool anisotropicFiltering;
+      bool bufferStorage;
+      bool shaderModel5;
+      bool textureStorage;
+      bool samplerObjects;
+      bool copyImage;
+      bool vertexAttributeBinding;
+   };
+   GLCapabilities mCapabilities;
+
    void zombify();
    void resurrect();
    GFXGLDevice(U32 adapterIndex);

+ 6 - 1
Engine/source/gfx/gl/gfxGLShader.cpp

@@ -23,6 +23,7 @@
 #include "platform/platform.h"
 #include "gfx/gl/gfxGLShader.h"
 #include "gfx/gl/gfxGLVertexAttribLocation.h"
+#include "gfx/gl/gfxGLDevice.h"
 
 #include "core/frameAllocator.h"
 #include "core/stream/fileStream.h"
@@ -344,6 +345,7 @@ void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* ma
 
 void GFXGLShaderConstBuffer::activate()
 {
+   PROFILE_SCOPE(GFXGLShaderConstBuffer_activate);
    mShader->setConstantsFromBuffer(this);
    mWasLost = false;
 }
@@ -394,6 +396,7 @@ void GFXGLShader::clearShaders()
 
 bool GFXGLShader::_init()
 {
+   PROFILE_SCOPE(GFXGLShader_Init);
    // Don't initialize empty shaders.
    if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() )
       return false;
@@ -956,7 +959,7 @@ bool GFXGLShader::_loadShaderFromStream(  GLuint shader,
    buffers.push_back( dStrdup( versionDecl ) );
    lengths.push_back( dStrlen( versionDecl ) );
 
-   if(gglHasExtension(ARB_gpu_shader5))
+   if(GFXGL->mCapabilities.shaderModel5)
    {
       const char *extension = "#extension GL_ARB_gpu_shader5 : enable\r\n";
       buffers.push_back( dStrdup( extension ) );
@@ -1013,6 +1016,7 @@ bool GFXGLShader::initShader( const Torque::Path &file,
                               bool isVertex, 
                               const Vector<GFXShaderMacro> &macros )
 {
+   PROFILE_SCOPE(GFXGLShader_CompileShader);
    GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
    if(isVertex)
       mVertexShader = activeShader;
@@ -1072,6 +1076,7 @@ bool GFXGLShader::initShader( const Torque::Path &file,
 /// Returns our list of shader constants, the material can get this and just set the constants it knows about
 const Vector<GFXShaderConstDesc>& GFXGLShader::getShaderConstDesc() const
 {
+   PROFILE_SCOPE(GFXGLShader_GetShaderConstants);
    return mConstants;
 }
 

+ 3 - 2
Engine/source/gfx/gl/gfxGLStateBlock.cpp

@@ -39,7 +39,7 @@ GFXGLStateBlock::GFXGLStateBlock(const GFXStateBlockDesc& desc) :
    mDesc(desc),
    mCachedHashValue(desc.getHashValue())
 {
-    if( !gglHasExtension(ARB_sampler_objects) )
+    if( !GFXGL->mCapabilities.samplerObjects )
 	   return;
 
    static Map<GFXSamplerStateDesc, U32> mSamplersMap;
@@ -88,6 +88,7 @@ const GFXStateBlockDesc& GFXGLStateBlock::getDesc() const
 /// @param oldState  The current state, used to make sure we don't set redundant states on the device.  Pass NULL to reset all states.
 void GFXGLStateBlock::activate(const GFXGLStateBlock* oldState)
 {
+   PROFILE_SCOPE(GFXGLStateBlock_Activate);
    // Big scary warning copied from Apple docs 
    // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_performance/chapter_13_section_2.html#//apple_ref/doc/uid/TP40001987-CH213-SW12
    // Don't set a state that's already set. Once a feature is enabled, it does not need to be enabled again.
@@ -165,7 +166,7 @@ void GFXGLStateBlock::activate(const GFXGLStateBlock* oldState)
 #undef CHECK_TOGGLE_STATE
 
    //sampler objects
-   if( gglHasExtension(ARB_sampler_objects) )
+   if( GFXGL->mCapabilities.samplerObjects )
    {
       for (U32 i = 0; i < getMin(getOwningDevice()->getNumSamplers(), (U32) TEXTURE_STAGE_COUNT); i++)
       {

+ 10 - 3
Engine/source/gfx/gl/gfxGLTextureManager.cpp

@@ -146,7 +146,7 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex,
 
     glTexParameteri(binding, GL_TEXTURE_MAX_LEVEL, retTex->mMipLevels-1 );
     
-    if( gglHasExtension(ARB_texture_storage) )
+    if( GFXGL->mCapabilities.textureStorage )
     {
         if(binding == GL_TEXTURE_2D)
             glTexStorage2D( retTex->getBinding(), retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height );
@@ -234,6 +234,7 @@ static void _fastTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL)
    
    if(pDL->getFormat() == GFXFormatR8G8B8A8 || pDL->getFormat() == GFXFormatR8G8B8X8)
    {
+      PROFILE_SCOPE(Swizzle32_Upload);
       FrameAllocatorMarker mem;
       U8* pboMemory = (U8*)mem.alloc(bufSize);
       GFX->getDeviceSwizzle32()->ToBuffer(pboMemory, pDL->getBits(0), bufSize);
@@ -241,6 +242,7 @@ static void _fastTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL)
    }
    else
    {
+      PROFILE_SCOPE(SwizzleNull_Upload);
       glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, pDL->getBits(0) );
    }
    
@@ -262,6 +264,7 @@ static void _slowTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL)
 
 bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
 {
+   PROFILE_SCOPE(GFXGLTextureManager_loadTexture);
    GFXGLTextureObject *texture = static_cast<GFXGLTextureObject*>(aTexture);
    
    AssertFatal(texture->getBinding() == GL_TEXTURE_1D || texture->getBinding() == GL_TEXTURE_2D, 
@@ -291,6 +294,8 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
 
 bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
 {
+   PROFILE_SCOPE(GFXGLTextureManager_loadTextureDDS);
+
    AssertFatal(!(dds->mFormat == GFXFormatDXT2 || dds->mFormat == GFXFormatDXT4), "GFXGLTextureManager::_loadTexture - OpenGL does not support DXT2 or DXT4 compressed textures");
    GFXGLTextureObject* texture = static_cast<GFXGLTextureObject*>(aTexture);
    
@@ -304,10 +309,11 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
    glBindTexture(texture->getBinding(), texture->getHandle());
    texture->mFormat = dds->mFormat;
    U32 numMips = dds->mSurfaces[0]->mMips.size();
-   if(GFX->getCardProfiler()->queryProfile("GL::Workaround::noManualMips"))
-      numMips = 1;
+
    for(U32 i = 0; i < numMips; i++)
    {
+      PROFILE_SCOPE(GFXGLTexMan_loadSurface);
+
       if(isCompressedFormat(dds->mFormat))
       {
          if((!isPow2(dds->getWidth()) || !isPow2(dds->getHeight())) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures"))
@@ -344,6 +350,7 @@ bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
 
 bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, void *raw)
 {
+   PROFILE_SCOPE(GFXGLTextureManager_loadTextureRaw);
    if(aTexture->getDepth() < 1)
       return false;
    

+ 8 - 3
Engine/source/gfx/gl/gfxGLTextureObject.cpp

@@ -96,6 +96,9 @@ void GFXGLTextureObject::unlock(U32 mipLevel)
    if(!mLockedRect.bits)
       return;
 
+   // I know this is in unlock, but in GL we actually do our submission in unlock.
+   PROFILE_SCOPE(GFXGLTextureObject_lockRT);
+
    PRESERVE_TEXTURE(mBinding);
    glBindTexture(mBinding, mHandle);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBuffer);
@@ -175,6 +178,7 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
 
    glGetTexImage(mBinding, 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], orig);
    
+   PROFILE_START(GFXGLTextureObject_copyToBmp_pixCopy);
    for(int i = 0; i < srcPixelCount; ++i)
    {
       dest[0] = orig[0];
@@ -186,6 +190,7 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
       orig += srcBytesPerPixel;
       dest += dstBytesPerPixel;
    }
+   PROFILE_END();
 
    return true;
 }
@@ -211,7 +216,7 @@ void GFXGLTextureObject::bind(U32 textureUnit)
    glBindTexture(mBinding, mHandle);
    GFXGL->getOpenglCache()->setCacheBindedTex(textureUnit, mBinding, mHandle);
 
-   if( gglHasExtension(ARB_sampler_objects) )
+   if(GFXGL->mCapabilities.samplerObjects)
 	   return;
   
    GFXGLStateBlockRef sb = mGLDevice->getCurrentStateBlock();
@@ -298,8 +303,8 @@ void GFXGLTextureObject::reloadFromCache()
    else if(mBinding == GL_TEXTURE_1D)
 		glTexSubImage1D(mBinding, 0, 0, (mTextureSize.x > 1 ? mTextureSize.x : mTextureSize.y), GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], mZombieCache);
    
-   if(GFX->getCardProfiler()->queryProfile("GL::Workaround::needsExplicitGenerateMipmap") && mMipLevels != 1)
-      glGenerateMipmapEXT(mBinding);
+   if(mMipLevels != 1)
+      glGenerateMipmap(mBinding);
       
    delete[] mZombieCache;
    mZombieCache = NULL;

+ 1 - 1
Engine/source/gfx/gl/gfxGLTextureTarget.cpp

@@ -410,7 +410,7 @@ void GFXGLTextureTarget::resolveTo(GFXTextureObject* obj)
    AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
    GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
 
-   if( gglHasExtension(ARB_copy_image) && mTargets[Color0]->isCompatible(glTexture) )
+   if( GFXGL->mCapabilities.copyImage && mTargets[Color0]->isCompatible(glTexture) )
    {
       GLenum binding = mTargets[Color0]->getBinding();      
       binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;

+ 2 - 2
Engine/source/gfx/gl/gfxGLVertexBuffer.cpp

@@ -78,7 +78,7 @@ void GFXGLVertexBuffer::lock( U32 vertexStart, U32 vertexEnd, void **vertexPtr )
    if( mBufferType == GFXBufferTypeVolatile )
    {
       AssertFatal(vertexStart == 0, "");
-      if( gglHasExtension(ARB_vertex_attrib_binding) )
+      if( GFXGL->mCapabilities.vertexAttributeBinding )
       {
          getCircularVolatileVertexBuffer()->lock( mNumVerts * mVertexSize, 0, mBufferOffset, *vertexPtr );
       }
@@ -136,7 +136,7 @@ void GFXGLVertexBuffer::prepare()
 
 void GFXGLVertexBuffer::prepare(U32 stream, U32 divisor)
 {
-   if( gglHasExtension(ARB_vertex_attrib_binding) )
+   if( GFXGL->mCapabilities.vertexAttributeBinding )
    {      
       glBindVertexBuffer( stream, mBuffer, mBufferOffset, mVertexSize );
       glVertexBindingDivisor( stream, divisor );

+ 2 - 2
Engine/source/gfx/gl/gfxGLVertexDecl.cpp

@@ -15,7 +15,7 @@ void GFXGLVertexDecl::init(const GFXVertexFormat *format)
 void GFXGLVertexDecl::prepareVertexFormat() const
 {
    AssertFatal(mFormat, "GFXGLVertexDecl - Not inited");
-   if( gglHasExtension(ARB_vertex_attrib_binding) )
+   if( GFXGL->mCapabilities.vertexAttributeBinding )
    {
       for ( U32 i=0; i < glVerticesFormat.size(); i++ )
       {
@@ -36,7 +36,7 @@ void GFXGLVertexDecl::prepareBuffer_old(U32 stream, GLint mBuffer, GLint mDiviso
    PROFILE_SCOPE(GFXGLVertexDecl_prepare);
    AssertFatal(mFormat, "GFXGLVertexDecl - Not inited");
 
-   if( gglHasExtension(ARB_vertex_attrib_binding) )
+   if( GFXGL->mCapabilities.vertexAttributeBinding )
       return;   
 
 	// Bind the buffer...

+ 1 - 1
Engine/source/gfx/gl/gfxGLWindowTarget.cpp

@@ -78,7 +78,7 @@ void GFXGLWindowTarget::resolveTo(GFXTextureObject* obj)
    AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
    GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
 
-   if( gglHasExtension(ARB_copy_image) )
+   if( GFXGL->mCapabilities.copyImage )
    {
       if(mBackBufferColorTex.getWidth() == glTexture->getWidth()
          && mBackBufferColorTex.getHeight() == glTexture->getHeight()

+ 69 - 1
Engine/source/gui/controls/guiTreeViewCtrl.cpp

@@ -36,7 +36,9 @@
    #include "gui/editor/editorFunctions.h"
 #endif
 #include "console/engineAPI.h"
-
+#ifdef TORQUE_EXPERIMENTAL_EC
+#include "T3D/entity.h"
+#endif
 
 IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
 
@@ -486,6 +488,14 @@ void GuiTreeViewCtrl::Item::getDisplayText(U32 bufLen, char *buf)
 {
    FrameAllocatorMarker txtAlloc;
 
+   //if we're doing the special case of forcing the item text, just skip the rest of this junk
+   if (mState.test(ForceItemName))
+   {
+      StringTableEntry text = (mScriptInfo.mText) ? mScriptInfo.mText : StringTable->EmptyString();
+      dStrncpy(buf, text, bufLen);
+      return;
+   }
+
    if( mState.test( InspectorData ) )
    {
       SimObject *pObject = getObject();
@@ -637,6 +647,20 @@ void GuiTreeViewCtrl::Item::getTooltipText(U32 bufLen, char *buf)
 
 bool GuiTreeViewCtrl::Item::isParent() const
 {
+#ifdef TORQUE_EXPERIMENTAL_EC
+   //We might have a special case with entities
+   //So if our entity either has children, or has some component with the EditorInspect interface, we return true
+   if (mInspectorInfo.mObject)
+   {
+      Entity* e = dynamic_cast<Entity*>(mInspectorInfo.mObject.getObject());
+      if (e)
+      {
+         if (e->size() > 0 || e->getComponentCount() != 0)
+            return true;
+      }
+   }
+#endif
+
    if(mState.test(VirtualParent))
    {
       if( !isInspectorData() )
@@ -1518,6 +1542,11 @@ bool GuiTreeViewCtrl::isValidDragTarget( Item* item )
 {
    bool isValid = true;
    
+   // First, check if we're just going to override this from manually setting the ForceAllowDrag flag
+   // If that's set, we're assuming special circumstances and will just let it go on it's way
+   if (item->isDragTargetAllowed())
+      return true;
+   
    // If this is inspector data, first make sure the item accepts all
    // selected objects as children.  This prevents bad surprises when
    // certain SimSet subclasses reject children and start shoving them
@@ -3462,6 +3491,11 @@ void GuiTreeViewCtrl::onMouseDragged(const GuiEvent &event)
    if (mSelectedItems.size() == 0)
       return;
       
+   //Check through to make sure all attempted dragged items even allow it
+   for (U32 i = 0; i < mSelectedItems.size(); i++)
+      if (!mSelectedItems[i]->isDragAllowed())
+         return;
+      
    // Give us a little delta before we actually start a mouse drag so that
    // if the user moves the mouse a little while clicking, he/she does not
    // accidentally trigger a drag.
@@ -3756,6 +3790,28 @@ void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event)
       if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
          onVirtualParentExpand(item);
       
+#ifdef TORQUE_EXPERIMENTAL_EC
+      //Slightly hacky, but I'm not sure of a better setup until we get major update to the editors
+      //We check if our object is an entity, and if it is, we call a 'onInspect' function.
+      //This function is pretty much a special notifier to the entity so if it has any behaviors that do special
+      //stuff in the editor, it can fire that up
+      if (item->isInspectorData())
+      {
+         Entity* e = dynamic_cast<Entity*>(item->getObject());
+         //if (item->mScriptInfo.mText != StringTable->insert("Components"))
+         {
+            Entity* e = dynamic_cast<Entity*>(item->getObject());
+            if (e)
+            {
+               if (item->isExpanded())
+                  e->onInspect();
+               else
+                  e->onEndInspect();
+            }
+         }
+      }
+#endif
+      
       mFlags.set( RebuildVisible );
       scrollVisible(item);
    }
@@ -4492,6 +4548,13 @@ bool GuiTreeViewCtrl::objectSearch( const SimObject *object, Item **item )
       if ( !pItem )
          continue;
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+      //A bit hackish, but we make a special exception here for items that are named 'Components', as they're merely
+      //virtual parents to act as a container to an Entity's components
+      if (pItem->mScriptInfo.mText == StringTable->insert("Components"))
+         continue;
+#endif
+
 		SimObject *pObj = pItem->getObject();
 
 		if ( pObj && pObj == object )
@@ -4555,6 +4618,11 @@ bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate)
    
    // Go through our items and purge those that have disappeared from
    // the set.
+#ifdef TORQUE_EXPERIMENTAL_EC
+   //Entities will be a special case here, if we're an entity, skip this step
+   if (dynamic_cast<Entity*>(srcObj))
+      return true;
+#endif
    
    for( Item* ptr = item->mChild; ptr != NULL; )
    {

+ 12 - 1
Engine/source/gui/controls/guiTreeViewCtrl.h

@@ -75,7 +75,10 @@ class GuiTreeViewCtrl : public GuiArrayCtrl
                ShowClassName     = BIT( 11 ),
                ShowObjectName    = BIT( 12 ),
                ShowInternalName  = BIT( 13 ),
-               ShowClassNameForUnnamed = BIT( 14 )
+               ShowClassNameForUnnamed = BIT( 14 ),
+               ForceItemName = BIT(15),
+               ForceDragTarget = BIT(16),
+               DenyDrag = BIT(17),
             };
 
             GuiTreeViewCtrl* mParentControl;
@@ -169,6 +172,14 @@ class GuiTreeViewCtrl : public GuiArrayCtrl
             /// or false if it's just an item.
             bool isInspectorData() const { return mState.test(InspectorData); };
 
+            /// Returns true if we've been manually set to allow dragging overrides.
+            /// As it's a manually set flag, by default it is false.
+            bool isDragTargetAllowed() const { return mState.test(ForceDragTarget); };
+
+            /// Returns true if we've been manually set to allow dragging overrides.
+            /// As it's a manually set flag, by default it is false.
+            bool isDragAllowed() const { return !mState.test(DenyDrag); };
+
             /// Returns true if we should show the expand art
             /// and make the item interact with the mouse as if
             /// it were a parent.

+ 1 - 1
Engine/source/gui/core/guiCanvas.h

@@ -37,7 +37,7 @@
 #include "core/util/tSignal.h"
 #endif
 
-#include "component/interfaces/IProcessInput.h"
+#include "platform/input/IProcessInput.h"
 #include "windowManager/platformWindowMgr.h"
 #include "gfx/gfxFence.h"
 

+ 42 - 0
Engine/source/gui/editor/guiInspector.cpp

@@ -29,6 +29,11 @@
 #include "gui/containers/guiScrollCtrl.h"
 #include "gui/editor/inspector/customField.h"
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+#include "gui/editor/inspector/entityGroup.h"
+#include "gui/editor/inspector/mountingGroup.h"
+#include "gui/editor/inspector/componentGroup.h"
+#endif
 
 IMPLEMENT_CONOBJECT(GuiInspector);
 
@@ -584,6 +589,43 @@ void GuiInspector::refresh()
    mGroups.push_back(general);
    addObject(general);
 
+#ifdef TORQUE_EXPERIMENTAL_EC
+   //Entity inspector group
+   if (mTargets.first()->getClassRep()->isSubclassOf("Entity"))
+   {
+      GuiInspectorEntityGroup *components = new GuiInspectorEntityGroup("Components", this);
+      if (components != NULL)
+      {
+         components->registerObject();
+         mGroups.push_back(components);
+         addObject(components);
+      }
+
+      //Mounting group override
+      GuiInspectorGroup *mounting = new GuiInspectorMountingGroup("Mounting", this);
+      if (mounting != NULL)
+      {
+         mounting->registerObject();
+         mGroups.push_back(mounting);
+         addObject(mounting);
+      }
+   }
+
+   if (mTargets.first()->getClassRep()->isSubclassOf("Component"))
+   {
+      //Build the component field groups as the component describes it
+      Component* comp = dynamic_cast<Component*>(mTargets.first().getPointer());
+
+      if (comp->getComponentFieldCount() > 0)
+      {
+         GuiInspectorComponentGroup *compGroup = new GuiInspectorComponentGroup("Component Fields", this);
+         compGroup->registerObject();
+         mGroups.push_back(compGroup);
+         addObject(compGroup);
+      }
+   }
+#endif
+
    // Create the inspector groups for static fields.
 
    for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter )

+ 252 - 0
Engine/source/gui/editor/inspector/componentGroup.cpp

@@ -0,0 +1,252 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "gui/buttons/guiIconButtonCtrl.h"
+#include "gui/editor/guiInspector.h"
+#include "gui/editor/inspector/componentGroup.h"
+#include "core/strings/stringUnit.h"
+#include "T3D/components/component.h"
+#include "gui/editor/inspector/field.h"
+
+#include "console/engineAPI.h"
+
+IMPLEMENT_CONOBJECT(GuiInspectorComponentGroup);
+
+ConsoleDocClass(GuiInspectorComponentGroup,
+   "@brief Used to inspect an object's FieldDictionary (dynamic fields) instead "
+   "of regular persistent fields.\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+   );
+
+GuiInspectorComponentGroup::GuiInspectorComponentGroup(StringTableEntry groupName, SimObjectPtr<GuiInspector> parent)
+: GuiInspectorGroup(groupName, parent) 
+{ 
+   /*mNeedScroll=false;*/
+};
+
+bool GuiInspectorComponentGroup::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+//-----------------------------------------------------------------------------
+// GuiInspectorComponentGroup - add custom controls
+//-----------------------------------------------------------------------------
+bool GuiInspectorComponentGroup::createContent()
+{
+   if(!Parent::createContent())
+      return false;
+
+   Con::evaluatef("%d.stack = %d;", this->getId(), mStack->getId());
+
+   Con::executef(this, "createContent");
+
+   return true;
+}
+
+//-----------------------------------------------------------------------------
+// GuiInspectorComponentGroup - inspectGroup override
+//-----------------------------------------------------------------------------
+bool GuiInspectorComponentGroup::inspectGroup()
+{
+   // We can't inspect a group without a target!
+   if (!mParent || !mParent->getNumInspectObjects())
+      return false;
+
+   clearFields();
+
+   // to prevent crazy resizing, we'll just freeze our stack for a sec..
+   mStack->freeze(true);
+
+   bool bNoGroup = false;
+
+   bool bNewItems = false;
+   bool bMakingArray = false;
+   GuiStackControl *pArrayStack = NULL;
+   GuiRolloutCtrl *pArrayRollout = NULL;
+   bool bGrabItems = false;
+
+   Component* comp = dynamic_cast<Component*>(getInspector()->getInspectObject(0));
+
+   //if this isn't a component, what are we even doing here?
+   if (!comp)
+      return false;
+
+   for (U32 i = 0; i < comp->getComponentFieldCount(); i++)
+   {
+      ComponentField* field = comp->getComponentField(i);
+
+      bNewItems = true;
+
+      GuiInspectorField *fieldGui = constructField(field->mFieldType);
+      if (fieldGui == NULL)
+         fieldGui = new GuiInspectorField();
+
+      fieldGui->init(mParent, this);
+
+      AbstractClassRep::Field *refField;
+
+      //check dynamics
+      SimFieldDictionary* fieldDictionary = comp->getFieldDictionary();
+      SimFieldDictionaryIterator itr(fieldDictionary);
+
+      while (*itr)
+      {
+         SimFieldDictionary::Entry* entry = *itr;
+         if (entry->slotName == field->mFieldName)
+         {
+            AbstractClassRep::Field f;
+            f.pFieldname = StringTable->insert(field->mFieldName);
+
+            if (field->mFieldDescription)
+               f.pFieldDocs = field->mFieldDescription;
+
+            f.type = field->mFieldType;
+            f.offset = -1;
+            f.elementCount = 1;
+            f.validator = NULL;
+            f.flag = 0; //change to be the component type
+
+            f.setDataFn = &defaultProtectedSetFn;
+            f.getDataFn = &defaultProtectedGetFn;
+            f.writeDataFn = &defaultProtectedWriteFn;
+
+            f.pFieldDocs = field->mFieldDescription;
+
+            f.pGroupname = "Component Fields";
+
+            ConsoleBaseType* conType = ConsoleBaseType::getType(field->mFieldType);
+            AssertFatal(conType, "ConsoleObject::addField - invalid console type");
+            f.table = conType->getEnumTable();
+
+            tempFields.push_back(f);
+
+            refField = &f;
+
+            break;
+         }
+         ++itr;
+      }
+
+      if (!refField)
+         continue;
+
+      fieldGui->setInspectorField(&tempFields[tempFields.size() - 1]);
+
+      if (fieldGui->registerObject())
+      {
+#ifdef DEBUG_SPEW
+         Platform::outputDebugString("[GuiInspectorGroup] Adding field '%s'",
+            field->pFieldname);
+#endif
+
+         mChildren.push_back(fieldGui);
+         mStack->addObject(fieldGui);
+      }
+      else
+      {
+         SAFE_DELETE(fieldGui);
+      }
+   }
+
+   mStack->freeze(false);
+   mStack->updatePanes();
+
+   // If we've no new items, there's no need to resize anything!
+   if (bNewItems == false && !mChildren.empty())
+      return true;
+
+   sizeToContents();
+
+   setUpdate();
+
+   return true;
+}
+
+void GuiInspectorComponentGroup::updateAllFields()
+{
+   // We overload this to just reinspect the group.
+   inspectGroup();
+}
+
+void GuiInspectorComponentGroup::onMouseMove(const GuiEvent &event)
+{
+   //mParent->mOverDivider = false;
+}
+ConsoleMethod(GuiInspectorComponentGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.")
+{
+   return object->inspectGroup();
+}
+
+void GuiInspectorComponentGroup::clearFields()
+{
+   // delete everything else
+   mStack->clear();
+
+   // clear the mChildren list.
+   mChildren.clear();
+}
+
+SimFieldDictionary::Entry* GuiInspectorComponentGroup::findDynamicFieldInDictionary(StringTableEntry fieldName)
+{
+   SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary();
+
+   for (SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr)
+   {
+      SimFieldDictionary::Entry * entry = (*ditr);
+
+      if (entry->slotName == fieldName)
+         return entry;
+   }
+
+   return NULL;
+}
+
+void GuiInspectorComponentGroup::addDynamicField()
+{
+}
+
+AbstractClassRep::Field* GuiInspectorComponentGroup::findObjectBehaviorField(Component* target, String fieldName)
+{
+   AbstractClassRep::FieldList& fieldList = target->getClassRep()->mFieldList;
+   for (AbstractClassRep::FieldList::iterator itr = fieldList.begin();
+      itr != fieldList.end(); ++itr)
+   {
+      AbstractClassRep::Field* field = &(*itr);
+      String fldNm(field->pFieldname);
+      if (fldNm == fieldName)
+         return field;
+   }
+   return NULL;
+}
+ConsoleMethod(GuiInspectorComponentGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();")
+{
+   object->addDynamicField();
+}
+
+ConsoleMethod(GuiInspectorComponentGroup, removeDynamicField, void, 3, 3, "")
+{
+}

Some files were not shown because too many files changed in this diff