Browse Source

Updates to various components, added a few new ones.

Areloch 6 years ago
parent
commit
34d05ff16f
52 changed files with 4516 additions and 1749 deletions
  1. 1 1
      Engine/source/T3D/components/animation/animationComponent.cpp
  2. 1 1
      Engine/source/T3D/components/animation/animationComponent.h
  3. 4 4
      Engine/source/T3D/components/audio/SoundComponent.cpp
  4. 695 403
      Engine/source/T3D/components/collision/collisionComponent.cpp
  5. 123 125
      Engine/source/T3D/components/collision/collisionComponent.h
  6. 0 258
      Engine/source/T3D/components/collision/collisionInterfaces.cpp
  7. 0 167
      Engine/source/T3D/components/collision/collisionInterfaces.h
  8. 1 1
      Engine/source/T3D/components/collision/collisionTrigger.cpp
  9. 122 0
      Engine/source/T3D/components/collision/raycastColliderComponent.cpp
  10. 46 0
      Engine/source/T3D/components/collision/raycastColliderComponent.h
  11. 607 0
      Engine/source/T3D/components/collision/shapeCollisionComponent.cpp
  12. 137 0
      Engine/source/T3D/components/collision/shapeCollisionComponent.h
  13. 172 0
      Engine/source/T3D/components/collision/shapeCollisionComponent_ScriptBinding.h
  14. 216 0
      Engine/source/T3D/components/collision/simpleHitboxComponent.cpp
  15. 39 0
      Engine/source/T3D/components/collision/simpleHitboxComponent.h
  16. 7 1
      Engine/source/T3D/components/component.cpp
  17. 4 1
      Engine/source/T3D/components/component.h
  18. 149 0
      Engine/source/T3D/components/game/controlObjectComponent.cpp
  19. 30 0
      Engine/source/T3D/components/game/controlObjectComponent.h
  20. 359 0
      Engine/source/T3D/components/game/followPathComponent.cpp
  21. 82 0
      Engine/source/T3D/components/game/followPathComponent.h
  22. 171 0
      Engine/source/T3D/components/game/interactComponent.cpp
  23. 34 0
      Engine/source/T3D/components/game/interactComponent.h
  24. 94 0
      Engine/source/T3D/components/game/interactableComponent.cpp
  25. 30 0
      Engine/source/T3D/components/game/interactableComponent.h
  26. 20 20
      Engine/source/T3D/components/game/triggerComponent.cpp
  27. 2 2
      Engine/source/T3D/components/game/triggerComponent.h
  28. 94 86
      Engine/source/T3D/components/physics/physicsComponent.cpp
  29. 42 21
      Engine/source/T3D/components/physics/physicsComponent.h
  30. 26 22
      Engine/source/T3D/components/physics/playerControllerComponent.cpp
  31. 9 13
      Engine/source/T3D/components/physics/playerControllerComponent.h
  32. 3 3
      Engine/source/T3D/components/physics/rigidBodyComponent.cpp
  33. 4 4
      Engine/source/T3D/components/physics/rigidBodyComponent.h
  34. 391 0
      Engine/source/T3D/components/physics/simplePhysicsComponent.cpp
  35. 88 0
      Engine/source/T3D/components/physics/simplePhysicsComponent.h
  36. 36 57
      Engine/source/T3D/components/render/meshComponent.cpp
  37. 4 15
      Engine/source/T3D/components/render/meshComponent.h
  38. 10 19
      Engine/source/T3D/entity.cpp
  39. 3 2
      Engine/source/T3D/entity.h
  40. 2 0
      Engine/source/T3D/physics/bullet/btBody.h
  41. 2 0
      Engine/source/T3D/physics/physicsBody.h
  42. 1 283
      Engine/source/T3D/systems/render/meshRenderSystem.cpp
  43. 1 146
      Engine/source/T3D/systems/render/meshRenderSystem.h
  44. 5 0
      Templates/BaseGame/game/core/components/Core_Components.module
  45. 2 3
      Templates/BaseGame/game/core/components/components/game/controlObject.asset.taml
  46. 0 89
      Templates/BaseGame/game/core/components/components/game/controlObject.cs
  47. 107 0
      Templates/BaseGame/game/tools/componentEditor/gui/TypeMaskFieldGui.gui
  48. 276 0
      Templates/BaseGame/game/tools/componentEditor/interface/materialFieldType.cs
  49. 215 0
      Templates/BaseGame/game/tools/componentEditor/interface/stateMachineField.cs
  50. 40 0
      Templates/BaseGame/game/tools/componentEditor/interface/typeMaskFieldType.cs
  51. 7 0
      Templates/BaseGame/game/tools/componentEditor/main.cs
  52. 2 2
      Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs

+ 1 - 1
Engine/source/T3D/components/animation/animationComponent.cpp

@@ -74,7 +74,7 @@ AnimationComponent::AnimationComponent() : Component()
    mNetworked = true;
 
    mFriendlyName = "Animation(Component)";
-   mComponentType = "Render";
+   mComponentType = "Animation";
 
    mDescription = getDescriptionText("Allows a rendered mesh to be animated");
 

+ 1 - 1
Engine/source/T3D/components/animation/animationComponent.h

@@ -101,7 +101,7 @@ public:
 
    TSShape* getShape();
 
-   void targetShapeChanged(RenderComponentInterface* instanceInterface);
+   virtual void targetShapeChanged(RenderComponentInterface* instanceInterface);
 
    virtual void processTick();
    virtual void advanceTime(F32 dt);

+ 4 - 4
Engine/source/T3D/components/audio/SoundComponent.cpp

@@ -92,13 +92,13 @@ void SoundComponent::onComponentAdd()
 {
    Parent::onComponentAdd();
 
-   Con::printf("We were added to an entity! SoundComponent reporting in for owner entity %i", mOwner->getId());
+   //Con::printf("We were added to an entity! SoundComponent reporting in for owner entity %i", mOwner->getId());
 }
 
 //This is called when the component has been removed from an entity
 void SoundComponent::onComponentRemove()
 {
-   Con::printf("We were removed from our entity! SoundComponent signing off for owner entity %i", mOwner->getId());
+   //Con::printf("We were removed from our entity! SoundComponent signing off for owner entity %i", mOwner->getId());
    Parent::onComponentRemove();
 }
 
@@ -113,14 +113,14 @@ void SoundComponent::componentAddedToOwner(Component *comp)
          playAudio(slotNum, mSoundFile[slotNum]);
       }
    }
-   Con::printf("Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
+   //Con::printf("Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
 }
 
 //This is called any time a component is removed from an entity. Every component current owned by the entity is informed of the event.
 //This allows cleanup and dependency management.
 void SoundComponent::componentRemovedFromOwner(Component *comp)
 {
-   Con::printf("Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
+   //Con::printf("Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
 }
 
 //Regular init persist fields function to set up static fields.

+ 695 - 403
Engine/source/T3D/components/collision/collisionComponent.cpp

@@ -21,122 +21,32 @@
 //-----------------------------------------------------------------------------
 
 #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 "scene/sceneObject.h"
+#include "T3D/entity.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 "materials/baseMatInstance.h"
+#include "collision/extrudedPolyList.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 "collision/clippedPolyList.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;
+static F32 sTractionDistance = 0.04f;
+
+IMPLEMENT_CONOBJECT(CollisionComponent);
 
-//
 CollisionComponent::CollisionComponent() : Component()
 {
-   mFriendlyName = "Collision(Component)";
-
-   mOwnerRenderInterface = NULL;
-   mOwnerPhysicsInterface = NULL;
+   mFriendlyName = "Collision Component";
 
-   mBlockColliding = true;
+   mComponentType = "Collision";
 
-   mCollisionType = CollisionMesh;
-   mLOSType = CollisionMesh;
-   mDecalType = CollisionMesh;
+   mDescription = getDescriptionText("A stub component class that collision components should inherit from.");
 
-   colisionMeshPrefix = StringTable->insert("Collision");
+   mBlockColliding = true;
 
    CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
       StaticShapeObjectType | VehicleObjectType |
@@ -146,8 +56,6 @@ CollisionComponent::CollisionComponent() : Component()
    mPhysicsWorld = nullptr;
 
    mTimeoutList = nullptr;
-
-   mAnimated = false;
 }
 
 CollisionComponent::~CollisionComponent()
@@ -159,423 +67,807 @@ CollisionComponent::~CollisionComponent()
    }
 
    SAFE_DELETE_ARRAY(mDescription);
+
+   SAFE_DELETE(mPhysicsRep);
+}
+
+bool CollisionComponent::checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start)
+{
+   return false;
+}
+
+bool CollisionComponent::updateCollisions(F32 time, VectorF vector, VectorF velocity)
+{
+   return false;
 }
 
-IMPLEMENT_CO_NETOBJECT_V1(CollisionComponent);
+void CollisionComponent::updateWorkingCollisionSet(const U32 mask)
+{
+}
 
-void CollisionComponent::onComponentAdd()
+void CollisionComponent::handleCollisionList( CollisionList &collisionList, VectorF velocity )
 {
-   Parent::onComponentAdd();
+   Collision bestCol;
+
+   mCollisionList = collisionList;
 
-   RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
-   if (renderInterface)
+   for (U32 i=0; i < collisionList.getCount(); ++i)
    {
-      renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
-      mOwnerRenderInterface = renderInterface;
+      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)
+            {
+               CollisionComponent *colObjectInterface = ent->getComponent<CollisionComponent>();
+               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);
+         }
+      }
    }
+}
 
-   //physicsInterface
-   PhysicsComponentInterface *physicsInterface = mOwner->getComponent<PhysicsComponentInterface>();
-   if (!physicsInterface)
+void CollisionComponent::handleCollision( Collision &col, VectorF velocity )
+{
+   if (col.object && (mContactInfo.contactObject == NULL ||
+      col.object->getId() != mContactInfo.contactObject->getId()))
    {
-      mPhysicsRep = PHYSICSMGR->createBody();
-   }
+      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);
+      }
 
-   prepCollision();
+      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 CollisionComponent::onComponentRemove()
+void CollisionComponent::handleCollisionNotifyList()
 {
-   SAFE_DELETE(mPhysicsRep);
+   //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());
+      }
+   }
 
-   Parent::onComponentRemove();
+   mCollisionNotifyList.clear();
 }
 
-void CollisionComponent::componentAddedToOwner(Component *comp)
+Chunker<CollisionComponent::CollisionTimeout> sCollisionTimeoutChunker;
+CollisionComponent::CollisionTimeout* CollisionComponent::sFreeTimeoutList = 0;
+
+void CollisionComponent::queueCollision( SceneObject *obj, const VectorF &vec)
 {
-   if (comp->getId() == getId())
-      return;
+   // Add object to list of collisions.
+   SimTime time = Sim::getCurrentTime();
+   S32 num = obj->getId();
 
-   //test if this is a shape component!
-   RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
-   if (renderInterface)
+   CollisionTimeout** adr = &mTimeoutList;
+   CollisionTimeout* ptr = mTimeoutList;
+   while (ptr) 
    {
-      renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
-      mOwnerRenderInterface = renderInterface;
-      prepCollision();
+      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;
+      }
    }
 
-   PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
-   if (physicsInterface)
+   // New entry for the object
+   if (sFreeTimeoutList != NULL)
    {
-      if (mPhysicsRep)
-         SAFE_DELETE(mPhysicsRep);
-
-      prepCollision();
+      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;
 }
 
-void CollisionComponent::componentRemovedFromOwner(Component *comp)
+bool CollisionComponent::checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale, 
+														Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList)
 {
-   if (comp->getId() == getId()) //?????????
-      return;
+   Point3F end = start + velocity * time;
+   Point3F distance = end - start;
 
-   //test if this is a shape component!
-   RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
-   if (renderInterface)
-   {
-      renderInterface->onShapeInstanceChanged.remove(this, &CollisionComponent::targetShapeChanged);
-      mOwnerRenderInterface = NULL;
-      prepCollision();
-   }
+   Box3F scaledBox = objectBox;
+   scaledBox.minExtents.convolve(objectScale);
+   scaledBox.maxExtents.convolve(objectScale);
 
-   //physicsInterface
-   PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
-   if (physicsInterface)
+   if (mFabs(distance.x) < objectBox.len_x() &&
+      mFabs(distance.y) < objectBox.len_y() &&
+      mFabs(distance.z) < objectBox.len_z())
    {
-      mPhysicsRep = PHYSICSMGR->createBody();
+      // 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;
+      }
 
-      prepCollision();
+      if (eaPolyList.isEmpty())
+      {
+         return true;
+      }
    }
-}
 
-void CollisionComponent::checkDependencies()
-{
+   return false;
 }
 
-void CollisionComponent::initPersistFields()
-{
-   Parent::initPersistFields();
 
-   addGroup("Collision");
+Collision* CollisionComponent::getCollision(S32 col) 
+{ 
+   if(col < mCollisionList.getCount() && col >= 0) 
+      return &mCollisionList[col];
+   else 
+      return NULL; 
+}
+
+Point3F CollisionComponent::getContactNormal() 
+{ 
+   return mContactInfo.contactNormal; 
+}
 
-      addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, CollisionComponent),
-         "The type of mesh data to use for collision queries.");
+bool CollisionComponent::hasContact()
+{
+   if (mContactInfo.contactObject)
+      return true;
+   else
+      return false;
+}
 
-      addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, CollisionComponent),
-         "The type of mesh data to use for collision queries.");
+S32 CollisionComponent::getCollisionCount()
+{
+   return mCollisionList.getCount();
+}
 
-      addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, CollisionComponent),
-         "The type of mesh data to use for collision queries.");
+Point3F CollisionComponent::getCollisionNormal(S32 collisionIndex)
+{
+   if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
+      return Point3F::Zero;
 
-      addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, CollisionComponent),
-         "The type of mesh data to use for collision queries.");
+   return mCollisionList[collisionIndex].normal;
+}
 
-      addField("BlockCollisions", TypeBool, Offset(mBlockColliding, CollisionComponent), "");
+F32 CollisionComponent::getCollisionAngle(S32 collisionIndex, Point3F upVector)
+{
+   if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
+      return 0.0f;
 
-   endGroup("Collision");
+   return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
 }
 
-void CollisionComponent::inspectPostApply()
+S32 CollisionComponent::getBestCollision(Point3F upVector)
 {
-   // Apply any transformations set in the editor
-   Parent::inspectPostApply();
+   S32 bestCollision = -1;
 
-   if (isServerObject())
+   F32 bestAngle = 360.f;
+   S32 count = mCollisionList.getCount();
+   for (U32 i = 0; i < count; ++i)
    {
-      setMaskBits(ColliderMask);
-      prepCollision();
+      F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector)));
+
+      if (angle < bestAngle)
+      {
+         bestCollision = i;
+         bestAngle = angle;
+      }
    }
+
+   return bestCollision;
 }
 
-U32 CollisionComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+F32 CollisionComponent::getBestCollisionAngle(VectorF upVector)
 {
-   U32 retMask = Parent::packUpdate(con, mask, stream);
+   S32 bestCol = getBestCollision(upVector);
 
-   if (stream->writeFlag(mask & (ColliderMask | InitialUpdateMask)))
-   {
-      stream->write((U32)mCollisionType);
-      stream->writeString(colisionMeshPrefix);
-   }
+   if (bestCol == -1)
+      return 0;
 
-   return retMask;
+   return getCollisionAngle(bestCol, upVector);
 }
 
-void CollisionComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+//
+bool CollisionComponent::buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list)
 {
-   Parent::unpackUpdate(con, stream);
+   AssertFatal(dl >= 0 && dl < sI->getShape()->details.size(), "TSShapeInstance::buildConvexOpcode");
 
-   if (stream->readFlag()) // UpdateMask
-   {
-      U32 collisionType = CollisionMesh;
+   TSShape* shape = sI->getShape();
 
-      stream->read(&collisionType);
+   const MatrixF &objMat = mOwner->getObjToWorld();
+   const Point3F &objScale = mOwner->getScale();
 
-      // Handle it if we have changed CollisionType's
-      if ((MeshType)collisionType != mCollisionType)
-      {
-         mCollisionType = (MeshType)collisionType;
+   // get subshape and object detail
+   const TSDetail * detail = &shape->details[dl];
+   S32 ss = detail->subShapeNum;
+   S32 od = detail->objectDetailNum;
 
-         prepCollision();
-      }
+   // nothing emitted yet...
+   bool emitted = false;
+
+   S32 start = shape->subShapeFirstObject[ss];
+   S32 end = shape->subShapeNumObjects[ss] + start;
+   if (start<end)
+   {
+      MatrixF initialMat = objMat;
+      Point3F initialScale = objScale;
+
+      // set up for first object's node
+      MatrixF mat;
+      MatrixF scaleMat(true);
+      F32* p = scaleMat;
+      p[0] = initialScale.x;
+      p[5] = initialScale.y;
+      p[10] = initialScale.z;
+      const MatrixF * previousMat = &sI->mMeshObjects[start].getTransform();
+      mat.mul(initialMat, scaleMat);
+      mat.mul(*previousMat);
+
+      // Update our bounding box...
+      Box3F localBox = bounds;
+      MatrixF otherMat = mat;
+      otherMat.inverse();
+      otherMat.mul(localBox);
+
+      // run through objects and collide
+      for (S32 i = start; i<end; i++)
+      {
+         TSShapeInstance::MeshObjectInstance * meshInstance = &sI->mMeshObjects[i];
 
-      char readBuffer[1024];
+         if (od >= meshInstance->object->numMeshes)
+            continue;
 
-      stream->readString(readBuffer);
-      colisionMeshPrefix = StringTable->insert(readBuffer);
+         if (&meshInstance->getTransform() != previousMat)
+         {
+            // different node from before, set up for this node
+            previousMat = &meshInstance->getTransform();
+
+            if (previousMat != NULL)
+            {
+               mat.mul(initialMat, scaleMat);
+               mat.mul(*previousMat);
+
+               // Update our bounding box...
+               otherMat = mat;
+               otherMat.inverse();
+               localBox = bounds;
+               otherMat.mul(localBox);
+            }
+         }
+
+         // collide... note we pass the original mech transform
+         // here so that the convex data returned is in mesh space.
+         TSMesh * mesh = meshInstance->getMesh(od);
+         if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f && localBox.isOverlapped(mesh->getBounds()))
+            emitted |= buildMeshOpcode(mesh, *previousMat, localBox, c, list);
+         else
+            emitted |= false;
+      }
    }
-}
 
-void CollisionComponent::ownerTransformSet(MatrixF *mat)
-{
-   if (mPhysicsRep)
-      mPhysicsRep->setTransform(mOwner->getTransform());
+   return emitted;
 }
 
-void CollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
+bool CollisionComponent::buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat,
+   const Box3F &nodeBox, Convex *convex, Convex *list)
 {
-   prepCollision();
-}
+   /*PROFILE_SCOPE(MeshCollider_buildConvexOpcode);
 
-void CollisionComponent::prepCollision()
-{
-   if (!mOwner)
-      return;
+   // This is small... there is no win for preallocating it.
+   Opcode::AABBCollider opCollider;
+   opCollider.SetPrimitiveTests(true);
 
-   // Let the client know that the collision was updated
-   setMaskBits(ColliderMask);
+   // This isn't really needed within the AABBCollider as 
+   // we don't use temporal coherance... use a static to 
+   // remove the allocation overhead.
+   static Opcode::AABBCache opCache;
 
-   mOwner->disableCollision();
+   IceMaths::AABB opBox;
+   opBox.SetMinMax(Point(nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z),
+      Point(nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z));
+   Opcode::CollisionAABB opCBox(opBox);
 
-   if ((!PHYSICSMGR || mCollisionType == None) ||
-      (mOwnerRenderInterface == NULL && (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh)))
-      return;
+   if (!opCollider.Collide(opCache, opCBox, *mesh->mOptTree))
+      return false;
 
-   PhysicsCollision *colShape = NULL;
+   U32 cnt = opCollider.GetNbTouchedPrimitives();
+   const udword *idx = opCollider.GetTouchedPrimitives();
 
-   if (mCollisionType == Bounds)
+   Opcode::VertexPointers vp;
+   for (S32 i = 0; i < cnt; i++)
    {
-      MatrixF offset(true);
+      // First, check our active convexes for a potential match (and clean things
+      // up, too.)
+      const U32 curIdx = idx[i];
+
+      // See if the square already exists as part of the working set.
+      bool gotMatch = false;
+      CollisionWorkingList& wl = convex->getWorkingList();
+      for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
+      {
+         if (itr->mConvex->getType() != TSPolysoupConvexType)
+            continue;
 
-      if (mOwnerRenderInterface && mOwnerRenderInterface->getShape())
-         offset.setPosition(mOwnerRenderInterface->getShape()->center);
+         const MeshColliderPolysoupConvex *chunkc = static_cast<MeshColliderPolysoupConvex*>(itr->mConvex);
 
-      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 (chunkc->getObject() != mOwner)
+            continue;
 
-   if (colShape)
-   {
-      mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+         if (chunkc->mesh != mesh)
+            continue;
 
-      if (mPhysicsRep)
-      {
-         if (mBlockColliding)
-            mPhysicsRep->init(colShape, 0, 0, mOwner, mPhysicsWorld);
-         else
-            mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER, mOwner, mPhysicsWorld);
+         if (chunkc->idx != curIdx)
+            continue;
 
-         mPhysicsRep->setTransform(mOwner->getTransform());
+         // A match! Don't need to add it.
+         gotMatch = true;
+         break;
       }
-   }
 
-   mOwner->enableCollision();
+      if (gotMatch)
+         continue;
 
-   onCollisionChanged.trigger(colShape);
-}
+      // Get the triangle...
+      mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, idx[i]);
 
-void CollisionComponent::processTick()
-{
-   if (!isActive())
-      return;
+      Point3F a(vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z);
+      Point3F b(vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z);
+      Point3F c(vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z);
 
-   //ProcessTick is where our collision testing begins!
+      // Transform the result into object space!
+      meshToObjectMat.mulP(a);
+      meshToObjectMat.mulP(b);
+      meshToObjectMat.mulP(c);
 
-   //callback if we have a persisting contact
-   if (mContactInfo.contactObject)
-   {
-      if (mContactInfo.contactTimer > 0)
-      {
-         if (isMethod("updateContact"))
-            Con::executef(this, "updateContact");
+      //If we're not doing debug rendering on the client, then set up our convex list as normal
+      PlaneF p(c, b, a);
+      Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f);
 
-         if (mOwner->isMethod("updateContact"))
-            Con::executef(mOwner, "updateContact");
-      }
+      // Set up the convex...
+      MeshColliderPolysoupConvex *cp = new MeshColliderPolysoupConvex();
 
-      ++mContactInfo.contactTimer;
-   }
-   else if (mContactInfo.contactTimer != 0)
-      mContactInfo.clear();
-}
+      list->registerObject(cp);
+      convex->addToWorkingList(cp);
 
-void CollisionComponent::updatePhysics()
-{
-   
-}
+      cp->mesh = mesh;
+      cp->idx = curIdx;
+      cp->mObject = mOwner;
 
-PhysicsCollision* CollisionComponent::getCollisionData()
-{
-   if ((!PHYSICSMGR || mCollisionType == None) || mOwnerRenderInterface == NULL)
-      return NULL;
+      cp->normal = p;
+      cp->verts[0] = a;
+      cp->verts[1] = b;
+      cp->verts[2] = c;
+      cp->verts[3] = peak;
 
-   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!");
+      // Update the bounding box.
+      Box3F &bounds = cp->box;
+      bounds.minExtents.set(F32_MAX, F32_MAX, F32_MAX);
+      bounds.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX);
+
+      bounds.minExtents.setMin(a);
+      bounds.minExtents.setMin(b);
+      bounds.minExtents.setMin(c);
+      bounds.minExtents.setMin(peak);
+
+      bounds.maxExtents.setMax(a);
+      bounds.maxExtents.setMax(b);
+      bounds.maxExtents.setMax(c);
+      bounds.maxExtents.setMax(peak);
    }
 
-   return colShape;
+   return true;*/
+   return false;
 }
 
-bool CollisionComponent::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
+bool CollisionComponent::castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info)
 {
-   if (!mCollisionType == None)
+   // if dl==-1, nothing to do
+   //if (dl == -1 || !getShapeInstance())
+      return false;
+
+   /*TSShape *shape = getShapeInstance()->getShape();
+
+   AssertFatal(dl >= 0 && dl < shape->details.size(), "TSShapeInstance::castRayOpcode");
+
+   info->t = 100.f;
+
+   // get subshape and object detail
+   const TSDetail * detail = &shape->details[dl];
+   S32 ss = detail->subShapeNum;
+   if (ss < 0)
+      return false;
+
+   S32 od = detail->objectDetailNum;
+
+   // nothing emitted yet...
+   bool emitted = false;
+
+   const MatrixF* saveMat = NULL;
+   S32 start = shape->subShapeFirstObject[ss];
+   S32 end = shape->subShapeNumObjects[ss] + start;
+   if (start<end)
    {
-      if (mPhysicsWorld)
+      MatrixF mat;
+      const MatrixF * previousMat = &getShapeInstance()->mMeshObjects[start].getTransform();
+      mat = *previousMat;
+      mat.inverse();
+      Point3F localStart, localEnd;
+      mat.mulP(startPos, &localStart);
+      mat.mulP(endPos, &localEnd);
+
+      // run through objects and collide
+      for (S32 i = start; i<end; i++)
       {
-         return mPhysicsWorld->castRay(start, end, info, Point3F::Zero);
+         TSShapeInstance::MeshObjectInstance * meshInstance = &getShapeInstance()->mMeshObjects[i];
+
+         if (od >= meshInstance->object->numMeshes)
+            continue;
+
+         if (&meshInstance->getTransform() != previousMat)
+         {
+            // different node from before, set up for this node
+            previousMat = &meshInstance->getTransform();
+
+            if (previousMat != NULL)
+            {
+               mat = *previousMat;
+               mat.inverse();
+               mat.mulP(startPos, &localStart);
+               mat.mulP(endPos, &localEnd);
+            }
+         }
+
+         // collide...
+         TSMesh * mesh = meshInstance->getMesh(od);
+         if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f)
+         {
+            if (castRayMeshOpcode(mesh, localStart, localEnd, info, getShapeInstance()->mMaterialList))
+            {
+               saveMat = previousMat;
+               emitted = true;
+            }
+         }
       }
    }
 
-   return false;
+   if (emitted)
+   {
+      saveMat->mulV(info->normal);
+      info->point = endPos - startPos;
+      info->point *= info->t;
+      info->point += startPos;
+   }
+
+   return emitted;*/
 }
 
-PhysicsCollision* CollisionComponent::buildColShapes()
+static Point3F	texGenAxis[18] =
 {
-   PROFILE_SCOPE(CollisionComponent_buildColShapes);
+   Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0),
+   Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0),
+   Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1),
+   Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1),
+   Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1),
+   Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1)
+};
+
+bool CollisionComponent::castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials)
+{
+   Opcode::RayCollider ray;
+   Opcode::CollisionFaces cfs;
 
-   PhysicsCollision *colShape = NULL;
-   U32 surfaceKey = 0;
+   IceMaths::Point dir(e.x - s.x, e.y - s.y, e.z - s.z);
+   const F32 rayLen = dir.Magnitude();
+   IceMaths::Ray vec(Point(s.x, s.y, s.z), dir.Normalize());
 
-   TSShape* shape = mOwnerRenderInterface->getShape();
+   ray.SetDestination(&cfs);
+   ray.SetFirstContact(false);
+   ray.SetClosestHit(true);
+   ray.SetPrimitiveTests(true);
+   ray.SetCulling(true);
+   ray.SetMaxDist(rayLen);
 
-   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;
+   AssertFatal(ray.ValidateSettings() == NULL, "invalid ray settings");
 
-         // No mesh or no verts.... nothing to do.
-         TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
-         if (!mesh || mesh->mNumVerts == 0)
-            continue;
+   // Do collision.
+   bool safety = ray.Collide(vec, *mesh->mOptTree);
+   AssertFatal(safety, "CollisionComponent::castRayOpcode - no good ray collide!");
 
-         // Gather the mesh triangles.
-         polyList.clear();
-         mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
+   // If no hit, just skip out.
+   if (cfs.GetNbFaces() == 0)
+      return false;
 
-         // Create the collision shape if we haven't already.
-         if (!colShape)
-            colShape = PHYSICSMGR->createCollision();
+   // Got a hit!
+   AssertFatal(cfs.GetNbFaces() == 1, "bad");
+   const Opcode::CollisionFace &face = cfs.GetFaces()[0];
 
-         // Get the object space mesh transform.
-         MatrixF localXfm;
-         shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
+   // If the cast was successful let's check if the t value is less than what we had
+   // and toggle the collision boolean
+   // Stupid t... i prefer coffee
+   const F32 t = face.mDistance / rayLen;
 
-         colShape->addTriangleMesh(polyList.mVertexList.address(),
-            polyList.mVertexList.size(),
-            polyList.mIndexList.address(),
-            polyList.mIndexList.size() / 3,
-            localXfm);
-      }
+   if (t < 0.0f || t > 1.0f)
+      return false;
 
-      // Return what we built... if anything.
-      return colShape;
-   }
-   else if (mCollisionType == CollisionMesh)
+   if (t <= info->t)
    {
+      info->t = t;
+
+      // Calculate the normal.
+      Opcode::VertexPointers vp;
+      mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, face.mFaceID);
+
+      if (materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size())
+         info->material = materials->getMaterialInst(vp.MatIdx);
 
-      // Scan out the collision hulls...
-      //
-      // TODO: We need to support LOS collision for physics.
-      //
-      for (U32 i = 0; i < shape->details.size(); i++)
+      // Get the two edges.
+      IceMaths::Point baseVert = *vp.Vertex[0];
+      IceMaths::Point a = *vp.Vertex[1] - baseVert;
+      IceMaths::Point b = *vp.Vertex[2] - baseVert;
+
+      IceMaths::Point n;
+      n.Cross(a, b);
+      n.Normalize();
+
+      info->normal.set(n.x, n.y, n.z);
+
+      // generate UV coordinate across mesh based on 
+      // matching normals, this isn't done by default and is 
+      // primarily of interest in matching a collision point to 
+      // either a GUI control coordinate or finding a hit pixel in texture space
+      if (info->generateTexCoord)
       {
-         const TSShape::Detail &detail = shape->details[i];
-         const String &name = shape->names[detail.nameIndex];
+         baseVert = *vp.Vertex[0];
+         a = *vp.Vertex[1];
+         b = *vp.Vertex[2];
 
-         // Is this a valid collision detail.
-         if (!dStrStartsWith(name, colisionMeshPrefix) || detail.subShapeNum < 0)
-            continue;
+         Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z)
+            + face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z);
 
-         // 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;
+         U32 faces[1024];
+         U32 numFaces = 0;
+         for (U32 i = 0; i < mesh->mOptTree->GetMeshInterface()->GetNbTriangles(); i++)
+         {
+            if (i == face.mFaceID)
+            {
+               faces[numFaces++] = i;
+            }
+            else
+            {
+               IceMaths::Point n2;
+
+               mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, i);
+
+               baseVert = *vp.Vertex[0];
+               a = *vp.Vertex[1] - baseVert;
+               b = *vp.Vertex[2] - baseVert;
+               n2.Cross(a, b);
+               n2.Normalize();
+
+               F32 eps = .01f;
+               if (mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps)
+               {
+                  faces[numFaces++] = i;
+               }
+            }
+
+            if (numFaces == 1024)
+            {
+               // too many faces in this collision mesh for UV generation
+               return true;
+            }
+
+         }
+
+         Point3F min(F32_MAX, F32_MAX, F32_MAX);
+         Point3F max(-F32_MAX, -F32_MAX, -F32_MAX);
+
+         for (U32 i = 0; i < numFaces; i++)
+         {
+            mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, faces[i]);
+
+            for (U32 j = 0; j < 3; j++)
+            {
+               a = *vp.Vertex[j];
+
+               if (a.x < min.x)
+                  min.x = a.x;
+               if (a.y < min.y)
+                  min.y = a.y;
+               if (a.z < min.z)
+                  min.z = a.z;
+
+               if (a.x > max.x)
+                  max.x = a.x;
+               if (a.y > max.y)
+                  max.y = a.y;
+               if (a.z > max.z)
+                  max.z = a.z;
+
+            }
+
+         }
+
+         // slerp
+         Point3F s = ((max - min) - (facePoint - min)) / (max - min);
+
+         // compute axis
+         S32		bestAxis = 0;
+         F32      best = 0.f;
+
+         for (U32 i = 0; i < 6; i++)
+         {
+            F32 dot = mDot(info->normal, texGenAxis[i * 3]);
+            if (dot > best)
+            {
+               best = dot;
+               bestAxis = i;
+            }
+         }
+
+         Point3F xv = texGenAxis[bestAxis * 3 + 1];
+         Point3F yv = texGenAxis[bestAxis * 3 + 2];
+
+         S32 sv, tv;
+
+         if (xv.x)
+            sv = 0;
+         else if (xv.y)
+            sv = 1;
+         else
+            sv = 2;
+
+         if (yv.x)
+            tv = 0;
+         else if (yv.y)
+            tv = 1;
+         else
+            tv = 2;
+
+         // handle coord translation
+         if (bestAxis == 2 || bestAxis == 3)
+         {
+            S32 x = sv;
+            sv = tv;
+            tv = x;
+
+            if (yv.z < 0)
+               s[sv] = 1.f - s[sv];
+         }
+
+         if (bestAxis < 2)
+         {
+            if (yv.y < 0)
+               s[sv] = 1.f - s[sv];
+         }
 
-         for (S32 o = start; o < end; o++)
+         if (bestAxis > 3)
          {
-            const TSShape::Object &object = shape->objects[o];
-
-            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
+            s[sv] = 1.f - s[sv];
+            if (yv.z > 0)
+               s[tv] = 1.f - s[tv];
+
+         }
+
+         // done!
+         info->texCoord.set(s[sv], s[tv]);
+
+      }
+
+      return true;
    }
 
-   return colShape;
+   return false;
 }

+ 123 - 125
Engine/source/T3D/components/collision/collisionComponent.h

@@ -19,190 +19,188 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
+#pragma once
 
 #ifndef COLLISION_COMPONENT_H
 #define COLLISION_COMPONENT_H
 
-#ifndef __RESOURCE_H__
-#include "core/resource.h"
+#ifndef COMPONENT_H
+#include "T3D/components/component.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"
+
+#ifndef _CONVEX_H_
+#include "collision/convex.h"
 #endif
-#ifndef CORE_INTERFACES_H
-#include "T3D/components/coreInterfaces.h"
+#ifndef _COLLISION_H_
+#include "collision/collision.h"
 #endif
-#ifndef COLLISION_INTERFACES_H
-#include "T3D/components/collision/collisionInterfaces.h"
+#ifndef _EARLYOUTPOLYLIST_H_
+#include "collision/earlyOutPolyList.h"
 #endif
-#ifndef RENDER_COMPONENT_INTERFACE_H
-#include "T3D/components/render/renderComponentInterface.h"
+#ifndef _SIM_H_
+#include "console/sim.h"
 #endif
-#ifndef PHYSICS_COMPONENT_INTERFACE_H
-#include "T3D/components/physics/physicsComponentInterface.h"
+#ifndef _SCENECONTAINER_H_
+#include "scene/sceneContainer.h"
 #endif
 #ifndef _T3D_PHYSICSCOMMON_H_
 #include "T3D/physics/physicsCommon.h"
 #endif
+#ifndef PHYSICS_COMPONENT_H
+#include "T3D/components/physics/physicsComponent.h"
+#endif
 #ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
 #include "T3D/physics/physicsWorld.h"
 #endif
 
-class TSShapeInstance;
-class SceneRenderState;
-class CollisionComponent;
-class PhysicsBody;
-class PhysicsWorld;
+struct CollisionContactInfo
+{
+   bool contacted, move;
+   SceneObject *contactObject;
+   VectorF  idealContactNormal;
+   VectorF  contactNormal;
+   Point3F  contactPoint;
+   F32	   contactTime;
+   S32	   contactTimer;
+   BaseMatInstance *contactMaterial;
+
+   Vector<SceneObject*> overlapObjects;
+
+   void clear()
+   {
+      contacted=move=false; 
+      contactObject = NULL; 
+      contactNormal.set(0,0,0);
+      contactTime = 0.f;
+      contactTimer = 0;
+      idealContactNormal.set(0, 0, 1);
+      contactMaterial = NULL;
+      overlapObjects.clear();
+   }
+
+   CollisionContactInfo() { clear(); }
 
-class CollisionComponent : public Component,
-   public CollisionInterface,
-   public CastRayInterface
+};
+
+class CollisionComponent : public Component
 {
    typedef Component Parent;
+
 public:
-   enum MeshType
+	// 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 
    {
-      None = 0,            ///< No mesh
-      Bounds = 1,          ///< Bounding box of the shape
-      CollisionMesh = 2,   ///< Specifically designated collision meshes
-      VisibleMesh = 3      ///< Rendered mesh polygons
+      CollisionTimeout* next;
+      SceneObject* object;
+      U32 objectNumber;
+      SimTime expireTime;
+      VectorF vector;
    };
 
+   Signal< void( SceneObject* ) > onCollisionSignal;
+   Signal< void( SceneObject* ) > onContactSignal;
+
+protected:
    PhysicsWorld* mPhysicsWorld;
    PhysicsBody* mPhysicsRep;
 
-protected:
-   MeshType mCollisionType;
-   MeshType mDecalType;
-   MeshType mLOSType;
+   CollisionTimeout* mTimeoutList;
+   static CollisionTimeout* sFreeTimeoutList;
 
-   Vector<S32> mCollisionDetails;
-   Vector<S32> mLOSDetails;
+   CollisionList mCollisionList;
+   Vector<CollisionComponent*> mCollisionNotifyList;
 
-   StringTableEntry colisionMeshPrefix;
+   CollisionContactInfo mContactInfo;
 
-   RenderComponentInterface* mOwnerRenderInterface;
+   U32 CollisionMoveMask;
 
-   PhysicsComponentInterface* mOwnerPhysicsInterface;
+   bool mBlockColliding;
 
-   //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;
+   bool mCollisionInited;
 
-   enum
-   {
-      ColliderMask = Parent::NextFreeMask,
-   };
+   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:
    CollisionComponent();
    virtual ~CollisionComponent();
+
    DECLARE_CONOBJECT(CollisionComponent);
 
-   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
-   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+   //Setup
+   virtual void prepCollision() {};
 
-   virtual void componentAddedToOwner(Component *comp);
-   virtual void componentRemovedFromOwner(Component *comp);
-   virtual void ownerTransformSet(MatrixF *mat);
-   void targetShapeChanged(RenderComponentInterface* instanceInterface);
+   /// 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;
 
-   virtual void onComponentRemove();
-   virtual void onComponentAdd();
+   CollisionList *getCollisionList() { return &mCollisionList; }
 
-   virtual void checkDependencies();
+   void clearCollisionList() { mCollisionList.clear(); }
 
-   static void initPersistFields();
+   void clearCollisionNotifyList() { mCollisionNotifyList.clear(); }
 
-   void inspectPostApply();
+   Collision *getCollision(S32 col);
 
-   virtual void processTick();
+   CollisionContactInfo* getContactInfo() { return &mContactInfo; }
 
-   void prepCollision();
+	enum PublicConstants { 
+      CollisionTimeoutValue = 250
+   };
 
-   PhysicsCollision* buildColShapes();
+   bool doesBlockColliding() { return mBlockColliding; }
 
-   void updatePhysics();
+   /// handleCollisionList
+   /// This basically takes in a CollisionList and calls handleCollision for each.
+   void handleCollisionList(CollisionList &collisionList, VectorF velocity);
 
-   virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
+   /// 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 bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere){ return false; }
+   virtual bool checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start);
+   virtual bool updateCollisions(F32 time, VectorF vector, VectorF velocity);
+   virtual void updateWorkingCollisionSet(const U32 mask);
 
-   virtual PhysicsCollision* getCollisionData();
+   //
+   bool buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list);
+   bool buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat, const Box3F &bounds, Convex *convex, Convex *list);
 
-   //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();
-   }
+   bool castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info);
+   bool castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials);
 
-   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)));
+   virtual PhysicsCollision* getCollisionData() {
+      return nullptr;
    }
 
-   S32 getBestCollision(Point3F upVector)
+   virtual PhysicsBody *getPhysicsRep()
    {
-      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;
+      return mPhysicsRep;
    }
 
-   F32 getBestCollisionAngle(VectorF upVector)
-   {
-      S32 bestCol = getBestCollision(upVector);
+   void buildConvex(const Box3F& box, Convex* convex) {}
+   bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) { return false; }
 
-      if (bestCol == -1)
-         return 0;
+   //
+   Point3F getContactNormal();
+   bool hasContact();
+   S32 getCollisionCount();
+   Point3F getCollisionNormal(S32 collisionIndex);
+   F32 getCollisionAngle(S32 collisionIndex, Point3F upVector);
+   S32 getBestCollision(Point3F upVector);
+   F32 getBestCollisionAngle(VectorF upVector);
 
-      return getCollisionAngle(bestCol, upVector);
-   }
+   Signal< void(PhysicsCollision* collision) > onCollisionChanged;
 };
 
-typedef CollisionComponent::MeshType CollisionMeshMeshType;
-DefineEnumType(CollisionMeshMeshType);
-
-#endif // COLLISION_COMPONENT_H
+#endif

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

@@ -1,258 +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 "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; 
-}

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

@@ -1,167 +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 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

+ 1 - 1
Engine/source/T3D/components/collision/collisionTrigger.cpp

@@ -480,7 +480,7 @@ void CollisionTrigger::processTick(const Move* move)
          }
       }
 
-      if (!mTickCommand.isEmpty())
+      if (!mTickCommand.isEmpty() && mObjects.size() != 0)
          Con::evaluate(mTickCommand.c_str());
 
       //if (mObjects.size() != 0)

+ 122 - 0
Engine/source/T3D/components/collision/raycastColliderComponent.cpp

@@ -0,0 +1,122 @@
+#include "T3D/components/collision/raycastColliderComponent.h"
+#include "T3D/physics/physicsPlugin.h"
+
+IMPLEMENT_CO_DATABLOCK_V1(RaycastColliderComponent);
+
+RaycastColliderComponent::RaycastColliderComponent() :
+   mUseVelocity(false),
+   mOwnerPhysicsComponent(nullptr),
+   mRayDirection(VectorF::Zero),
+   mRayLength(1),
+   mPhysicsWorld(nullptr),
+   mOldPosition(Point3F::Zero),
+   mMask(-1)
+{
+}
+
+RaycastColliderComponent::~RaycastColliderComponent() 
+{
+
+}
+
+bool RaycastColliderComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   if (PHYSICSMGR)
+      mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
+
+   return true;
+}
+void RaycastColliderComponent::onRemove() 
+{
+   Parent::onRemove();
+}
+
+void RaycastColliderComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+void RaycastColliderComponent::onComponentAdd()
+{
+   PhysicsComponent* physComp = mOwner->getComponent<PhysicsComponent>();
+
+   if (physComp)
+   {
+      mOwnerPhysicsComponent = physComp;
+   }
+}
+
+void RaycastColliderComponent::onComponentRemove()
+{
+   mOwnerPhysicsComponent = nullptr;
+}
+
+void RaycastColliderComponent::componentAddedToOwner(Component *comp) 
+{
+   Parent::componentAddedToOwner(comp);
+
+   PhysicsComponent* physComp = dynamic_cast<PhysicsComponent*>(comp);
+   if (physComp)
+   {
+      mOwnerPhysicsComponent = physComp;
+   }
+}
+
+void RaycastColliderComponent::componentRemovedFromOwner(Component *comp)
+{
+   Parent::componentRemovedFromOwner(comp);
+
+   if (mOwnerPhysicsComponent != nullptr && mOwnerPhysicsComponent->getId() == comp->getId())
+   {
+      mOwnerPhysicsComponent = nullptr;
+   }
+}
+
+void RaycastColliderComponent::processTick() 
+{
+   Parent::processTick();
+
+   // Raycast the abstract PhysicsWorld if a PhysicsPlugin exists.
+   bool hit = false;
+
+   Point3F start = mOldPosition;
+   Point3F end;
+
+   if (mUseVelocity)
+   {
+      //our end is the new position
+      end = mOwner->getPosition();
+   }
+   else
+   {
+      end = start + (mRayDirection * mRayLength);
+   }
+
+   RayInfo rInfo;
+
+   if (mPhysicsWorld)
+      hit = mPhysicsWorld->castRay(start, end, &rInfo, Point3F::Zero);
+   else
+      hit = mOwner->getContainer()->castRay(start, end, mMask, &rInfo);
+
+   if (hit)
+   {
+
+   }
+
+   if (mUseVelocity)
+      mOldPosition = end;
+}
+
+void RaycastColliderComponent::interpolateTick(F32 dt) 
+{
+   Parent::interpolateTick(dt);
+}
+
+void RaycastColliderComponent::advanceTime(F32 dt) 
+{
+   Parent::advanceTime(dt);
+}

+ 46 - 0
Engine/source/T3D/components/collision/raycastColliderComponent.h

@@ -0,0 +1,46 @@
+#pragma once
+
+#include "T3D/components/collision/collisionComponent.h"
+#include "T3D/components/physics/physicsComponent.h"
+#include "T3D/physics/physicsWorld.h"
+
+class RaycastColliderComponent : public CollisionComponent
+{
+   typedef CollisionComponent Parent;
+
+   //If we're velocity based, we need a physics component on our owner to calculate the vel
+   bool mUseVelocity;
+   PhysicsComponent* mOwnerPhysicsComponent;
+
+   //If we're not using velocity, we'll just have a set direction and length we check against
+   VectorF mRayDirection;
+   F32 mRayLength;
+
+   PhysicsWorld *mPhysicsWorld;
+
+   Point3F mOldPosition;
+
+   U32 mMask;
+
+public:
+   DECLARE_CONOBJECT(RaycastColliderComponent);
+
+   RaycastColliderComponent();
+   ~RaycastColliderComponent();
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   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 processTick();
+   virtual void interpolateTick(F32 dt);
+   virtual void advanceTime(F32 dt);
+};

+ 607 - 0
Engine/source/T3D/components/collision/shapeCollisionComponent.cpp

@@ -0,0 +1,607 @@
+//-----------------------------------------------------------------------------
+// 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/shapeCollisionComponent.h"
+#include "T3D/components/collision/shapeCollisionComponent_ScriptBinding.h"
+#include "T3D/components/physics/physicsComponent.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(ShapeCollisionComponent,
+   "@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 ShapeCollisionComponentInstance()\n"
+   "{\n"
+   "   template = ShapeCollisionComponentTemplate;\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")
+{ ShapeCollisionComponent::None, "None", "No mesh data." },
+{ ShapeCollisionComponent::Bounds, "Bounds", "Bounding box of the shape." },
+{ ShapeCollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
+{ ShapeCollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." },
+EndImplementEnumType;
+
+//
+ShapeCollisionComponent::ShapeCollisionComponent() : CollisionComponent()
+{
+   mFriendlyName = "Shape Collision Component";
+
+   mFriendlyName = "Shape Collision";
+   mComponentType = "Collision";
+
+   mDescription = getDescriptionText("A stub component class that physics components should inherit from.");
+
+   mOwnerRenderInterface = NULL;
+   mOwnerPhysicsComp = NULL;
+
+   mBlockColliding = true;
+
+   mCollisionType = CollisionMesh;
+   mLOSType = CollisionMesh;
+   mDecalType = CollisionMesh;
+
+   colisionMeshPrefix = StringTable->insert("Collision");
+
+   CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
+      StaticShapeObjectType | VehicleObjectType |
+      VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
+
+   mAnimated = false;
+
+   mCollisionInited = false;
+}
+
+ShapeCollisionComponent::~ShapeCollisionComponent()
+{
+   for (S32 i = 0; i < mFields.size(); ++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CONOBJECT(ShapeCollisionComponent);
+
+void ShapeCollisionComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
+   if (renderInterface)
+   {
+      renderInterface->onShapeInstanceChanged.notify(this, &ShapeCollisionComponent::targetShapeChanged);
+      mOwnerRenderInterface = renderInterface;
+   }
+
+   //physicsInterface
+   PhysicsComponent *physicsComp = mOwner->getComponent<PhysicsComponent>();
+   if (physicsComp)
+   {
+      mOwnerPhysicsComp = physicsComp;
+   }
+   else
+   {
+      if (PHYSICSMGR)
+      {
+         mPhysicsRep = PHYSICSMGR->createBody();
+      }
+   }
+
+   prepCollision();
+}
+
+void ShapeCollisionComponent::onComponentRemove()
+{
+   SAFE_DELETE(mPhysicsRep);
+
+   mOwnerPhysicsComp = nullptr;
+
+   mCollisionInited = false;
+
+   Parent::onComponentRemove();
+}
+
+void ShapeCollisionComponent::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, &ShapeCollisionComponent::targetShapeChanged);
+      mOwnerRenderInterface = renderInterface;
+      prepCollision();
+   }
+
+   PhysicsComponent *physicsComp = dynamic_cast<PhysicsComponent*>(comp);
+   if (physicsComp)
+   {
+      if (mPhysicsRep)
+         SAFE_DELETE(mPhysicsRep);
+
+      mOwnerPhysicsComp = physicsComp;
+
+      prepCollision();
+   }
+}
+
+void ShapeCollisionComponent::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, &ShapeCollisionComponent::targetShapeChanged);
+      mOwnerRenderInterface = NULL;
+      prepCollision();
+   }
+
+   //physicsInterface
+   PhysicsComponent *physicsComp = dynamic_cast<PhysicsComponent*>(comp);
+   if (physicsComp)
+   {
+      mPhysicsRep = PHYSICSMGR->createBody();
+
+      mCollisionInited = false;
+
+      mOwnerPhysicsComp = nullptr;
+
+      prepCollision();
+   }
+}
+
+void ShapeCollisionComponent::checkDependencies()
+{
+}
+
+void ShapeCollisionComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addGroup("Collision");
+
+      addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, ShapeCollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, ShapeCollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, ShapeCollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, ShapeCollisionComponent),
+         "The type of mesh data to use for collision queries.");
+
+      addField("BlockCollisions", TypeBool, Offset(mBlockColliding, ShapeCollisionComponent), "");
+
+   endGroup("Collision");
+}
+
+void ShapeCollisionComponent::inspectPostApply()
+{
+   // Apply any transformations set in the editor
+   Parent::inspectPostApply();
+
+   if (isServerObject())
+   {
+      setMaskBits(ColliderMask);
+      prepCollision();
+   }
+}
+
+U32 ShapeCollisionComponent::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 ShapeCollisionComponent::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 ShapeCollisionComponent::ownerTransformSet(MatrixF *mat)
+{
+   /*bool isSrv = isServerObject();
+   if (mPhysicsRep && mCollisionInited)
+      mPhysicsRep->setTransform(mOwner->getTransform());*/
+}
+
+//Setup
+void ShapeCollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
+{
+   prepCollision();
+}
+
+void ShapeCollisionComponent::prepCollision()
+{
+   if (!mOwner)
+      return;
+
+   // Let the client know that the collision was updated
+   setMaskBits(ColliderMask);
+
+   mOwner->disableCollision();
+
+   if (mCollisionType == None)
+      return;
+
+   //Physics API
+   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());
+
+         mCollisionInited = true;
+      }
+   }
+
+   mOwner->enableCollision();
+
+   onCollisionChanged.trigger(colShape);
+}
+
+//Update
+void ShapeCollisionComponent::processTick()
+{
+   if (!isActive())
+      return;
+
+   //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 ShapeCollisionComponent::updatePhysics()
+{
+   
+}
+
+PhysicsCollision* ShapeCollisionComponent::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("ShapeCollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!");
+   }
+
+   return colShape;
+}
+
+bool ShapeCollisionComponent::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* ShapeCollisionComponent::buildColShapes()
+{
+   PROFILE_SCOPE(ShapeCollisionComponent_buildColShapes);
+
+   PhysicsCollision *colShape = NULL;
+   U32 surfaceKey = 0;
+
+   TSShape* shape = mOwnerRenderInterface->getShape();
+
+   if (shape == nullptr)
+      return nullptr;
+
+   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;
+}

+ 137 - 0
Engine/source/T3D/components/collision/shapeCollisionComponent.h

@@ -0,0 +1,137 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+#pragma once
+#ifndef SHAPE_COLLISION_COMPONENT_H
+#define SHAPE_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_COMPONENT_H
+#include "T3D/components/collision/collisionComponent.h"
+#endif
+#ifndef RENDER_COMPONENT_INTERFACE_H
+#include "T3D/components/render/renderComponentInterface.h"
+#endif
+
+class TSShapeInstance;
+class SceneRenderState;
+class ShapeCollisionComponent;
+class PhysicsBody;
+class PhysicsWorld;
+
+class ShapeCollisionComponent : public CollisionComponent
+{
+   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
+   };
+
+protected:
+   MeshType mCollisionType;
+   MeshType mDecalType;
+   MeshType mLOSType;
+
+   Vector<S32> mCollisionDetails;
+   Vector<S32> mLOSDetails;
+
+   StringTableEntry colisionMeshPrefix;
+
+   RenderComponentInterface* mOwnerRenderInterface;
+
+   PhysicsComponent* mOwnerPhysicsComp;
+
+   //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:
+   ShapeCollisionComponent();
+   virtual ~ShapeCollisionComponent();
+   DECLARE_CONOBJECT(ShapeCollisionComponent);
+
+   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();
+
+   //Setup
+   virtual void prepCollision();
+
+   //Updates
+   virtual void processTick();
+   
+   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();
+
+};
+
+typedef ShapeCollisionComponent::MeshType CollisionMeshMeshType;
+DefineEnumType(CollisionMeshMeshType);
+
+#endif // COLLISION_COMPONENT_H

+ 172 - 0
Engine/source/T3D/components/collision/shapeCollisionComponent_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/shapeCollisionComponent.h"
+#include "materials/baseMatInstance.h"
+
+DefineEngineMethod(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();
+}
+
+DefineEngineMethod(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;
+}
+
+DefineEngineMethod(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;
+}
+
+DefineEngineMethod(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;
+}
+
+DefineEngineMethod(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;
+}
+
+DefineEngineMethod(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;
+}
+
+DefineEngineMethod(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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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);
+}

+ 216 - 0
Engine/source/T3D/components/collision/simpleHitboxComponent.cpp

@@ -0,0 +1,216 @@
+#include "simpleHitboxComponent.h"
+
+IMPLEMENT_CO_NETOBJECT_V1(SimpleHitboxComponent);
+
+SimpleHitboxComponent::SimpleHitboxComponent() :
+   // location of head, torso, legs
+   mBoxHeadPercentage(0.85f),
+   mBoxTorsoPercentage(0.55f),
+   mBoxLeftPercentage(0),
+   mBoxRightPercentage(1),
+   mBoxBackPercentage(0),
+   mBoxFrontPercentage(1),
+   mIsProne(false)
+{
+}
+
+SimpleHitboxComponent::~SimpleHitboxComponent()
+{
+}
+
+void SimpleHitboxComponent::initPersistFields()
+{
+   addGroup("Hitbox");
+   addField("headPercentage", TypeF32, Offset(mBoxHeadPercentage, SimpleHitboxComponent), "");
+   addField("torsoPercentage", TypeF32, Offset(mBoxTorsoPercentage, SimpleHitboxComponent), "");
+   addField("leftPercentage", TypeF32, Offset(mBoxLeftPercentage, SimpleHitboxComponent), "");
+   addField("rightPercentage", TypeF32, Offset(mBoxRightPercentage, SimpleHitboxComponent), "");
+   addField("backPercentage", TypeF32, Offset(mBoxBackPercentage, SimpleHitboxComponent), "");
+   addField("frontPercentage", TypeF32, Offset(mBoxFrontPercentage, SimpleHitboxComponent), "");
+
+   addField("isProne", TypeF32, Offset(mIsProne, SimpleHitboxComponent), "");
+   endGroup("Hitbox");
+
+   Parent::initPersistFields();
+}
+
+void SimpleHitboxComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+}
+
+void SimpleHitboxComponent::componentAddedToOwner(Component *comp)
+{
+   Parent::componentAddedToOwner(comp);
+}
+
+void SimpleHitboxComponent::componentRemovedFromOwner(Component *comp)
+{
+   Parent::componentRemovedFromOwner(comp);
+}
+
+void SimpleHitboxComponent::processTick()
+{
+   Parent::processTick();
+}
+
+void SimpleHitboxComponent::interpolateTick(F32 dt)
+{
+   Parent::interpolateTick(dt);
+}
+
+void SimpleHitboxComponent::advanceTime(F32 dt)
+{
+   Parent::advanceTime(dt);
+}
+
+void SimpleHitboxComponent::getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad)
+{
+   Point3F newPoint;
+   mOwner->getWorldToObj().mulP(in_rPos, &newPoint);
+
+   Point3F boxSize = mOwner->getObjBox().getExtents();
+
+   F32 boxHeight = boxSize.z;
+   F32 pointHeight = newPoint.z;
+
+   if (mIsProne)
+      pointHeight = newPoint.y; //this assumes we're y-forward
+
+   F32 zTorso = mBoxTorsoPercentage;
+   F32 zHead = mBoxHeadPercentage;
+
+   zTorso *= boxHeight;
+   zHead *= boxHeight;
+
+   if (pointHeight <= zTorso)
+      out_rpVert = "legs";
+   else if (pointHeight <= zHead)
+      out_rpVert = "torso";
+   else
+      out_rpVert = "head";
+
+   if (dStrcmp(out_rpVert, "head") != 0)
+   {
+      if (newPoint.y >= 0.0f)
+      {
+         if (newPoint.x <= 0.0f)
+            out_rpQuad = "front_left";
+         else
+            out_rpQuad = "front_right";
+      }
+      else
+      {
+         if (newPoint.x <= 0.0f)
+            out_rpQuad = "back_left";
+         else
+            out_rpQuad = "back_right";
+      }
+   }
+   else
+   {
+      F32 backToFront = boxSize.x;
+      F32 leftToRight = boxSize.y;
+
+      F32 backPoint = backToFront * mBoxBackPercentage;
+      F32 frontPoint = backToFront * mBoxFrontPercentage;
+      F32 leftPoint = leftToRight * mBoxLeftPercentage;
+      F32 rightPoint = leftToRight * mBoxRightPercentage;
+
+      S32 index = 0;
+      if (newPoint.y < backPoint)
+         index += 0;
+      else if (newPoint.y >= frontPoint)
+         index += 3;
+      else
+         index += 6;
+
+      if (newPoint.x < leftPoint)
+         index += 0;
+      else if (newPoint.x >= rightPoint)
+         index += 1;
+      else
+         index += 2;
+
+      switch (index)
+      {
+         case 0: out_rpQuad = "left_back";      break;
+         case 1: out_rpQuad = "middle_back";    break;
+         case 2: out_rpQuad = "right_back";     break;
+         case 3: out_rpQuad = "left_middle";    break;
+         case 4: out_rpQuad = "middle_middle";  break;
+         case 5: out_rpQuad = "right_middle";   break;
+         case 6: out_rpQuad = "left_front";     break;
+         case 7: out_rpQuad = "middle_front";   break;
+         case 8: out_rpQuad = "right_front";    break;
+
+         default:
+            AssertFatal(0, "Bad non-tant index");
+      };
+   }
+}
+
+DefineEngineMethod(SimpleHitboxComponent, getDamageLocation, const char*, (Point3F pos), ,
+   "@brief Get the named damage location and modifier for a given world position.\n\n"
+
+   "the Player object can simulate different hit locations based on a pre-defined set "
+   "of PlayerData defined percentages.  These hit percentages divide up the Player's "
+   "bounding box into different regions.  The diagram below demonstrates how the various "
+   "PlayerData properties split up the bounding volume:\n\n"
+
+   "<img src=\"images/player_damageloc.png\">\n\n"
+
+   "While you may pass in any world position and getDamageLocation() will provide a best-fit "
+   "location, you should be aware that this can produce some interesting results.  For example, "
+   "any position that is above PlayerData::boxHeadPercentage will be considered a 'head' hit, even "
+   "if the world position is high in the sky.  Therefore it may be wise to keep the passed in point "
+   "to somewhere on the surface of, or within, the Player's bounding volume.\n\n"
+
+   "@note This method will not return an accurate location when the player is "
+   "prone or swimming.\n\n"
+
+   "@param pos A world position for which to retrieve a body region on this player.\n"
+
+   "@return a string containing two words (space separated strings), where the "
+   "first is a location and the second is a modifier.\n\n"
+
+   "Posible locations:<ul>"
+   "<li>head</li>"
+   "<li>torso</li>"
+   "<li>legs</li></ul>\n"
+
+   "Head modifiers:<ul>"
+   "<li>left_back</li>"
+   "<li>middle_back</li>"
+   "<li>right_back</li>"
+   "<li>left_middle</li>"
+   "<li>middle_middle</li>"
+   "<li>right_middle</li>"
+   "<li>left_front</li>"
+   "<li>middle_front</li>"
+   "<li>right_front</li></ul>\n"
+
+   "Legs/Torso modifiers:<ul>"
+   "<li>front_left</li>"
+   "<li>front_right</li>"
+   "<li>back_left</li>"
+   "<li>back_right</li></ul>\n"
+
+   "@see PlayerData::boxHeadPercentage\n"
+   "@see PlayerData::boxHeadFrontPercentage\n"
+   "@see PlayerData::boxHeadBackPercentage\n"
+   "@see PlayerData::boxHeadLeftPercentage\n"
+   "@see PlayerData::boxHeadRightPercentage\n"
+   "@see PlayerData::boxTorsoPercentage\n"
+)
+{
+   const char *buffer1;
+   const char *buffer2;
+
+   object->getDamageLocation(pos, buffer1, buffer2);
+
+   static const U32 bufSize = 128;
+   char *buff = Con::getReturnBuffer(bufSize);
+   dSprintf(buff, bufSize, "%s %s", buffer1, buffer2);
+   return buff;
+}

+ 39 - 0
Engine/source/T3D/components/collision/simpleHitboxComponent.h

@@ -0,0 +1,39 @@
+#pragma once
+
+#include "T3D/components/component.h"
+
+class SimpleHitboxComponent : public Component
+{
+   typedef Component Parent;
+
+   // location of head, torso, legs
+   F32 mBoxHeadPercentage;
+   F32 mBoxTorsoPercentage;
+
+   // damage locations
+   F32 mBoxLeftPercentage;
+   F32 mBoxRightPercentage;
+   F32 mBoxBackPercentage;
+   F32 mBoxFrontPercentage;
+
+   // Is our hitbox horizontal, usually due to being prone, swimming, etc
+   bool mIsProne;
+
+public:
+   SimpleHitboxComponent();
+   ~SimpleHitboxComponent();
+
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual void processTick();
+   virtual void interpolateTick(F32 dt);
+   virtual void advanceTime(F32 dt);
+
+   void getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad);
+
+   DECLARE_CONOBJECT(SimpleHitboxComponent);
+};

+ 7 - 1
Engine/source/T3D/components/component.cpp

@@ -456,7 +456,7 @@ void Component::checkComponentFieldModified(const char* slotName, const char* ne
 //////////////////////////////////////////////////////////////////////////
 
 //////////////////////////////////////////////////////////////////////////
-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 */)
+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 */, const char* customLabel /* = ""*/)
 {
    StringTableEntry stFieldName = StringTable->insert(fieldName);
 
@@ -467,6 +467,12 @@ void Component::addComponentField(const char *fieldName, const char *desc, const
    }
 
    ComponentField field;
+
+   if (customLabel != "")
+      field.mFieldLabel = customLabel;
+   else
+      field.mFieldLabel = stFieldName;
+
    field.mFieldName = stFieldName;
 
    //find the field type

+ 4 - 1
Engine/source/T3D/components/component.h

@@ -20,6 +20,8 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
+#pragma once
+
 #ifndef COMPONENT_H
 #define COMPONENT_H
 
@@ -44,6 +46,7 @@ class Namespace;
 
 struct ComponentField
 {
+   StringTableEntry mFieldLabel;
    StringTableEntry mFieldName;
    StringTableEntry mFieldDescription;
 
@@ -145,7 +148,7 @@ public:
    /// @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);
+   void addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue = NULL, const char *userData = NULL, bool hidden = false, const char* customLabel = "");
 
    /// Returns the number of ComponentField's on this template
    inline S32 getComponentFieldCount() { return mFields.size(); };

+ 149 - 0
Engine/source/T3D/components/game/controlObjectComponent.cpp

@@ -0,0 +1,149 @@
+//-----------------------------------------------------------------------------
+// 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/controlObjectComponent.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+ControlObjectComponent::ControlObjectComponent() : Component(),
+   mOwnerConnection(nullptr),
+   mOwnerConnectionId(0)
+{
+   mFriendlyName = "Control Object";
+   mComponentType = "Game";
+
+   mDescription = getDescriptionText("Allows owner entity to be controlled by a client.");
+}
+
+ControlObjectComponent::~ControlObjectComponent()
+{
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(ControlObjectComponent);
+
+bool ControlObjectComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void ControlObjectComponent::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 ControlObjectComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   if (mOwnerConnection && mOwnerConnection->getControlObject() == nullptr)
+   {
+      mOwnerConnection->setControlObject(mOwner);
+      mOwnerConnectionId = mOwnerConnection->getId();
+   }
+}
+
+void ControlObjectComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+
+   if (mOwnerConnection)
+   {
+      mOwnerConnection->setControlObject(nullptr);
+      mOwnerConnectionId = 0;
+   }
+}
+
+void ControlObjectComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField("clientOwner", TypeS32, Offset(mOwnerConnectionId, ControlObjectComponent), "Client connection ID");
+}
+
+void ControlObjectComponent::setConnectionControlObject(GameConnection* conn)
+{
+   if (conn)
+   {
+      if (conn->getControlObject() == nullptr)
+      {
+         conn->setControlObject(mOwner);
+         mOwnerConnectionId = conn->getId();
+      }
+      else
+      {
+         //Inform the old control object it's no longer in control?
+         conn->setControlObject(mOwner);
+         mOwnerConnectionId = conn->getId();
+      }
+   }
+}
+
+void ControlObjectComponent::onClientConnect(GameConnection* conn)
+{
+   if (conn && conn->getControlObject() == nullptr)
+   {
+      conn->setControlObject(mOwner);
+      mOwnerConnectionId = conn->getId();
+   }
+}
+
+void ControlObjectComponent::onClientDisconnect(GameConnection* conn)
+{
+   if (conn && conn->getControlObject() == mOwner)
+   {
+      conn->setControlObject(nullptr);
+      mOwnerConnectionId = 0;
+   }
+}
+
+DefineEngineMethod(ControlObjectComponent, onClientConnect, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
+"Triggers a signal call to all components for a certain function.")
+{
+   if (conn == nullptr)
+      return;
+
+   object->onClientConnect(conn);
+}
+
+DefineEngineMethod(ControlObjectComponent, onClientDisconnect, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
+   "Triggers a signal call to all components for a certain function.")
+{
+   if (conn == nullptr)
+      return;
+
+   object->onClientDisconnect(conn);
+}
+
+DefineEngineMethod(ControlObjectComponent, setConnectionControlObject, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
+   "Triggers a signal call to all components for a certain function.")
+{
+   if (conn == nullptr)
+      return;
+
+   object->setConnectionControlObject(conn);
+}

+ 30 - 0
Engine/source/T3D/components/game/controlObjectComponent.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include "T3D/components/component.h"
+
+#include "T3D/gameBase/gameConnection.h"
+
+class ControlObjectComponent : public Component
+{
+   typedef Component Parent;
+
+   GameConnection* mOwnerConnection;
+   S32 mOwnerConnectionId;
+
+public:
+   ControlObjectComponent();
+   ~ControlObjectComponent();
+
+   DECLARE_CONOBJECT(ControlObjectComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   void onClientConnect(GameConnection* conn);
+   void onClientDisconnect(GameConnection* conn);
+   void setConnectionControlObject(GameConnection* conn);
+};

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

@@ -0,0 +1,359 @@
+#include "T3D/components/game/followPathComponent.h"
+
+IMPLEMENT_CO_DATABLOCK_V1(FollowPathComponent);
+
+IMPLEMENT_CALLBACK(FollowPathComponent, onNode, void, (S32 node), (node),
+   "A script callback that indicates the path camera has arrived at a specific node in its path.  Server side only.\n"
+   "@param Node Unique ID assigned to this node.\n");
+
+FollowPathComponent::FollowPathComponent() 
+{
+   delta.time = 0;
+   delta.timeVec = 0;
+
+   //mDataBlock = 0;
+   mState = Forward;
+   mNodeBase = 0;
+   mNodeCount = 0;
+   mPosition = 0;
+   mTarget = 0;
+   mTargetSet = false;
+}
+
+FollowPathComponent::~FollowPathComponent() 
+{
+
+}
+
+bool FollowPathComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   /*// Initialize from the current transform.
+   if (!mNodeCount) 
+   {
+      QuatF rot(getTransform());
+      Point3F pos = getPosition();
+      mSpline.removeAll();
+      mSpline.push_back(new CameraSpline::Knot(pos, rot, 1,
+         CameraSpline::Knot::NORMAL, CameraSpline::Knot::SPLINE));
+      mNodeCount = 1;
+   }
+
+   //
+   mObjBox.maxExtents = mObjScale;
+   mObjBox.minExtents = mObjScale;
+   mObjBox.minExtents.neg();
+   resetWorldBox();*/
+
+   return true;
+}
+void FollowPathComponent::onRemove() 
+{
+   Parent::onRemove();
+}
+
+void FollowPathComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+void FollowPathComponent::componentAddedToOwner(Component *comp) 
+{
+   Parent::componentAddedToOwner(comp);
+}
+
+void FollowPathComponent::componentRemovedFromOwner(Component *comp)
+{
+   Parent::componentRemovedFromOwner(comp);
+}
+
+void FollowPathComponent::processTick() 
+{
+   Parent::processTick();
+
+   // Move to new time
+   advancePosition(TickMs);
+
+   // Set new position
+   MatrixF mat;
+   interpolateMat(mPosition, &mat);
+   mOwner->setTransform(mat);
+
+   mOwner->updateContainer();
+}
+
+void FollowPathComponent::interpolateTick(F32 dt) 
+{
+   Parent::interpolateTick(dt);
+
+   MatrixF mat;
+   interpolateMat(delta.time + (delta.timeVec * dt), &mat);
+   mOwner->setRenderTransform(mat);
+}
+
+void FollowPathComponent::interpolateMat(F32 pos, MatrixF* mat)
+{
+   /*CameraSpline::Knot knot;
+   mSpline.value(pos - mNodeBase, &knot);
+   knot.mRotation.setMatrix(mat);
+   mat->setPosition(knot.mPosition);*/
+}
+
+void FollowPathComponent::advanceTime(F32 dt) 
+{
+   Parent::advanceTime(dt);
+}
+
+void FollowPathComponent::advancePosition(S32 ms)
+{
+   /*delta.timeVec = mPosition;
+
+   // Advance according to current speed
+   if (mState == Forward) {
+      mPosition = mSpline.advanceTime(mPosition - mNodeBase, ms);
+      if (mPosition > F32(mNodeCount - 1))
+         mPosition = F32(mNodeCount - 1);
+      mPosition += (F32)mNodeBase;
+      if (mTargetSet && mPosition >= mTarget) {
+         mTargetSet = false;
+         mPosition = mTarget;
+         mState = Stop;
+      }
+   }
+   else
+      if (mState == Backward) {
+         mPosition = mSpline.advanceTime(mPosition - mNodeBase, -ms);
+         if (mPosition < 0)
+            mPosition = 0;
+         mPosition += mNodeBase;
+         if (mTargetSet && mPosition <= mTarget) {
+            mTargetSet = false;
+            mPosition = mTarget;
+            mState = Stop;
+         }
+      }
+
+   // Script callbacks
+   if (int(mPosition) != int(delta.timeVec))
+      onNode(int(mPosition));
+
+   // Set frame interpolation
+   delta.time = mPosition;
+   delta.timeVec -= mPosition;*/
+}
+
+
+//----------------------------------------------------------------------------
+
+/*void FollowPathComponent::getCameraTransform(F32* pos, MatrixF* mat)
+{
+   // Overide the ShapeBase method to skip all the first/third person support.
+   getRenderEyeTransform(mat);
+
+   // Apply Camera FX.
+   mat->mul(gCamFXMgr.getTrans());
+}*/
+
+
+//----------------------------------------------------------------------------
+
+void FollowPathComponent::setPosition(F32 pos)
+{
+   mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1));
+   MatrixF mat;
+   interpolateMat(mPosition, &mat);
+   mOwner->setTransform(mat);
+   setMaskBits(PositionMask);
+}
+
+void FollowPathComponent::setTarget(F32 pos)
+{
+   mTarget = pos;
+   mTargetSet = true;
+   if (mTarget > mPosition)
+      mState = Forward;
+   else
+      if (mTarget < mPosition)
+         mState = Backward;
+      else {
+         mTargetSet = false;
+         mState = Stop;
+      }
+   setMaskBits(TargetMask | StateMask);
+}
+
+void FollowPathComponent::setState(State s)
+{
+   mState = s;
+   setMaskBits(StateMask);
+}
+
+
+//-----------------------------------------------------------------------------
+
+void FollowPathComponent::reset(F32 speed)
+{
+   /*CameraSpline::Knot *knot = new CameraSpline::Knot;
+   mSpline.value(mPosition - mNodeBase, knot);
+   if (speed)
+      knot->mSpeed = speed;
+   mSpline.removeAll();
+   mSpline.push_back(knot);
+
+   mNodeBase = 0;
+   mNodeCount = 1;
+   mPosition = 0;
+   mTargetSet = false;
+   mState = Forward;
+   setMaskBits(StateMask | PositionMask | WindowMask | TargetMask);*/
+}
+
+/*void FollowPathComponent::pushBack(CameraSpline::Knot *knot)
+{
+   // Make room at the end
+   if (mNodeCount == NodeWindow) {
+      delete mSpline.remove(mSpline.getKnot(0));
+      mNodeBase++;
+   }
+   else
+      mNodeCount++;
+
+   // Fill in the new node
+   mSpline.push_back(knot);
+   setMaskBits(WindowMask);
+
+   // Make sure the position doesn't fall off
+   if (mPosition < mNodeBase) {
+      mPosition = (F32)mNodeBase;
+      setMaskBits(PositionMask);
+   }
+}
+
+void FollowPathComponent::pushFront(CameraSpline::Knot *knot)
+{
+   // Make room at the front
+   if (mNodeCount == NodeWindow)
+      delete mSpline.remove(mSpline.getKnot(mNodeCount));
+   else
+      mNodeCount++;
+   mNodeBase--;
+
+   // Fill in the new node
+   mSpline.push_front(knot);
+   setMaskBits(WindowMask);
+
+   // Make sure the position doesn't fall off
+   if (mPosition > F32(mNodeBase + (NodeWindow - 1)))
+   {
+      mPosition = F32(mNodeBase + (NodeWindow - 1));
+      setMaskBits(PositionMask);
+   }
+}*/
+
+void FollowPathComponent::popFront()
+{
+   /*if (mNodeCount < 2)
+      return;
+
+   // Remove the first node. Node base and position are unaffected.
+   mNodeCount--;
+   delete mSpline.remove(mSpline.getKnot(0));
+
+   if (mPosition > 0)
+      mPosition--;*/
+}
+
+
+//----------------------------------------------------------------------------
+
+void FollowPathComponent::onNode(S32 node)
+{
+   //if (!isGhost())
+   //   onNode_callback(node);
+
+}
+
+/*U32 FollowPathComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   Parent::packUpdate(con, mask, stream);
+
+   if (stream->writeFlag(mask & StateMask))
+      stream->writeInt(mState, StateBits);
+
+   if (stream->writeFlag(mask & PositionMask))
+      stream->write(mPosition);
+
+   if (stream->writeFlag(mask & TargetMask))
+      if (stream->writeFlag(mTargetSet))
+         stream->write(mTarget);
+
+   if (stream->writeFlag(mask & WindowMask)) {
+      stream->write(mNodeBase);
+      stream->write(mNodeCount);
+      for (S32 i = 0; i < mNodeCount; i++) {
+         CameraSpline::Knot *knot = mSpline.getKnot(i);
+         mathWrite(*stream, knot->mPosition);
+         mathWrite(*stream, knot->mRotation);
+         stream->write(knot->mSpeed);
+         stream->writeInt(knot->mType, CameraSpline::Knot::NUM_TYPE_BITS);
+         stream->writeInt(knot->mPath, CameraSpline::Knot::NUM_PATH_BITS);
+      }
+   }
+
+   // The rest of the data is part of the control object packet update.
+   // If we're controlled by this client, we don't need to send it.
+   if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
+      return 0;
+
+   return 0;
+}
+
+void FollowPathComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   // StateMask
+   if (stream->readFlag())
+      mState = stream->readInt(StateBits);
+
+   // PositionMask
+   if (stream->readFlag())
+   {
+      stream->read(&mPosition);
+      delta.time = mPosition;
+      delta.timeVec = 0;
+   }
+
+   // TargetMask
+   if (stream->readFlag())
+   {
+      mTargetSet = stream->readFlag();
+      if (mTargetSet)
+         stream->read(&mTarget);
+   }
+
+   // WindowMask
+   if (stream->readFlag())
+   {
+      mSpline.removeAll();
+      stream->read(&mNodeBase);
+      stream->read(&mNodeCount);
+      for (S32 i = 0; i < mNodeCount; i++)
+      {
+         CameraSpline::Knot *knot = new CameraSpline::Knot();
+         mathRead(*stream, &knot->mPosition);
+         mathRead(*stream, &knot->mRotation);
+         stream->read(&knot->mSpeed);
+         knot->mType = (CameraSpline::Knot::Type)stream->readInt(CameraSpline::Knot::NUM_TYPE_BITS);
+         knot->mPath = (CameraSpline::Knot::Path)stream->readInt(CameraSpline::Knot::NUM_PATH_BITS);
+         mSpline.push_back(knot);
+      }
+   }
+
+   // Controlled by the client?
+   if (stream->readFlag())
+      return;
+
+}*/

+ 82 - 0
Engine/source/T3D/components/game/followPathComponent.h

@@ -0,0 +1,82 @@
+#pragma once
+
+#include "T3D/components/component.h"
+
+class FollowPathComponent : public Component
+{
+   typedef Component Parent;
+
+   enum State {
+      Forward,
+      Backward,
+      Stop,
+      StateBits = 3
+   };
+
+   enum MaskBits {
+      WindowMask = Parent::NextFreeMask,
+      PositionMask = Parent::NextFreeMask + 1,
+      TargetMask = Parent::NextFreeMask + 2,
+      StateMask = Parent::NextFreeMask + 3,
+      NextFreeMask = Parent::NextFreeMask << 1
+   };
+
+   struct StateDelta {
+      F32 time;
+      F32 timeVec;
+   };
+   StateDelta delta;
+
+   enum Constants {
+      NodeWindow = 128    // Maximum number of active nodes
+   };
+
+   //
+   //PathCameraData* mDataBlock;
+   //CameraSpline mSpline;
+   S32 mNodeBase;
+   S32 mNodeCount;
+   F32 mPosition;
+   S32 mState;
+   F32 mTarget;
+   bool mTargetSet;
+
+   void interpolateMat(F32 pos, MatrixF* mat);
+   void advancePosition(S32 ms);
+
+public:
+   DECLARE_CONOBJECT(FollowPathComponent);
+
+   FollowPathComponent();
+   ~FollowPathComponent();
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   //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 processTick();
+   virtual void interpolateTick(F32 dt);
+   virtual void advanceTime(F32 dt);
+
+   //
+   //void onEditorEnable();
+   //void onEditorDisable();
+
+   void onNode(S32 node);
+
+   void reset(F32 speed = 1);
+   //void pushFront(CameraSpline::Knot *knot);
+   //void pushBack(CameraSpline::Knot *knot);
+   void popFront();
+
+   void setPosition(F32 pos);
+   void setTarget(F32 pos);
+   void setState(State s);
+
+   DECLARE_CALLBACK(void, onNode, (S32 node));
+};

+ 171 - 0
Engine/source/T3D/components/game/interactComponent.cpp

@@ -0,0 +1,171 @@
+//-----------------------------------------------------------------------------
+// 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/interactComponent.h"
+#include "scene/sceneContainer.h"
+#include "T3D/components/game/interactableComponent.h"
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+InteractComponent::InteractComponent() : Component(),
+   mUseRaycastInteract(true),
+   mUseRenderedRaycast(false),
+   mUseRadiusInteract(true),
+   mInteractRadius(1.5),
+   mInteractRayDist(1.5),
+   mUseNaturalReach(false)
+{
+   mFriendlyName = "Interact";
+   mComponentType = "Game";
+
+   mDescription = getDescriptionText("Allows owner entity interact.");
+}
+
+InteractComponent::~InteractComponent()
+{
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(InteractComponent);
+
+bool InteractComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void InteractComponent::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 InteractComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+}
+
+void InteractComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+}
+
+void InteractComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+void InteractComponent::processTick()
+{
+   if (isClientObject())
+      return; //this shouldn't happen
+
+   //First, if we're doing rays, try that as if we get a hit, we can always assume that's the "correct" option
+   if (mUseRaycastInteract)
+   {
+      mOwner->disableCollision();
+
+      Point3F start = mOwner->getPosition();
+      Point3F end = mOwner->getTransform().getForwardVector() * mInteractRayDist + start;
+
+      RayInfo rinfo;
+      S32 ret = 0;
+
+      if (mUseRenderedRaycast)
+      {
+         rinfo.generateTexCoord = true;
+         if (gServerContainer.castRayRendered(mOwner->getPosition(), end, EntityObjectType, &rinfo) == true)
+            ret = rinfo.object->getId();
+      }
+      else
+      {
+         if (gServerContainer.castRay(mOwner->getPosition(), end, EntityObjectType, &rinfo) == true)
+            ret = rinfo.object->getId();
+      }
+
+      mOwner->enableCollision();
+
+      Entity* hitEntity = static_cast<Entity*>(rinfo.object);
+
+      if (hitEntity)
+      {
+         //call on that badboy!
+         InteractableComponent* iComp = hitEntity->getComponent<InteractableComponent>();
+
+         if (iComp)
+         {
+            iComp->interact(this, rinfo);
+            return;
+         }
+      }
+   }
+
+   //If not using rays or no hit, then do the radius search if we have it
+   if (mUseRadiusInteract)
+   {
+      gServerContainer.initRadiusSearch(mOwner->getPosition(), mInteractRadius, EntityObjectType);
+
+      F32 lastBestDist = 9999;
+      F32 lastBestWeight = 0;
+      Entity* bestFitEntity = nullptr;
+
+      Entity* e = static_cast<Entity*>(gServerContainer.containerSearchNextObject());
+
+      while (e != nullptr)
+      {
+         InteractableComponent* iComp = e->getComponent<InteractableComponent>();
+
+         if (iComp != nullptr)
+         {
+            F32 weight = iComp->getWeight();
+            VectorF distVec = e->getPosition() - mOwner->getPosition();
+            F32 dist = distVec.len();
+
+            //If the weight is better, always pick it
+            if (weight > lastBestWeight)
+            {
+               lastBestDist = dist;
+               lastBestWeight = weight;
+               bestFitEntity = e;
+            }
+            //Otherwise, if the weight is matched and the distance is closer, pick that
+            else if (weight >= lastBestWeight && lastBestDist < dist)
+            {
+               lastBestWeight = weight;
+               bestFitEntity = e;
+            }
+         }
+
+         e = static_cast<Entity*>(gServerContainer.containerSearchNextObject()); //loop 'round
+      }
+
+      if (bestFitEntity)
+      {
+         //call on that badboy!
+         InteractableComponent* iComp = bestFitEntity->getComponent<InteractableComponent>();
+
+         iComp->interact(this);
+      }
+   }
+}

+ 34 - 0
Engine/source/T3D/components/game/interactComponent.h

@@ -0,0 +1,34 @@
+#pragma once
+
+#include "T3D/components/component.h"
+
+
+class InteractComponent : public Component
+{
+   typedef Component Parent;
+
+   bool mUseRaycastInteract;
+   bool mUseRenderedRaycast;
+   bool mUseRadiusInteract;
+
+   F32 mInteractRadius;
+   F32 mInteractRayDist;
+
+   //Adjusts the length of the ray based on the idea of further reach if you look down because of crouching
+   bool mUseNaturalReach;
+
+public:
+   InteractComponent();
+   ~InteractComponent();
+
+   DECLARE_CONOBJECT(InteractComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   virtual void processTick();
+};

+ 94 - 0
Engine/source/T3D/components/game/interactableComponent.cpp

@@ -0,0 +1,94 @@
+//-----------------------------------------------------------------------------
+// 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/InteractableComponent.h"
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+InteractableComponent::InteractableComponent() : Component(),
+   mInteractableWeight(1)
+{
+   mFriendlyName = "Interactable";
+   mComponentType = "Game";
+
+   mDescription = getDescriptionText("Allows owner entity to be interacted with.");
+}
+
+InteractableComponent::~InteractableComponent()
+{
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(InteractableComponent);
+
+bool InteractableComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void InteractableComponent::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 InteractableComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+}
+
+void InteractableComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+}
+
+void InteractableComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField("interactableWeight", TypeF32, Offset(mInteractableWeight, InteractableComponent), "Controls importance values when using radius mode for interaction");
+}
+
+void InteractableComponent::interact(InteractComponent* interactor)
+{
+   if (interactor != nullptr)
+   {
+      mOwner->notifyComponents("onInteract", interactor->getIdString());
+
+      if(isMethod("onInteract"))
+         Con::executef(this, "onInteract", interactor);
+   }
+}
+
+void InteractableComponent::interact(InteractComponent* interactor, RayInfo rayInfo)
+{
+   if (interactor != nullptr)
+   {
+      mOwner->notifyComponents("onInteract", interactor->getIdString());
+
+      if (isMethod("onInteract"))
+         Con::executef(this, "onInteract", interactor);
+   }
+}

+ 30 - 0
Engine/source/T3D/components/game/interactableComponent.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include "T3D/components/component.h"
+#include "T3D/components/game/interactComponent.h"
+
+class InteractableComponent : public Component
+{
+   typedef Component Parent;
+
+   //Controls importance values when using radius mode for interaction
+   F32 mInteractableWeight;
+
+public:
+   InteractableComponent();
+   ~InteractableComponent();
+
+   DECLARE_CONOBJECT(InteractableComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
+
+   void interact(InteractComponent* interactor);
+   void interact(InteractComponent* interactor, RayInfo rayInfo);
+
+   F32 getWeight() { return mInteractableWeight; }
+};

+ 20 - 20
Engine/source/T3D/components/game/triggerComponent.cpp

@@ -11,7 +11,7 @@
 #include "console/engineAPI.h"
 #include "sim/netConnection.h"
 #include "T3D/gameBase/gameConnection.h"
-#include "T3D/components/coreInterfaces.h"
+//#include "T3D/components/coreInterfaces.h"
 #include "math/mathUtils.h"
 #include "collision/concretePolyList.h"
 #include "collision/clippedPolyList.h"
@@ -94,21 +94,21 @@ void TriggerComponent::onComponentAdd()
 {
    Parent::onComponentAdd();
 
-   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+   CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
 
-   if(colInt)
+   if(colComp)
    {
-      colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
+      colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
    }
 }
 
 void TriggerComponent::onComponentRemove()
 {
-   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+   CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
 
-   if(colInt)
+   if(colComp)
    {
-      colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
+      colComp->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
    }
 
    Parent::onComponentRemove();
@@ -119,11 +119,11 @@ void TriggerComponent::componentAddedToOwner(Component *comp)
    if (comp->getId() == getId())
       return;
 
-   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+   CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
 
-   if (colInt)
+   if (colComp)
    {
-      colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
+      colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
    }
 }
 
@@ -132,11 +132,11 @@ void TriggerComponent::componentRemovedFromOwner(Component *comp)
    if (comp->getId() == getId()) //?????????
       return;
 
-   CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
+   CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
 
-   if (colInt)
+   if (colComp)
    {
-      colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
+      colComp->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
    }
 }
 
@@ -217,19 +217,19 @@ bool TriggerComponent::testObject(SceneObject* enter)
          return false;
 
       //check if the entity has a collision shape
-      CollisionInterface *cI = enterEntity->getComponent<CollisionInterface>();
-      if (cI)
+      CollisionComponent *colComp = enterEntity->getComponent<CollisionComponent>();
+      if (colComp)
       {
-         cI->buildPolyList(PLC_Collision, &mClippedList, mOwner->getWorldBox(), sphere);
+         colComp->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>();
+            CollisionComponent *myColComp = mOwner->getComponent<CollisionComponent>();
 
             //wait, how would we NOT have this?
-            if (myCI)
+            if (myColComp)
             {
                //anywho, build our list and then we'll check intersections
                ClippedPolyList myList;
@@ -238,7 +238,7 @@ bool TriggerComponent::testObject(SceneObject* enter)
                myList.setTransform(&ownerTransform, mOwner->getScale());
                myList.setObject(mOwner);
 
-               myCI->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
+               myColComp->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
             }
          }
       }
@@ -354,4 +354,4 @@ DefineEngineMethod( TriggerComponent, visualizeFrustums, void,
                    "@return true if successful, false if failed (objB is not valid)" )
 {
    object->visualizeFrustums(renderTime);
-}
+}

+ 2 - 2
Engine/source/T3D/components/game/triggerComponent.h

@@ -13,8 +13,8 @@
 #include "T3D/entity.h"
 #endif
 
-#ifndef _COLLISION_INTERFACES_H_
-#include "T3D/components/collision/collisionInterfaces.h"
+#ifndef COLLISION_COMPONENT_H
+#include "T3D/components/collision/collisionComponent.h"
 #endif
 
 //////////////////////////////////////////////////////////////////////////

+ 94 - 86
Engine/source/T3D/components/physics/physicsBehavior.cpp → Engine/source/T3D/components/physics/physicsComponent.cpp

@@ -20,7 +20,7 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
-#include "T3D/components/physics/physicsBehavior.h"
+#include "T3D/components/physics/physicsComponent.h"
 #include "platform/platform.h"
 #include "console/consoleTypes.h"
 #include "core/util/safeDelete.h"
@@ -37,6 +37,8 @@
 #include "T3D/containerQuery.h"
 #include "math/mathIO.h"
 
+#include "T3D/physics/physicsPlugin.h"
+
 //////////////////////////////////////////////////////////////////////////
 // Constructor/Destructor
 //////////////////////////////////////////////////////////////////////////
@@ -47,6 +49,11 @@ PhysicsComponent::PhysicsComponent() : Component()
    addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
    addComponentField("mass", "The mass of the object", "float", "1", "");
 
+   mFriendlyName = "Physics Component";
+   mComponentType = "Physics";
+
+   mDescription = getDescriptionText("A stub component class that physics components should inherit from.");
+
    mStatic = false;
    mAtRest = false;
    mAtRestCounter = 0;
@@ -80,7 +87,7 @@ PhysicsComponent::~PhysicsComponent()
    SAFE_DELETE_ARRAY(mDescription);
 }
 
-IMPLEMENT_CO_NETOBJECT_V1(PhysicsComponent);
+IMPLEMENT_CONOBJECT(PhysicsComponent);
 
 void PhysicsComponent::onComponentAdd()
 {
@@ -92,6 +99,11 @@ void PhysicsComponent::onComponentAdd()
    mDelta.posVec = Point3F(0,0,0);
 }
 
+void PhysicsComponent::onComponentRemove()
+{
+   Parent::onComponentRemove();
+}
+
 void PhysicsComponent::initPersistFields()
 {
    Parent::initPersistFields();
@@ -101,6 +113,7 @@ void PhysicsComponent::initPersistFields()
    addField("isStatic", TypeBool, Offset(mStatic, PhysicsComponent));
 }
 
+//Networking
 U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
 {
    U32 retMask = Parent::packUpdate(con, mask, stream);
@@ -123,7 +136,6 @@ U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream
    }
    return retMask;
 }
-
 void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
 {
    Parent::unpackUpdate(con, stream);
@@ -146,7 +158,35 @@ void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
    }
 }
 
-//
+//Setup
+void PhysicsComponent::prepCollision()
+{
+   if (!mOwner)
+      return;
+
+   if (mConvexList != NULL)
+      mConvexList->nukeList();
+
+   mOwner->enableCollision();
+   _updatePhysics();
+}
+
+void PhysicsComponent::_updatePhysics()
+{
+   SAFE_DELETE( mPhysicsRep );
+
+   if ( !PHYSICSMGR )
+      return;
+
+   return;
+}
+
+void PhysicsComponent::buildConvex(const Box3F& box, Convex* convex)
+{
+   convex = nullptr;
+}
+
+//Updates
 void PhysicsComponent::interpolateTick(F32 dt)
 {
    Point3F pos = mDelta.pos + mDelta.posVec * dt;
@@ -155,10 +195,19 @@ void PhysicsComponent::interpolateTick(F32 dt)
    setRenderPosition(pos,dt);
 }
 
-//
+void PhysicsComponent::updatePos(const F32 travelTime)
+{
+   return;
+}
+
+void PhysicsComponent::updateForces()
+{
+   return;
+}
+
 void PhysicsComponent::updateContainer()
 {
-   PROFILE_SCOPE( PhysicsBehaviorInstance_updateContainer );
+   PROFILE_SCOPE(PhysicsBehaviorInstance_updateContainer);
 
    // Update container drag and buoyancy properties
 
@@ -168,11 +217,11 @@ void PhysicsComponent::updateContainer()
    //mGravityMod = 1.0;
    //mAppliedForce.set(0,0,0);
 
-   ContainerQueryInfo info;
-   info.box = mOwner->getWorldBox();
-   info.mass = mMass;
+   mLastContainerInfo = ContainerQueryInfo();
+   mLastContainerInfo.box = mOwner->getWorldBox();
+   mLastContainerInfo.mass = mMass;
 
-   mOwner->getContainer()->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
+   mOwner->getContainer()->findObjects(mLastContainerInfo.box, WaterObjectType | PhysicalZoneObjectType, findRouter, &mLastContainerInfo);
 
    //mWaterCoverage = info.waterCoverage;
    //mLiquidType    = info.liquidType;
@@ -182,71 +231,37 @@ void PhysicsComponent::updateContainer()
    // 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) 
-   {      
+   if (mLastContainerInfo.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;
+      mDrag = (mLastContainerInfo.waterCoverage * mLastContainerInfo.waterViscosity) +
+         (1.0f - mLastContainerInfo.waterCoverage) * mDrag;
       //mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
    }
 
    //mAppliedForce = info.appliedForce;
-   mGravityMod = info.gravityScale;
+   mGravityMod = mLastContainerInfo.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;
+//Events
+void PhysicsComponent::updateVelocity(const F32 dt)
+{
 }
 
-PhysicsBody *PhysicsComponent::getPhysicsRep()
+void PhysicsComponent::applyImpulse(const Point3F&, const VectorF& vec)
 {
-   /*if(mOwner)
-   {
-      Entity* ac = dynamic_cast<Entity*>(mOwner);
-      if(ac)
-         return ac->mPhysicsRep;
-   }*/
-   return NULL;
+   // Items ignore angular velocity
+   VectorF vel;
+   vel.x = vec.x / mMass;
+   vel.y = vec.y / mMass;
+   vel.z = vec.z / mMass;
+   setVelocity(mVelocity + vel);
 }
-//
+
+//Setters
 void PhysicsComponent::setTransform(const MatrixF& mat)
 {
    mOwner->setTransform(mat);
@@ -257,8 +272,8 @@ void PhysicsComponent::setTransform(const MatrixF& mat)
       mAtRestCounter = 0;
    }
 
-   if ( getPhysicsRep() )
-      getPhysicsRep()->setTransform( mOwner->getTransform() );
+   if (getPhysicsRep())
+      getPhysicsRep()->setTransform(mOwner->getTransform());
 
    setMaskBits(UpdateMask);
 }
@@ -272,16 +287,15 @@ void PhysicsComponent::setPosition(const Point3F& pos)
       return;
    }
    else {
-      mat.setColumn(3,pos);
+      mat.setColumn(3, pos);
    }
 
    mOwner->setTransform(mat);
 
-   if ( getPhysicsRep() )
-      getPhysicsRep()->setTransform( mat );
+   if (getPhysicsRep())
+      getPhysicsRep()->setTransform(mat);
 }
 
-
 void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
 {
    MatrixF mat = mOwner->getRenderTransform();
@@ -291,16 +305,12 @@ void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
       return;
    }
    else {
-      mat.setColumn(3,pos);
+      mat.setColumn(3, pos);
    }
 
    mOwner->setRenderTransform(mat);
 }
 
-void PhysicsComponent::updateVelocity(const F32 dt)
-{
-}
-
 void PhysicsComponent::setVelocity(const VectorF& vel)
 {
    mVelocity = vel;
@@ -310,6 +320,18 @@ void PhysicsComponent::setVelocity(const VectorF& vel)
    setMaskBits(VelocityMask);
 }
 
+//Getters
+PhysicsBody *PhysicsComponent::getPhysicsRep()
+{
+   /*if(mOwner)
+   {
+      Entity* ac = dynamic_cast<Entity*>(mOwner);
+      if(ac)
+         return ac->mPhysicsRep;
+   }*/
+   return NULL;
+}
+
 void PhysicsComponent::getVelocity(const Point3F& r, Point3F* v)
 {
    *v = mVelocity;
@@ -339,20 +361,6 @@ F32 PhysicsComponent::getZeroImpulse(const Point3F& r,const Point3F& normal)
    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"

+ 42 - 21
Engine/source/T3D/components/physics/physicsBehavior.h → Engine/source/T3D/components/physics/physicsComponent.h

@@ -2,9 +2,10 @@
 // Torque Game Engine
 // Copyright (C) GarageGames.com, Inc.
 //-----------------------------------------------------------------------------
+#pragma once
+#ifndef PHYSICS_COMPONENT_H
+#define PHYSICS_COMPONENT_H
 
-#ifndef _PHYSICSBEHAVIOR_H_
-#define _PHYSICSBEHAVIOR_H_
 #include "T3D/components/component.h"
 
 #ifndef __RESOURCE_H__
@@ -59,13 +60,17 @@ protected:
    VectorF mGravity;
    VectorF mVelocity;
    F32     mDrag;
-   F32		mMass;
+   F32	  mMass;
 
    F32		mGravityMod;
 
    S32 csmAtRestTimer;
    F32 sAtRestVelocity;      // Min speed after collisio
 
+   PhysicsBody*   mPhysicsRep;
+   PhysicsWorld*  mPhysicsWorld;
+
+   Convex*        mConvexList;
 public:
    enum MaskBits {
       PositionMask = Parent::NextFreeMask << 0,
@@ -79,7 +84,7 @@ public:
    {
       Move move;                    ///< Last move from server
       F32 dt;                       ///< Last interpolation time
-      // Interpolation data
+                                    // Interpolation data
       Point3F pos;
       Point3F posVec;
       QuatF rot[2];
@@ -91,8 +96,11 @@ public:
    };
 
    StateDelta mDelta;
+
    S32 mPredictionCount;            ///< Number of ticks to predict
 
+   ContainerQueryInfo mLastContainerInfo;
+
 public:
    PhysicsComponent();
    virtual ~PhysicsComponent();
@@ -100,36 +108,49 @@ public:
 
    static void initPersistFields();
 
-   virtual void interpolateTick(F32 dt);
-   virtual void updatePos(const U32 /*mask*/, const F32 dt){}
-   virtual void _updatePhysics();
-   virtual PhysicsBody *getPhysicsRep();
+   //Components
+   virtual void onComponentAdd();
+   virtual void onComponentRemove();
 
-   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
-   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+   //Setup
+   void prepCollision();
+   virtual void _updatePhysics();
+   virtual void buildConvex(const Box3F& box, Convex* convex);
 
-   virtual void onComponentAdd();
+   //Update
+   virtual void interpolateTick(F32 dt);
+   virtual void updatePos(const F32 dt);
 
+   virtual void updateForces();
    void updateContainer();
 
+   //Physics 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(const Point3F& p, const Point3F &normal) { return false; }
+   
+   //Networking
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   //Events
    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; }
+   //Gets
+   F32 getMass() { return mMass; }
+   virtual PhysicsBody *getPhysicsRep();
+   virtual Point3F getVelocity() { return mVelocity; }
+   virtual void getOriginVector(const Point3F &p, Point3F* r);
+   virtual void getVelocity(const Point3F& r, Point3F* v);
+   virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
+   
 };
 
 #endif // _COMPONENT_H_

+ 26 - 22
Engine/source/T3D/components/physics/playerControllerComponent.cpp

@@ -37,7 +37,6 @@
 #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"
 
@@ -58,13 +57,8 @@ IMPLEMENT_CALLBACK(PlayerControllerComponent, updateMove, void, (PlayerControlle
 //////////////////////////////////////////////////////////////////////////
 // Constructor/Destructor
 //////////////////////////////////////////////////////////////////////////
-PlayerControllerComponent::PlayerControllerComponent() : Component()
+PlayerControllerComponent::PlayerControllerComponent() : PhysicsComponent()
 {
-   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;
@@ -122,7 +116,7 @@ PlayerControllerComponent::PlayerControllerComponent() : Component()
    mPhysicsRep = nullptr;
    mPhysicsWorld = nullptr;
 
-   mOwnerCollisionInterface = nullptr;
+   mOwnerCollisionComp = nullptr;
    mIntegrationCount = 0;
 }
 
@@ -160,6 +154,13 @@ void PlayerControllerComponent::onComponentAdd()
 {
    Parent::onComponentAdd();
 
+   CollisionComponent *collisionComp = mOwner->getComponent<CollisionComponent>();
+   if (collisionComp)
+   {
+      collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
+      mOwnerCollisionComp = collisionComp;
+   }
+
    updatePhysics();
 }
 
@@ -168,12 +169,11 @@ 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)
+   CollisionComponent *collisionComp = dynamic_cast<CollisionComponent*>(comp);
+   if (collisionComp)
    {
-      collisionInterface->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
-      mOwnerCollisionInterface = collisionInterface;
+      collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
+      mOwnerCollisionComp = collisionComp;
       updatePhysics();
    }
 }
@@ -183,12 +183,11 @@ 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)
+   CollisionComponent *collisionComp = dynamic_cast<CollisionComponent*>(comp);
+   if (collisionComp)
    {
-      collisionInterface->onCollisionChanged.remove(this, &PlayerControllerComponent::updatePhysics);
-      mOwnerCollisionInterface = NULL;
+      collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
+      mOwnerCollisionComp = nullptr;
       updatePhysics();
    }
 }
@@ -219,7 +218,7 @@ void PlayerControllerComponent::initPersistFields()
    Parent::initPersistFields();
 
    addField("inputVelocity", TypePoint3F, Offset(mInputVelocity, PlayerControllerComponent), "");
-   addField("useDirectMoveInput", TypePoint3F, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
+   addField("useDirectMoveInput", TypeBool, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
 }
 
 U32 PlayerControllerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
@@ -579,10 +578,10 @@ void PlayerControllerComponent::updatePos(const F32 travelTime)
       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)
+      CollisionComponent* colComp = mOwner->getComponent<CollisionComponent>();
+      if (colComp)
       {
-         colInterface->handleCollisionList(collisionList, mVelocity);
+         colComp->handleCollisionList(collisionList, mVelocity);
       }
    }
 
@@ -629,6 +628,8 @@ void PlayerControllerComponent::updatePos(const F32 travelTime)
          }
       }
    }
+
+   updateContainer();
    
    MatrixF newMat;
    newMat.setPosition(newPos);
@@ -701,6 +702,9 @@ void PlayerControllerComponent::findContact(bool *run, bool *jump, VectorF *cont
 
    if (mContactInfo.contacted)
       mContactInfo.contactNormal = *contactNormal;
+
+   mContactInfo.run = *run;
+   mContactInfo.jump = *jump;
 }
 
 void PlayerControllerComponent::applyImpulse(const Point3F &pos, const VectorF &vec)

+ 9 - 13
Engine/source/T3D/components/physics/playerControllerComponent.h

@@ -23,8 +23,8 @@
 #ifndef PLAYER_CONTORLLER_COMPONENT_H
 #define PLAYER_CONTORLLER_COMPONENT_H
 
-#ifndef PHYSICSBEHAVIOR_H
-#include "T3D/components/physics/physicsBehavior.h"
+#ifndef PHYSICS_COMPONENT_H
+#include "T3D/components/physics/physicsComponent.h"
 #endif
 #ifndef __RESOURCE_H__
 #include "core/resource.h"
@@ -53,25 +53,20 @@
 #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"
+#ifndef COLLISION_COMPONENT_H
+#include "T3D/components/collision/collisionComponent.h"
 #endif
 
 class SceneRenderState;
 class PhysicsWorld;
 class PhysicsPlayer;
 class SimplePhysicsBehaviorInstance;
-class CollisionInterface;
 
 //////////////////////////////////////////////////////////////////////////
 /// 
 /// 
 //////////////////////////////////////////////////////////////////////////
-class PlayerControllerComponent : public Component,
-   public PhysicsComponentInterface
+class PlayerControllerComponent : public PhysicsComponent
 {
    typedef Component Parent;
 
@@ -101,7 +96,7 @@ class PlayerControllerComponent : public Component,
    PhysicsPlayer *mPhysicsRep;
    PhysicsWorld  *mPhysicsWorld;
 
-   CollisionInterface* mOwnerCollisionInterface;
+   CollisionComponent* mOwnerCollisionComp;
 
    struct ContactInfo
    {
@@ -113,8 +108,9 @@ class PlayerControllerComponent : public Component,
       void clear()
       {
          contacted = jump = run = false;
-         contactObject = NULL;
-         contactNormal.set(1, 1, 1);
+         contactObject = nullptr;
+         contactNormal.set(0,0,0);
+         contactTime = 0;
       }
 
       ContactInfo() { clear(); }

+ 3 - 3
Engine/source/T3D/components/physics/rigidBodyComponent.cpp

@@ -39,7 +39,7 @@ bool RigidBodyComponent::smNoSmoothing = false;
 //////////////////////////////////////////////////////////////////////////
 // Constructor/Destructor
 //////////////////////////////////////////////////////////////////////////
-RigidBodyComponent::RigidBodyComponent() : Component()   
+RigidBodyComponent::RigidBodyComponent() : PhysicsComponent()
 {
    mMass = 20;
    mDynamicFriction = 1;
@@ -353,11 +353,11 @@ void RigidBodyComponent::findContact()
 
    mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
 
-   if (!overlapObjects.empty())
+   /*if (!overlapObjects.empty())
    {
       //fire our signal that the physics sim said collisions happened
       onPhysicsCollision.trigger(*contactNormal, overlapObjects);
-   }
+   }*/
 }
 
 void RigidBodyComponent::_onPhysicsReset(PhysicsResetEvent reset)

+ 4 - 4
Engine/source/T3D/components/physics/rigidBodyComponent.h

@@ -32,8 +32,8 @@
 #ifndef COLLISION_COMPONENT_H
 #include "T3D/components/collision/collisionComponent.h"
 #endif
-#ifndef PHYSICS_COMPONENT_INTERFACE_H
-#include "T3D/components/physics/physicsComponentInterface.h"
+#ifndef PHYSICS_COMPONENT_H
+#include "T3D/components/physics/physicsComponent.h"
 #endif
 
 class PhysicsBody;
@@ -42,9 +42,9 @@ class PhysicsBody;
 /// 
 /// 
 //////////////////////////////////////////////////////////////////////////
-class RigidBodyComponent : public Component, public PhysicsComponentInterface
+class RigidBodyComponent : public PhysicsComponent
 {
-   typedef Component Parent;
+   typedef PhysicsComponent Parent;
 
    enum SimType
    {

+ 391 - 0
Engine/source/T3D/components/physics/simplePhysicsComponent.cpp

@@ -0,0 +1,391 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "T3D/components/physics/simplePhysicsComponent.h"
+#include "T3D/components/collision/collisionComponent.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"
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+IMPLEMENT_CALLBACK( SimplePhysicsComponent, updateMove, void, ( SimplePhysicsComponent* 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
+//////////////////////////////////////////////////////////////////////////
+SimplePhysicsComponent::SimplePhysicsComponent() : PhysicsComponent()
+{
+   mBuoyancy = 0.f;
+   mFriction = 0.3f;
+   mElasticity = 0.4f;
+   mMaxVelocity = 3000.f;
+   mSticky = false;
+
+   mDrag = 0.5;
+
+   mVelocity = Point3F::Zero;
+
+   moveSpeed = Point3F(1, 1, 1);
+
+   mFriendlyName = "Simple Physics";
+   mComponentType = "Physics";
+
+   mDescription = getDescriptionText("Simple physics Component that allows gravity and impulses.");
+}
+
+SimplePhysicsComponent::~SimplePhysicsComponent()
+{
+   for(S32 i = 0;i < mFields.size();++i)
+   {
+      ComponentField &field = mFields[i];
+      SAFE_DELETE_ARRAY(field.mFieldDescription);
+   }
+
+   SAFE_DELETE_ARRAY(mDescription);
+}
+
+IMPLEMENT_CONOBJECT(SimplePhysicsComponent);
+
+//////////////////////////////////////////////////////////////////////////
+
+bool SimplePhysicsComponent::onAdd()
+{
+   if(! Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void SimplePhysicsComponent::onRemove()
+{
+   Parent::onRemove();
+}
+
+void SimplePhysicsComponent::initPersistFields()
+{
+   Parent::initPersistFields();
+
+   addField( "moveSpeed", TypePoint3F, Offset(moveSpeed, SimplePhysicsComponent), "");
+}
+
+U32 SimplePhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+   return retMask;
+}
+
+void SimplePhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+}
+
+//
+void SimplePhysicsComponent::processTick()
+{
+   Parent::processTick();
+
+   if (!isServerObject() || !isActive())
+      return;
+
+   //
+   //if (mCollisionObject && !--mCollisionTimeout)
+   //    mCollisionObject = 0;
+
+   // Warp to catch up to server
+   if (mDelta.warpCount < mDelta.warpTicks) 
+   {
+      mDelta.warpCount++;
+
+      // Set new pos.
+      mOwner->getTransform().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);
+      MatrixF trans;
+      mDelta.rot[1].setMatrix(&trans);
+      trans.setPosition(mDelta.pos);
+      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();
+
+      updateForces();
+      updatePos(TickSec);
+
+      // Wrap up interpolation info
+      mDelta.pos     = mOwner->getPosition();
+      mDelta.posVec -= mOwner->getPosition();
+      //mDelta.rot[1]  = mRigid.angPosition;
+
+      // Update container database
+      setTransform(mOwner->getTransform());
+      setMaskBits(UpdateMask);
+      updateContainer();
+   }
+}
+
+void SimplePhysicsComponent::interpolateTick(F32 dt)
+{
+   // Client side interpolation
+   Point3F pos = mDelta.pos + mDelta.posVec * dt;
+   MatrixF mat = mOwner->getRenderTransform();
+   mat.setColumn(3,pos);
+   mOwner->setRenderTransform(mat);
+   mDelta.dt = dt;
+}
+
+void SimplePhysicsComponent::updatePos(const F32 travelTime)
+{
+   mOwner->getTransform().getColumn(3,&mDelta.posVec);
+
+   // When mounted to another object, only Z rotation used.
+   if (mOwner->isMounted()) {
+      mVelocity = mOwner->getObjectMount()->getVelocity();
+      setPosition(Point3F(0.0f, 0.0f, 0.0f));
+      setMaskBits(UpdateMask);
+      return;
+   }
+
+   Point3F newPos;
+
+   if ( mVelocity.isZero() )
+      newPos = mDelta.posVec;
+   else
+      newPos = _move( travelTime );
+   //}
+
+   // Set new position
+   // If on the client, calc delta for backstepping
+   if (isClientObject())
+   {
+      mDelta.pos = newPos;
+      mDelta.posVec = mDelta.posVec - mDelta.pos;
+      mDelta.dt = 1.0f;
+   }
+
+   setPosition( newPos );
+   setMaskBits( UpdateMask );
+   updateContainer();
+
+   /*if (!isGhost())  
+   {
+   // Do mission area callbacks on the server as well
+   checkMissionArea();
+   }*/
+
+   return;
+}
+
+Point3F SimplePhysicsComponent::_move( const F32 travelTime )
+{
+   // Try and move to new pos
+   F32 totalMotion  = 0.0f;
+
+   Point3F start;
+   Point3F initialPosition;
+   mOwner->getTransform().getColumn(3,&start);
+   initialPosition = start;
+
+   VectorF firstNormal(0.0f, 0.0f, 0.0f);
+   //F32 maxStep = mDataBlock->maxStepHeight;
+   F32 time = travelTime;
+   U32 count = 0;
+   S32 sMoveRetryCount = 5;
+
+   CollisionComponent* colComp = mOwner->getComponent<CollisionComponent>();
+
+   if(!colComp)
+      return start + mVelocity * time;
+
+   colComp->clearCollisionList();
+
+   for (; count < sMoveRetryCount; count++) 
+   {
+      F32 speed = mVelocity.len();
+      if (!speed)
+         break;
+
+      Point3F end = start + mVelocity * time;
+      Point3F distance = end - start;
+
+      bool collided = colComp->checkCollisions(time, &mVelocity, start);
+
+      if (colComp->getCollisionList()->getCount() != 0 && colComp->getCollisionList()->getTime() < 1.0f)
+      {
+         // Set to collision point
+         F32 velLen = mVelocity.len();
+
+         F32 dt = time * getMin(colComp->getCollisionList()->getTime(), 1.0f);
+         start += mVelocity * dt;
+         time -= dt;
+
+         totalMotion += velLen * dt;
+
+         // Back off...
+         if ( velLen > 0.f ) 
+         {
+            F32 newT = getMin(0.01f / velLen, dt);
+            start -= mVelocity * newT;
+            totalMotion -= velLen * newT;
+         }
+
+         // Pick the surface most parallel to the face that was hit.
+         U32 colCount = colComp->getCollisionList()->getCount();
+
+         const Collision *collision = colComp->getCollision(0);
+         const Collision *cp = collision + 1;
+         const Collision *ep = collision + colComp->getCollisionList()->getCount();
+         for (; cp != ep; cp++)
+         {
+            U32 colCountLoop = colComp->getCollisionList()->getCount();
+
+            //TODO: Move this somewhere else
+            if(Entity* colEnt = dynamic_cast<Entity*>(collision->object))
+            {
+               if(CollisionComponent *collidingEntityColComp = colEnt->getComponent<CollisionComponent>())
+               {
+                  if(!collidingEntityColComp->doesBlockColliding())
+                  {
+                     continue;
+                  }
+               }
+            }
+
+            if (cp->faceDot > collision->faceDot)
+               collision = cp;
+         }
+
+         //check the last/first one just incase
+         if(Entity* colEnt = dynamic_cast<Entity*>(collision->object))
+         {
+            if(CollisionComponent *collidingEntityColComp = colEnt->getComponent<CollisionComponent>())
+            {
+               if(!collidingEntityColComp->doesBlockColliding())
+               {
+                  //if our ideal surface doesn't stop us, just move along
+                  return start + mVelocity * time;
+               }
+            }
+         }
+
+         //F32 bd = _doCollisionImpact( collision, wasFalling );
+         F32 bd = -mDot( mVelocity, collision->normal);
+
+         // Subtract out velocity
+         F32 sNormalElasticity = 0.01f;
+         VectorF dv = collision->normal * (bd + sNormalElasticity);
+         mVelocity += dv;
+         if (count == 0)
+         {
+            firstNormal = collision->normal;
+         }
+         else
+         {
+            if (count == 1)
+            {
+               // Re-orient velocity along the crease.
+               if (mDot(dv,firstNormal) < 0.0f &&
+                  mDot(collision->normal,firstNormal) < 0.0f)
+               {
+                  VectorF nv;
+                  mCross(collision->normal,firstNormal,&nv);
+                  F32 nvl = nv.len();
+                  if (nvl)
+                  {
+                     if (mDot(nv,mVelocity) < 0.0f)
+                        nvl = -nvl;
+                     nv *= mVelocity.len() / nvl;
+                     mVelocity = nv;
+                  }
+               }
+            }
+         }
+      }
+      else
+      {
+         totalMotion += (end - start).len();
+         start = end;
+         break;
+      }
+   }
+
+   U32 colCountThree = colComp->getCollisionList()->getCount();
+
+   if (colCountThree != 0)
+      bool derp = true;
+
+   if (count == sMoveRetryCount)
+   {
+      // Failed to move
+      start = initialPosition;
+      mVelocity.set(0.0f, 0.0f, 0.0f);
+   }
+
+   return start;
+}
+
+void SimplePhysicsComponent::updateForces()
+{
+   // Acceleration due to gravity
+   mVelocity += (mGravity * mGravityMod) * TickMs;
+   F32 len = mVelocity.len();
+
+   if (mMaxVelocity > 0 && mAbs(len) > mMaxVelocity) 
+   {
+      Point3F excess = mVelocity * (1.0 - (mMaxVelocity / len));
+      excess *= 0.1f;
+      mVelocity -= excess;
+   }
+
+   // Container buoyancy & drag
+   if(mOwner->getContainerInfo().waterCoverage > 0.65f)
+      mVelocity -= mBuoyancy * (mGravity * mGravityMod) * TickMs;
+
+   mVelocity -= mVelocity * mDrag * TickMs;
+
+   if( mVelocity.isZero() )
+      mVelocity = Point3F::Zero;
+   else
+      setMaskBits(VelocityMask);
+}
+
+//
+void SimplePhysicsComponent::setVelocity(const VectorF& vel)
+{
+   Parent::setVelocity(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;
+      }
+   }
+}

+ 88 - 0
Engine/source/T3D/components/physics/simplePhysicsComponent.h

@@ -0,0 +1,88 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef SIMPLE_PHYSICS_COMPONENT_H
+#define SIMPLE_PHYSICS_COMPONENT_H
+
+#ifndef PHYSICS_COMPONENT_H
+#include "T3D/components/physics/physicsComponent.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
+
+class SceneRenderState;
+class PhysicsBody;
+//////////////////////////////////////////////////////////////////////////
+/// 
+/// 
+//////////////////////////////////////////////////////////////////////////
+class SimplePhysicsComponent : public PhysicsComponent
+{
+   typedef PhysicsComponent Parent;
+
+protected:
+   F32 mBuoyancy;
+   F32 mFriction;
+   F32 mElasticity;
+   F32 mMaxVelocity;
+   bool mSticky;
+
+   U32 mIntegrationCount;
+
+   Point3F moveSpeed;
+
+   Point3F mStickyCollisionPos;
+   Point3F mStickyCollisionNormal;
+
+public:
+   SimplePhysicsComponent();
+   virtual ~SimplePhysicsComponent();
+   DECLARE_CONOBJECT(SimplePhysicsComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void processTick();
+   virtual void interpolateTick(F32 dt);
+   virtual void updatePos(const F32 dt);
+   void updateForces();
+
+   void updateMove(const Move* move);
+   Point3F _move(const F32 travelTime);
+
+   //virtual void onComponentRemove();
+
+   virtual VectorF getVelocity() { return mVelocity; }
+   virtual void setVelocity(const VectorF& vel);
+
+   //
+   DECLARE_CALLBACK(void, updateMove, (SimplePhysicsComponent* obj));
+};
+
+#endif // _COMPONENT_H_

+ 36 - 57
Engine/source/T3D/components/render/meshComponent.cpp

@@ -45,21 +45,10 @@
 #include "core/strings/findMatch.h"
 #include "T3D/components/render/meshComponent_ScriptBinding.h"
 
-ImplementEnumType(BatchingMode,
-   "Type of mesh data available in a shape.\n"
-   "@ingroup gameObjects")
-{
-   MeshComponent::Individual, "Individual", "This mesh is rendered indivudally, wthout batching or instancing."
-},
-   { MeshComponent::StaticBatch, "Static Batching", "Statically batches this mesh together with others to reduce drawcalls." },
-   //{ MeshComponent::DynamicBatch, "Dynamic Batching", "Dynamical batches this mesh together with others to reduce drawcalls each frame." },
-  // { MeshComponent::Instanced, "Instanced", "This mesh is rendered as an instance, reducing draw overhead with others that share the same mesh and material." },
-      EndImplementEnumType;
-
 //////////////////////////////////////////////////////////////////////////
 // Constructor/Destructor
 //////////////////////////////////////////////////////////////////////////
-MeshComponent::MeshComponent() : Component(), mShape(nullptr), mRenderMode(Individual)
+MeshComponent::MeshComponent() : Component(), mShape(nullptr)
 {
    mFriendlyName = "Mesh Component";
    mComponentType = "Render";
@@ -75,8 +64,6 @@ MeshComponent::MeshComponent() : Component(), mShape(nullptr), mRenderMode(Indiv
    mMeshAssetId = StringTable->EmptyString();
 
    mInterfaceData = new MeshRenderSystemInterface();
-
-   mRenderMode = Individual;
 }
 
 MeshComponent::~MeshComponent()
@@ -134,14 +121,9 @@ void MeshComponent::initPersistFields()
 {
    Parent::initPersistFields();
 
-   addGroup("Rendering");
-   addField("BatchingMode", TypeBatchingMode, Offset(mRenderMode, MeshComponent),
-      "The mode of batching this shape should be rendered with.");
-   endGroup("Rendering");
-
    //create a hook to our internal variables
    addGroup("Model");
-   addProtectedField("MeshAsset", TypeShapeAssetPtr, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn,
+   addProtectedField("MeshAsset", TypeShapeAssetPtr, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn, &writeShape,
       "The asset Id used for the mesh.", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
    endGroup("Model");
 }
@@ -182,6 +164,8 @@ bool MeshComponent::setMeshAsset(const char* assetName)
       return false;
    }
 
+   mMeshAsset->onShapeChanged.notify(this, &MeshComponent::_shapeAssetUpdated);
+
    mShapeName = mMeshAssetId;
    mShapeAsset = mShapeName;
    updateShape(); //make sure we force the update to resize the owner bounds
@@ -190,6 +174,11 @@ bool MeshComponent::setMeshAsset(const char* assetName)
    return true;
 }
 
+void MeshComponent::_shapeAssetUpdated(ShapeAsset* asset)
+{
+   updateShape();
+}
+
 void MeshComponent::updateShape()
 {
    if (mInterfaceData == nullptr)
@@ -197,7 +186,6 @@ void MeshComponent::updateShape()
 
    //if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
    if ((mShapeName && mShapeName[0] != '\0') || (mMeshAssetId && mMeshAssetId[0] != '\0'))
-
    {
       if (mMeshAsset == NULL)
          return;
@@ -210,7 +198,7 @@ void MeshComponent::updateShape()
       setupShape();
 
       //Do this on both the server and client
-      S32 materialCount = mMeshAsset->getShape()->materialList->getMaterialNameList().size();
+      S32 materialCount = mMeshAsset->getShape()->materialList->getMaterialNameList().size(); //mMeshAsset->getMaterialCount();
 
       if (isServerObject())
       {
@@ -234,13 +222,21 @@ void MeshComponent::updateShape()
 
          for (U32 i = 0; i < materialCount; i++)
          {
-            String materialname = mMeshAsset->getShape()->materialList->getMaterialName(i);
-            if (materialname == String("ShapeBounds"))
-               continue;
+            StringTableEntry materialname = StringTable->insert(mMeshAsset->getShape()->materialList->getMaterialName(i).c_str());
+
+            //Iterate through our assetList to find the compliant entry in our matList
+            for (U32 m = 0; m < mMeshAsset->getMaterialCount(); m++)
+            {
+               AssetPtr<MaterialAsset> matAsset = mMeshAsset->getMaterialAsset(m);
 
-            dSprintf(matFieldName, 128, "MaterialSlot%d", i);
+               if (matAsset->getMaterialDefinitionName() == materialname)
+               {
+                  dSprintf(matFieldName, 128, "MaterialSlot%d", i);
 
-            addComponentField(matFieldName, "A material used in the shape file", "Material", materialname, "");
+                  addComponentField(matFieldName, "A material used in the shape file", "Material", matAsset->getAssetId(), "");
+                  break;
+               }
+            }
          }
 
          if (materialCount > 0)
@@ -272,27 +268,6 @@ void MeshComponent::updateShape()
             mOwner->getSceneManager()->notifyObjectDirty(mOwner);
       }
 
-      if (isClientObject() && mInterfaceData)
-      {
-         if (mRenderMode == StaticBatch)
-         {
-            mInterfaceData->mStatic = true;
-
-            OptimizedPolyList geom;
-            MatrixF transform = mInterfaceData->mTransform;
-            mInterfaceData->mGeometry.setTransform(&transform, mInterfaceData->mScale);
-            mInterfaceData->mGeometry.setObject(mOwner);
-
-            mInterfaceData->mShapeInstance->buildPolyList(&mInterfaceData->mGeometry, 0);
-         }
-         else
-         {
-            mInterfaceData->mStatic = false;
-         }
-
-         MeshRenderSystem::rebuildBuffers();
-      }
-
       //finally, notify that our shape was changed
       onShapeInstanceChanged.trigger(this);
    }
@@ -305,6 +280,8 @@ void MeshComponent::setupShape()
 
 void MeshComponent::_onResourceChanged( const Torque::Path &path )
 {
+   /*bool srv = isServerObject();
+
    if (mInterfaceData == nullptr)
       return;
 
@@ -316,7 +293,7 @@ void MeshComponent::_onResourceChanged( const Torque::Path &path )
       return;
 
    updateShape();
-   setMaskBits(ShapeMask);
+   setMaskBits(ShapeMask);*/
 }
 
 void MeshComponent::inspectPostApply()
@@ -343,8 +320,6 @@ U32 MeshComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
    if (stream->writeFlag(mask & ShapeMask))
    {
       stream->writeString(mShapeName);
-
-      stream->writeInt(mRenderMode, 8);
    }
 
    if (stream->writeFlag( mask & MaterialMask ))
@@ -373,7 +348,6 @@ void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
    {
       mShapeName = stream->readSTString();
 
-      mRenderMode = (RenderMode)stream->readInt(8);
       setMeshAsset(mShapeName);
       updateShape();
    }
@@ -460,28 +434,34 @@ void MeshComponent::updateMaterials()
    TSMaterialList* pMatList = mInterfaceData->mShapeInstance->getMaterialList();
    pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
 
+   bool found = false;
    const Vector<String> &materialNames = pMatList->getMaterialNameList();
    for ( S32 i = 0; i < materialNames.size(); i++ )
    {
+      if (found)
+         break;
+
       for(U32 m=0; m < mChangingMaterials.size(); m++)
       {
          if(mChangingMaterials[m].slot == i)
          {
             //Fetch the actual material asset
             pMatList->renameMaterial( i, mChangingMaterials[m].matAsset->getMaterialDefinitionName());
+            found = true;
+            break;
          }
       }
-
-      mChangingMaterials.clear();
    }
 
+   mChangingMaterials.clear();
+
    // Initialize the material instances
    mInterfaceData->mShapeInstance->initMaterialList();
 }
 
 MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
 {
-   if (mInterfaceData != nullptr && mMeshAsset->getShape())
+   if (mInterfaceData != nullptr && !mMeshAsset.isNull() && mMeshAsset->isAssetValid() && mMeshAsset->getShape())
    {
       S32 nodeCount = getShape()->nodes.size();
 
@@ -589,7 +569,6 @@ void MeshComponent::onDynamicModified(const char* slotName, const char* newValue
 
 void MeshComponent::changeMaterial(U32 slot, MaterialAsset* newMat)
 {
-   
    char fieldName[512];
 
    //update our respective field
@@ -629,4 +608,4 @@ void MeshComponent::ownerTransformSet(MatrixF *mat)
       MatrixF newTransform = *mat;
       mInterfaceData->mTransform = newTransform;
    }
-}
+}

+ 4 - 15
Engine/source/T3D/components/render/meshComponent.h

@@ -101,18 +101,6 @@ protected:
    Vector<matMap>  mChangingMaterials;
    Vector<matMap>  mMaterials;
 
-public:
-   enum RenderMode
-   {
-      Individual = 0,
-      DynamicBatch,
-      StaticBatch,
-      Instanced
-   };
-
-protected:
-   RenderMode           mRenderMode;
-
 public:
    StringTableEntry       mMeshAssetId;
    AssetPtr<ShapeAsset>   mMeshAsset;
@@ -144,6 +132,8 @@ public:
    void updateShape();
    void updateMaterials();
 
+   void _shapeAssetUpdated(ShapeAsset* asset);
+
    virtual void onComponentRemove();
    virtual void onComponentAdd();
 
@@ -153,6 +143,8 @@ public:
    static bool _setShape(void *object, const char *index, const char *data);
    const char* _getShape(void *object, const char *data);
 
+   static bool writeShape(void* obj, StringTableEntry pFieldName) { return static_cast<MeshComponent*>(obj)->mMeshAsset.notNull(); }
+
    bool setMeshAsset(const char* assetName);
 
    virtual TSShape* getShape() { if (mMeshAsset)  return mMeshAsset->getShape(); else return NULL; }
@@ -186,7 +178,4 @@ public:
    }
 };
 
-typedef MeshComponent::RenderMode BatchingMode;
-DefineEnumType(BatchingMode);
-
 #endif

+ 10 - 19
Engine/source/T3D/entity.cpp

@@ -36,7 +36,7 @@
 
 #include "T3D/components/coreInterfaces.h"
 #include "T3D/components/render/renderComponentInterface.h"
-#include "T3D/components/collision/collisionInterfaces.h"
+#include "T3D/components/collision/collisionComponent.h"
 
 #include "gui/controls/guiTreeViewCtrl.h"
 #include "assets/assetManager.h"
@@ -1119,8 +1119,8 @@ bool Entity::buildPolyList(PolyListContext context, AbstractPolyList* polyList,
 
 void Entity::buildConvex(const Box3F& box, Convex* convex)
 {
-   Vector<BuildConvexInterface*> updaters = getComponents<BuildConvexInterface>();
-   for (Vector<BuildConvexInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++)
+   Vector<CollisionComponent*> colliders = getComponents<CollisionComponent>();
+   for (Vector<CollisionComponent*>::iterator it = colliders.begin(); it != colliders.end(); it++)
    {
       (*it)->buildConvex(box, convex);
    }
@@ -1394,10 +1394,7 @@ void Entity::clearComponents(bool deleteComponents)
          {
             comp->onComponentRemove(); //in case the behavior needs to do cleanup on the owner
 
-            //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();
+            comp->deleteObject();
          }
       }
    }
@@ -1911,12 +1908,9 @@ DefineEngineMethod(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);
-   }
+   //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;
 }
@@ -1925,12 +1919,9 @@ DefineEngineMethod(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);
-   }
+   //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;
 }

+ 3 - 2
Engine/source/T3D/entity.h

@@ -204,7 +204,7 @@ public:
    //Components
    virtual bool deferAddingComponents() const { return true; }
 
-   void notifyComponents(String signalFunction, String argA, String argB, String argC, String argD, String argE);
+   void notifyComponents(String signalFunction, String argA, String argB="", String argC = "", String argD = "", String argE = "");
 
    template <class T>
    T* getComponent();
@@ -283,8 +283,9 @@ public:
    MatrixF getWorldToObj() { return mWorldToObj; }
    MatrixF getObjToWorld() { return mObjToWorld; }
 
-   DECLARE_CONOBJECT(Entity);
+   StateDelta getNetworkDelta() { return mDelta; }
 
+   DECLARE_CONOBJECT(Entity);
 };
 
 template <class T>

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

@@ -116,6 +116,8 @@ public:
    virtual void findContact(SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects) const;
    virtual void moveKinematicTo(const MatrixF &xfm);
 
+   virtual bool isValid() { return mActor != nullptr; }
+
 };
 
 #endif // _T3D_PHYSICS_BTBODY_H_

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

@@ -128,6 +128,8 @@ public:
    ///
    virtual void moveKinematicTo(const MatrixF &xfm) = 0;
 
+   virtual bool isValid() = 0;
+
 };
 
 

+ 1 - 283
Engine/source/T3D/systems/render/meshRenderSystem.cpp

@@ -6,9 +6,6 @@
 #include "materials/materialManager.h"
 #include "materials/baseMatInstance.h"
 
-Vector<MeshRenderSystem::BufferMaterials> MeshRenderSystem::mBufferMaterials(0);
-Vector<MeshRenderSystem::BufferSet> MeshRenderSystem::mStaticBuffers(0);
-
 void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* state)
 {
    if (sceneManager == nullptr || state == nullptr)
@@ -24,9 +21,6 @@ void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* stat
       if (!MeshRenderSystemInterface::all[i]->mIsClient)
          continue;
 
-      if (MeshRenderSystemInterface::all[i]->mStatic)
-         continue;
-
       //First, do frustum culling
       if (viewFrustum.isCulled(MeshRenderSystemInterface::all[i]->mBounds))
          continue;
@@ -52,90 +46,6 @@ void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* stat
       //if we've made it this far, call down to the render function to actually display our stuff
       renderInterface(i, state);
    }
-
-   //Static Batch rendering
-   if ( /*!mMaterialInst ||*/ !state)
-      return;
-
-   BaseMatInstance *matInst = MATMGR->getWarningMatInstance();
-
-   // Get a handy pointer to our RenderPassmanager
-   RenderPassManager *renderPass = state->getRenderPass();
-
-   for (U32 i = 0; i < mStaticBuffers.size(); i++)
-   {
-      for (U32 b = 0; b < mStaticBuffers[i].buffers.size(); b++)
-      {
-         if (mStaticBuffers[i].buffers[b].vertData.empty())
-            continue;
-
-         MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
-
-         // Set our RenderInst as a standard mesh render
-         ri->type = RenderPassManager::RIT_Mesh;
-
-         //If our material has transparency set on this will redirect it to proper render bin
-         if (matInst->getMaterial()->isTranslucent())
-         {
-            ri->type = RenderPassManager::RIT_Translucent;
-            ri->translucentSort = true;
-         }
-
-         // Calculate our sorting point
-         if (state)
-         {
-            // Calculate our sort point manually.
-            const Box3F& rBox = Box3F(1000);// getRenderWorldBox();
-            ri->sortDistSq = rBox.getSqDistanceToPoint(state->getCameraPosition());
-         }
-         else
-            ri->sortDistSq = 0.0f;
-
-         // Set up our transforms
-         MatrixF objectToWorld = MatrixF::Identity;//getRenderTransform();
-                                                   //objectToWorld.scale(getScale());
-
-         ri->objectToWorld = renderPass->allocUniqueXform(objectToWorld);
-         ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
-         ri->projection = renderPass->allocSharedXform(RenderPassManager::Projection);
-
-         // If our material needs lights then fill the RIs 
-         // light vector with the best lights.
-         /*if (matInst->isForwardLit())
-         {
-         LightQuery query;
-         query.init(getWorldSphere());
-         query.getLights(ri->lights, 8);
-         }*/
-
-         // Make sure we have an up-to-date backbuffer in case
-         // our Material would like to make use of it
-         // NOTICE: SFXBB is removed and refraction is disabled!
-         //ri->backBuffTex = GFX->getSfxBackBuffer();
-
-         // Set our Material
-         ri->matInst = matInst;
-
-         // Set up our vertex buffer and primitive buffer
-         ri->vertBuff = &mStaticBuffers[i].buffers[b].vertexBuffer;
-         ri->primBuff = &mStaticBuffers[i].buffers[b].primitiveBuffer;
-
-         ri->prim = renderPass->allocPrim();
-         ri->prim->type = GFXTriangleList;
-         ri->prim->minIndex = 0;
-         ri->prim->startIndex = 0;
-         ri->prim->numPrimitives = mStaticBuffers[i].buffers[b].primData.size();
-         ri->prim->startVertex = 0;
-         ri->prim->numVertices = mStaticBuffers[i].buffers[b].vertData.size();
-
-         // We sort by the material then vertex buffer
-         ri->defaultKey = matInst->getStateHint();
-         ri->defaultKey2 = (uintptr_t)ri->vertBuff;
-
-                                                    // Submit our RenderInst to the RenderPassManager
-         state->getRenderPass()->addInst(ri);
-      }
-   }
 }
 
 void MeshRenderSystem::renderInterface(U32 interfaceIndex, SceneRenderState* state)
@@ -181,196 +91,4 @@ void MeshRenderSystem::renderInterface(U32 interfaceIndex, SceneRenderState* sta
    GFX->setWorldMatrix(mat);
 
    interface->mShapeInstance->render(rdata);
-}
-
-void MeshRenderSystem::rebuildBuffers()
-{
-   U32 BUFFER_SIZE = 65000;
-   Vector<U32> tempIndices;
-   tempIndices.reserve(4);
-
-   Box3F newBounds = Box3F::Zero;
-
-   mStaticBuffers.clear();
-
-   for (U32 i = 0; i < MeshRenderSystemInterface::all.size(); i++)
-   {
-      if (!MeshRenderSystemInterface::all[i]->mIsEnabled)
-         continue;
-
-      if (!MeshRenderSystemInterface::all[i]->mIsClient || !MeshRenderSystemInterface::all[i]->mStatic)
-         continue;
-
-      //TODO: Properly re-implement StaticElements to container owner interfaces and buffer sets
-      for (U32 j = 0; j < MeshRenderSystemInterface::all[i]->mGeometry.mPolyList.size(); j++)
-      {
-         const OptimizedPolyList::Poly& poly = MeshRenderSystemInterface::all[i]->mGeometry.mPolyList[j];
-
-         if (poly.vertexCount < 3)
-            continue;
-
-         tempIndices.setSize(poly.vertexCount);
-         dMemset(tempIndices.address(), 0, poly.vertexCount);
-
-         if (poly.type == OptimizedPolyList::TriangleStrip ||
-            poly.type == OptimizedPolyList::TriangleFan)
-         {
-            tempIndices[0] = 0;
-            U32 idx = 1;
-
-            for (U32 k = 1; k < poly.vertexCount; k += 2)
-               tempIndices[idx++] = k;
-
-            for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
-               tempIndices[idx++] = k;
-         }
-         else if (poly.type == OptimizedPolyList::TriangleList)
-         {
-            for (U32 k = 0; k < poly.vertexCount; k++)
-               tempIndices[k] = k;
-         }
-         else
-            continue;
-
-         //got our data, now insert it into the correct buffer!
-         S32 bufferId = findBufferSetByMaterial(poly.material);
-
-         if (bufferId == -1)
-         {
-            //add a new buffer set if we didn't get a match!
-            BufferSet newSet;
-            newSet.surfaceMaterialId = poly.material;
-
-            mStaticBuffers.push_back(newSet);
-
-            bufferId = mStaticBuffers.size() - 1;
-         }
-
-         //see if this would push us over our buffer size limit, if it is, make a new buffer for this set
-         if (mStaticBuffers[bufferId].buffers.last().vertData.size() + 3 > BUFFER_SIZE
-            || mStaticBuffers[bufferId].buffers.last().primData.size() + 1 > BUFFER_SIZE)
-         {
-            //yep, we'll overstep with this, so spool up a new buffer in this set
-            BufferSet::Buffers newBuffer = BufferSet::Buffers();
-            mStaticBuffers[bufferId].buffers.push_back(newBuffer);
-         }
-
-         const U32& firstIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart];
-         const OptimizedPolyList::VertIndex& firstVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[firstIdx];
-
-         //Vector<Point3F> geomPoints = MeshRenderSystemInterface::all[i]->mGeometry.mPoints;
-         //Vector<Point3F> geomNormals = MeshRenderSystemInterface::all[i]->mGeometry.mNormals;
-         //Vector<Point2F> geoUVs = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s;
-
-         for (U32 k = 1; k < poly.vertexCount - 1; k++)
-         {
-            const U32& secondIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart + tempIndices[k]];
-            const U32& thirdIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart + tempIndices[k + 1]];
-
-            const OptimizedPolyList::VertIndex& secondVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[secondIdx];
-            const OptimizedPolyList::VertIndex& thirdVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[thirdIdx];
-
-            Point3F points[3];
-            points[0] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[firstVertIdx.vertIdx];
-            points[1] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[secondVertIdx.vertIdx];
-            points[2] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[thirdVertIdx.vertIdx];
-
-            Point3F normals[3];
-            normals[0] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[firstVertIdx.normalIdx];
-            normals[1] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[secondVertIdx.normalIdx];
-            normals[2] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[thirdVertIdx.normalIdx];
-
-            Point3F tangents[3];
-            tangents[0] = mCross(points[1] - points[0], normals[0]);
-            tangents[1] = mCross(points[2] - points[1], normals[1]);
-            tangents[2] = mCross(points[0] - points[2], normals[2]);
-
-            Point2F uvs[3];
-            uvs[0] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[firstVertIdx.uv0Idx];
-            uvs[1] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[secondVertIdx.uv0Idx];
-            uvs[2] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[thirdVertIdx.uv0Idx];
-
-            mStaticBuffers[bufferId].vertCount += 3;
-            mStaticBuffers[bufferId].primCount += 1;
-
-            for (U32 v = 0; v < 3; ++v)
-            {
-               //Build the vert and store it to the buffers!
-               GFXVertexPNTT bufVert;
-               bufVert.point = points[v];
-               bufVert.normal = normals[v];
-               bufVert.tangent = tangents[v];
-               bufVert.texCoord = uvs[v];
-
-               newBounds.extend(points[v]);
-
-               mStaticBuffers[bufferId].buffers.last().vertData.push_back(bufVert);
-
-               U32 vertPrimId = mStaticBuffers[bufferId].buffers.last().vertData.size() - 1;
-               mStaticBuffers[bufferId].buffers.last().primData.push_back(vertPrimId);
-
-               mStaticBuffers[bufferId].center += points[v];
-            }
-         }
-      }
-   }
-
-   //Now, iterate through the organized data and turn them into renderable buffers
-   for (U32 i = 0; i < mStaticBuffers.size(); i++)
-   {
-      for (U32 b = 0; b < mStaticBuffers[i].buffers.size(); b++)
-      {
-         BufferSet::Buffers& buffers = mStaticBuffers[i].buffers[b];
-
-         //if there's no data to be had in this buffer, just skip it
-         if (buffers.vertData.empty())
-            continue;
-
-         buffers.vertexBuffer.set(GFX, buffers.vertData.size(), GFXBufferTypeStatic);
-         GFXVertexPNTT *pVert = buffers.vertexBuffer.lock();
-
-         for (U32 v = 0; v < buffers.vertData.size(); v++)
-         {
-            pVert->normal = buffers.vertData[v].normal;
-            pVert->tangent = buffers.vertData[v].tangent;
-            //pVert->color = buffers.vertData[v].color;
-            pVert->point = buffers.vertData[v].point;
-            pVert->texCoord = buffers.vertData[v].texCoord;
-
-            pVert++;
-         }
-
-         buffers.vertexBuffer.unlock();
-
-         // Allocate PB
-         buffers.primitiveBuffer.set(GFX, buffers.primData.size(), buffers.primData.size() / 3, GFXBufferTypeStatic);
-
-         U16 *pIndex;
-         buffers.primitiveBuffer.lock(&pIndex);
-
-         for (U16 primDataIDx = 0; primDataIDx < buffers.primData.size(); primDataIDx++)
-         {
-            *pIndex = primDataIDx;
-            pIndex++;
-         }
-
-         buffers.primitiveBuffer.unlock();
-      }
-
-      mStaticBuffers[i].center /= mStaticBuffers[i].vertCount;
-   }
-
-   //mObjBox = newBounds;
-   //resetWorldBox();
-}
-
-U32 MeshRenderSystem::findBufferSetByMaterial(U32 matId)
-{
-   for (U32 i = 0; i < mStaticBuffers.size(); i++)
-   {
-      if (mStaticBuffers[i].surfaceMaterialId == matId)
-         return i;
-   }
-
-   return -1;
-}
+}

+ 1 - 146
Engine/source/T3D/systems/render/meshRenderSystem.h

@@ -38,12 +38,7 @@ public:
    Vector<matMap>  mChangingMaterials;
    Vector<matMap>  mMaterials;
 
-   //Static geometry stuff
-   bool                    mStatic;
-
-   OptimizedPolyList       mGeometry;
-
-   MeshRenderSystemInterface() : SystemInterface(), mShapeInstance(nullptr), mTransform(MatrixF::Identity), mScale(Point3F::One), mIsClient(false), mStatic(false)
+   MeshRenderSystemInterface() : SystemInterface(), mShapeInstance(nullptr), mTransform(MatrixF::Identity), mScale(Point3F::One), mIsClient(false)
    {
       mBounds = Box3F(1);
       mSphere = SphereF();
@@ -58,150 +53,10 @@ public:
 
 class MeshRenderSystem
 {
-protected:
-   /*struct StaticBatchElement
-   {
-      SimObject* owner;
-      OptimizedPolyList geometry;
-      String batchName;
-   };
-
-   static Vector<StaticBatchElement> mStaticElements;*/
-
-   //We retain the pushed geometry data for rendering here. It's static(unless forced to change through editing or whatnot)
-   //so rendering the batches is real fast
-   struct BufferMaterials
-   {
-      // The name of the Material we will use for rendering
-      String            mMaterialName;
-      // The actual Material instance
-      BaseMatInstance*  mMaterialInst;
-
-      BufferMaterials()
-      {
-         mMaterialName = "";
-         mMaterialInst = NULL;
-      }
-   };
-
-   static Vector<BufferMaterials> mBufferMaterials;
-
-   struct BufferSet
-   {
-      U32 surfaceMaterialId;
-
-      U32 vertCount;
-      U32 primCount;
-
-      Point3F center;
-
-      struct Buffers
-      {
-         U32 vertStart;
-         U32 primStart;
-         U32 vertCount;
-         U32 primCount;
-
-         Vector<GFXVertexPNTT> vertData;
-         Vector<U32> primData;
-
-         GFXVertexBufferHandle< GFXVertexPNTT > vertexBuffer;
-         GFXPrimitiveBufferHandle            primitiveBuffer;
-
-         Buffers()
-         {
-            vertStart = 0;
-            primStart = 0;
-            vertCount = 0;
-            primCount = 0;
-
-            vertexBuffer = NULL;
-            primitiveBuffer = NULL;
-         }
-      };
-
-      Vector<Buffers> buffers;
-
-      BufferSet()
-      {
-         Buffers newBuffer;
-         buffers.push_back(newBuffer);
-
-         surfaceMaterialId = 0;
-
-         vertCount = 0;
-         primCount = 0;
-
-         center = Point3F::Zero;
-      }
-   };
-
-   static Vector<BufferSet> mStaticBuffers;
-
 public:
-   /*virtual void prepRenderImage(SceneRenderState *state);
-
-   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, MaterialAsset* newMat);
-   bool setMatInstField(U32 slot, const char* field, const char* value);
-
-   virtual void onInspect();
-   virtual void onEndInspect();
-
-   virtual Vector<MatrixF> getNodeTransforms()
-   {
-      Vector<MatrixF> bob;
-      return bob;
-   }
-
-   virtual void setNodeTransforms(Vector<MatrixF> transforms)
-   {
-      return;
-   }*/
-
-   /*MeshRenderSystem()
-   {
-
-   }
-   virtual ~MeshRenderSystem()
-   {
-      smInterfaceList.clear();
-   }
-
-   static MeshComponentInterface* GetNewInterface()
-   {
-      smInterfaceList.increment();
-
-      return &smInterfaceList.last();
-   }
-
-   static void RemoveInterface(T* q)
-   {
-      smInterfaceList.erase(q);
-   }*/
-
    //Core render function, which does all the real work
    static void render(SceneManager *sceneManager, SceneRenderState* state);
 
    //Render our particular interface's data
    static void renderInterface(U32 interfaceIndex, SceneRenderState* state);
-
-   //Static Batch rendering
-   static void rebuildBuffers();
-
-   static U32 findBufferSetByMaterial(U32 matId);
 };

+ 5 - 0
Templates/BaseGame/game/core/components/Core_Components.module

@@ -11,4 +11,9 @@
            canSaveDynamicFields="true"
            Extension="asset.taml"
            Recurse="true" />
+	<AutoloadAssets
+	   canSave="true"
+	   canSaveDynamicFields="true"
+	   AssetType="ComponentAsset"
+	   Recurse="true" />
 </ModuleDefinition>

+ 2 - 3
Templates/BaseGame/game/core/components/components/game/controlObject.asset.taml

@@ -3,8 +3,7 @@
     canSaveDynamicFields="true"
     AssetName="ControlObjectComponentAsset"
     componentName="ControlObjectComponent"
-    componentClass="Component"
+    componentClass="ControlObjectComponent"
     friendlyName="Control Object"
     componentType="Game"
-    description="Allows the component owner to be controlled by a client."
-    scriptFile="core/components/components/game/controlObject.cs" />
+    description="Allows the component owner to be controlled by a client." />

+ 0 - 89
Templates/BaseGame/game/core/components/components/game/controlObject.cs

@@ -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.
-//-----------------------------------------------------------------------------
-
-//registerComponent("ControlObjectComponent", "Component", "Control Object", "Game", false, "Allows the behavior owner to operate as a camera.");
-
-function ControlObjectComponent::onAdd(%this)
-{
-   %this.addComponentField(clientOwner, "The shape to use for rendering", "int", "1", "");
-
-   %clientID = %this.getClientID();
-
-   if(%clientID && !isObject(%clientID.getControlObject()))
-      %clientID.setControlObject(%this.owner);
-}
-
-function ControlObjectComponent::onRemove(%this)
-{
-   %clientID = %this.getClientID();
-	
-   if(%clientID)
-      %clientID.setControlObject(0);
-}
-
-function ControlObjectComponent::onClientConnect(%this, %client)
-{
-   if(%this.isControlClient(%client) && !isObject(%client.getControlObject()))
-      %client.setControlObject(%this.owner);
-}
-
-function ControlObjectComponent::onClientDisconnect(%this, %client)
-{
-   if(%this.isControlClient(%client))
-      %client.setControlObject(0);
-}
-
-function ControlObjectComponent::getClientID(%this)
-{
-	return ClientGroup.getObject(%this.clientOwner-1);
-}
-
-function ControlObjectComponent::isControlClient(%this, %client)
-{
-	%clientID = ClientGroup.getObject(%this.clientOwner-1);
-	
-	if(%client.getID() == %clientID)
-		return true;
-	else
-	    return false;
-}
-
-function ControlObjectComponent::onInspectorUpdate(%this, %field)
-{
-   %clientID = %this.getClientID();
-	
-   if(%clientID && !isObject(%clientID.getControlObject()))
-      %clientID.setControlObject(%this.owner);
-}
-
-function switchControlObject(%client, %newControlEntity)
-{
-	if(!isObject(%client) || !isObject(%newControlEntity))
-		return error("SwitchControlObject: No client or target controller!");
-		
-	%control = %newControlEntity.getComponent(ControlObjectComponent);
-		
-	if(!isObject(%control))
-		return error("SwitchControlObject: Target controller has no conrol object behavior!");
-		
-    %client.setControlObject(%newControlEntity);
-}

+ 107 - 0
Templates/BaseGame/game/tools/componentEditor/gui/TypeMaskFieldGui.gui

@@ -0,0 +1,107 @@
+//--- OBJECT WRITE BEGIN ---
+%guiContent = new GuiControl(TypeMaskFieldGui) {
+   position = "0 0";
+   extent = "1024 768";
+   minExtent = "8 2";
+   horizSizing = "right";
+   vertSizing = "bottom";
+   profile = "ToolsGuiDefaultNonModalProfile";
+   visible = "1";
+   active = "1";
+   tooltipProfile = "GuiToolTipProfile";
+   hovertime = "1000";
+   isContainer = "1";
+   canSave = "1";
+   canSaveDynamicFields = "1";
+
+   new GuiWindowCtrl(TypeMaskFieldWindow) {
+      text = "Select Type Masks";
+      resizeWidth = "0";
+      resizeHeight = "1";
+      canMove = "1";
+      canClose = "1";
+      canMinimize = "0";
+      canMaximize = "0";
+      canCollapse = "0";
+      closeCommand = "Canvas.popDialog(TypeMaskFieldGui);";
+      edgeSnap = "1";
+      docking = "None";
+      margin = "4 4 4 4";
+      padding = "0 0 0 0";
+      anchorTop = "0";
+      anchorBottom = "0";
+      anchorLeft = "0";
+      anchorRight = "0";
+      position = "430 176";
+      extent = "161 250";
+      minExtent = "161 86";
+      horizSizing = "windowRelative";
+      vertSizing = "windowRelative";
+      profile = "ToolsGuiWindowProfile";
+      visible = "1";
+      active = "1";
+      tooltipProfile = "GuiToolTipProfile";
+      hovertime = "1000";
+      isContainer = "1";
+      canSave = "1";
+      canSaveDynamicFields = "0";
+
+      new GuiScrollCtrl() {
+         willFirstRespond = "1";
+         hScrollBar = "alwaysOff";
+         vScrollBar = "dynamic";
+         lockHorizScroll = "0";
+         lockVertScroll = "0";
+         constantThumbHeight = "0";
+         childMargin = "2 0";
+         mouseWheelScrollSpeed = "-1";
+         docking = "Client";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "0";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "1 9";
+         extent = "159 238";
+         minExtent = "8 2";
+         horizSizing = "width";
+         vertSizing = "height";
+         profile = "ToolsGuiScrollProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+
+         new GuiStackControl() {
+            stackingType = "Vertical";
+            horizStacking = "Left to Right";
+            vertStacking = "Top to Bottom";
+            padding = "-2";
+            dynamicSize = "1";
+            dynamicNonStackExtent = "0";
+            dynamicPos = "0";
+            changeChildSizeToFit = "1";
+            changeChildPosition = "1";
+            position = "3 1";
+            extent = "153 18";
+            minExtent = "16 16";
+            horizSizing = "width";
+            vertSizing = "bottom";
+            profile = "ToolsGuiDefaultProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "ToolsGuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "1";
+            internalName = "TypeMaskList";
+            canSave = "1";
+            canSaveDynamicFields = "0";
+         };
+      };
+   };
+};
+//--- OBJECT WRITE END ---

+ 276 - 0
Templates/BaseGame/game/tools/componentEditor/interface/materialFieldType.cs

@@ -0,0 +1,276 @@
+function GuiInspectorComponentGroup::buildMaterialField(%this, %component, %fieldName)
+{
+   %extent = 200;
+
+   %currentMaterial = %component.getFieldValue(%fieldName);
+
+   //if we don't have a new material set on this slot, just use the default
+   if(%currentMaterial $= "" || %currentMaterial == 0)
+	  %currentMaterial = %material;
+      
+   %container = new GuiControl() {
+      canSaveDynamicFields = "0";
+      Profile = "EditorContainerProfile";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "0 0";
+      Extent = "300 110";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = %tooltip;
+      tooltipProfile = "EditorToolTipProfile";
+   };
+
+   %labelControl = new GuiTextCtrl() {
+      canSaveDynamicFields = "0";
+      Profile = "EditorFontHLBold";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "16 3";
+      Extent = "100 18";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = %tooltip;
+      tooltipProfile = "EditorToolTipProfile";
+      text = %fieldName;
+      maxLength = "1024";
+   };
+
+   //
+   %imageContainer = new GuiControl(){
+      profile = "ToolsGuiDefaultProfile";
+      Position = "20 25";
+      Extent = "74 87";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      isContainer = "1";
+      
+      new GuiTextCtrl(){
+         position = "7 71";
+         profile = "ToolsGuiTextCenterProfile";
+         extent = "64 16";
+         text = %matName;
+      };
+   };
+   
+   %previewButton = new GuiBitmapButtonCtrl(){
+      internalName = %matName;
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      profile = "ToolsGuiButtonProfile";
+      position = "7 4";
+      extent = "64 64";
+      buttonType = "PushButton";
+      bitmap = "";
+      Command = "";
+      text = "Loading...";
+      useStates = false;
+	  tooltip = "Change material";
+      
+      new GuiBitmapButtonCtrl(){
+            HorizSizing = "right";
+            VertSizing = "bottom";
+            profile = "ToolsGuiButtonProfile";
+            position = "0 0";
+            extent = "64 64";
+            Variable = "";
+            buttonType = "toggleButton";
+            bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+            groupNum = "0";
+            text = "";
+			tooltip = "Change material";
+         }; 
+   }; 
+   
+   %previewBorder = new GuiButtonCtrl(){
+	     className = "materialFieldBtn";
+         internalName = %matName@"Border";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         profile = "ToolsGuiThumbHighlightButtonProfile";
+         position = "3 0";
+         extent = "72 88";
+         Variable = "";
+         buttonType = "toggleButton";
+         tooltip = %matName;
+         groupNum = "0";
+         text = "";
+		 Object = %component;
+		 targetField = %fieldName;
+   };
+   %previewBorder.Command = %previewBorder @ ".getMaterialName();"; 
+   
+   %imageContainer.add(%previewButton);  
+   %imageContainer.add(%previewBorder);
+   %container.add(%imageContainer);	
+   //
+   
+   %mapToLabel = new GuiTextCtrl() {
+      canSaveDynamicFields = "0";
+      Profile = "EditorFontHLBold";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "100 26";
+      Extent = %extent SPC "18";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = %tooltip;
+      tooltipProfile = "EditorToolTipProfile";
+      text = "Mapped to:" SPC %material.mapTo;
+      maxLength = "1024";
+   };
+
+   %editControl = new GuiTextEditCtrl() {
+      class = "BehaviorEdTextField";
+      internalName = %accessor @ "File";
+      canSaveDynamicFields = "0";
+      Profile = "EditorTextEdit";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "100 50";
+      Extent = %extent SPC "22";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = %tooltip;
+      tooltipProfile = "EditorToolTipProfile";
+      maxLength = "1024";
+      historySize = "0";
+      password = "0";
+      text = %currentMaterial;
+      
+      tabComplete = "0";
+      sinkAllKeyEvents = "0";
+      password = "0";
+      passwordMask = "*";
+      precision = %precision;
+      accessor = %accessor;
+      isProperty = true;
+      undoLabel = %fieldName;
+      object = %this.object;
+      useWords = false;
+   };
+   
+   %resetButton = new GuiButtonCtrl() {
+      canSaveDynamicFields = "0";
+      className = "materialFieldBtn";
+      Profile = "GuiButtonProfile";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "100 75";
+      Extent = (%extent * 0.3)-3 SPC "22";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = "Reset to default material";
+      tooltipProfile = "EditorToolTipProfile";
+      text = "Reset";
+      pathField = %editControl;
+   };
+
+   %editMatButton = new GuiButtonCtrl() {
+      canSaveDynamicFields = "0";
+      className = "materialFieldBtn";
+      Profile = "GuiButtonProfile";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = %resetButton.position.x + (%extent * 0.3) + 6 SPC "75";
+      Extent = (%extent * 0.6)-3 SPC "22";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = "Edit in Material Editor";
+      tooltipProfile = "EditorToolTipProfile";
+      text = "Open in Editor";
+      pathField = %editControl;
+   };
+   
+   %container.add(%mapToLabel);
+   %container.add(%labelControl);
+   %container.add(%editControl);
+   %container.add(%resetButton);
+   %container.add(%editMatButton);
+
+   //load the material
+   %matName = "";
+
+   // CustomMaterials are not available for selection
+   /*if ( !isObject( %currentMaterial ) || %currentMaterial.isMemberOfClass( "CustomMaterial" ) )
+      return;*/
+   %assetDef = AssetDatabase.acquireAsset(%currentMaterial);
+   
+   %materialDef = %assetDef.materialDefinitionName;
+
+   if( %materialDef.isMemberOfClass("TerrainMaterial") )
+   {
+      %matName = %currentMaterial.getInternalName();
+      
+      if( %materialDef.diffuseMap $= "")
+         %previewImage = "core/art/warnmat";
+      else
+         %previewImage = %materialDef.diffuseMap;
+   }
+   else if( %materialDef.toneMap[0] $= "" && %materialDef.diffuseMap[0] $= "" && !isObject(%materialDef.cubemap) )
+   {
+      %matName = %materialDef.name;
+      %previewImage = "core/art/warnmat";
+   }
+   else
+   {
+      %matName = %materialDef.name;
+      
+      if( %materialDef.toneMap[0] !$= "" )
+         %previewImage = %materialDef.toneMap[0];
+      else if( %materialDef.diffuseMap[0] !$= "" )
+         %previewImage = %materialDef.diffuseMap[0];
+      else if( %materialDef.cubemap.cubeFace[0] !$= "" )
+         %previewImage = %materialDef.cubemap.cubeFace[0];
+      
+      //%previewImage = MaterialEditorGui.searchForTexture( %material,  %previewImage );
+      
+      // were going to use a couple of string commands in order to properly
+      // find out what the img src path is 
+      // **NEW** this needs to be updated with the above, but has some timing issues
+      %materialDiffuse =  %previewImage;
+      %materialPath = %materialDef.getFilename();
+      
+      if( strchr( %materialDiffuse, "/") $= "" )
+      {
+         %k = 0;
+         while( strpos( %materialPath, "/", %k ) != -1 )
+         {
+            %foo = strpos( %materialPath, "/", %k );
+            %k = %foo + 1;
+         }
+      
+         %foobar = getSubStr( %materialPath , %k , 99 );
+         %previewImage =  strreplace( %materialPath, %foobar, %previewImage );
+      }
+      else
+         %previewImage =  strreplace( %materialPath, %materialPath, %previewImage );
+   }
+
+   %previewButton.setBitmap(%previewImage);
+   %previewButton.setText("");
+   
+   %this.stack.add(%container);
+}
+
+function materialFieldBtn::onClick(%this)
+{
+   AssetBrowser.showDialog("MaterialAsset", "", %this.Object, %this.targetField, "");  
+}
+
+function materialFieldBtn::setMaterial(%this, %matAssetName)
+{
+   
+}

+ 215 - 0
Templates/BaseGame/game/tools/componentEditor/interface/stateMachineField.cs

@@ -0,0 +1,215 @@
+function BehaviorFieldStack::createStateMachineEditor(%this, %behavior, %fieldIndex)
+{
+   %fieldInfo = %behavior.template.getBehaviorField(%fieldIndex);
+   %name = getField(%fieldInfo, 0);
+   
+   %button = new GuiButtonCtrl()
+   {
+      class = EditStateMachineBtn;
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "0 2";
+      Extent = (%this.extent.x - 8) SPC 13;
+      text = "Edit States";
+      tooltip = "Open window to edit the state machine";
+      behavior = %behavior;
+   };
+   %this.add(%button);
+}
+
+function EditStateMachineBtn::onClick(%this)
+{
+   Canvas.pushDialog(StateMachineEditor);
+   StateMachineEditor.behavior = %this.behavior;
+   
+   StateMachineEditor.open();
+}
+
+function StateMachineEditor::open(%this)
+{
+   //check our behavior and see if we have any existing state/field info to work with
+   //if we do, load those up first
+   for(%i = 0; %i < %this.behavior.stateMachine.count(); %i++)
+   {
+      %stateName = %this.behavior.stateMachine.getKey(%i);
+      
+      %this.addState(%stateName);
+   }    
+}
+
+function StateMachineEditor::addState(%this, %stateName)
+{
+   if(%stateName $= "")
+      %stateName = "New State";
+      
+   %state = new GuiControl() {
+      position = "0 0";
+      extent = "285 50";
+      horizSizing = "horizResizeWidth";
+      vertSizing = "vertResizeTop";
+      isContainer = "1";
+      
+      new GuiTextEditCtrl() {
+         position = "0 0";
+         extent = "100 15";
+         text = %stateName;
+      };
+      
+      new GuiButtonCtrl() {
+         //buttonMargin = "4 4";
+         text = "Remove State";
+         position = "184 0";
+         extent = "100 15";
+         //profile = "GuiButtonProfile";
+         command = "ScriptEditorGui.save();";
+      };
+      
+      new GuiSeparatorCtrl() {
+         position = "0 15";
+         extent = %this.extent.x SPC "10";
+         type = "horizontal";
+      };
+      
+      new GuiStackControl(%stateName@"StateStack")
+      {
+         //Profile = "EditorContainerProfile";
+         HorizSizing = "right";
+         VertSizing = "bottom";
+         Position = "0 25";
+         Extent = "285 20";
+         padding = 4;
+         
+         new GuiButtonCtrl() {
+            text = "Add field";
+            position = "3 0";
+            extent = "280 20";
+            horizSizing = "left";
+            vertSizing = "top";
+            command = "StateMachineEditor.addField("@%stateName@");";
+         };
+      };
+   };
+   
+   %this-->Stack.add(%state);
+   //%this-->stateStackScroll.computeSizes();
+}
+
+function StateMachineEditor::addField(%this, %stateName)
+{
+   %index = %this.behavior.stateMachine.count();
+   %field = new GuiControl() {
+      position = "0 0";
+      extent = "285 20";
+      horizSizing = "width";
+      vertSizing = "height";
+      isContainer = "1";
+      fieldValueCtrl = "";
+      fieldID = %index++;
+   };
+      
+   %fieldList = new GuiPopUpMenuCtrlEx() 
+   {
+      class = "stateMachineFieldList";
+      Profile = "GuiPopupMenuProfile";
+      HorizSizing = "width";
+      VertSizing = "bottom";
+      position = "0 1";
+      Extent = "120 18";
+      behavior = %this.behavior;
+   };
+   
+   %field.add(%fieldList);
+   %fieldList.refresh();
+   
+    (%stateName@"StateStack").addToStack(%field);
+      
+   %this-->Stack.updateStack();
+   %this-->stateStackScroll.computeSizes();
+   
+   %this.behavior.addStateField(%stateName, "", "");
+   
+   return %field;
+}
+
+//==============================================================================
+function stateMachineFieldList::refresh(%this)
+{
+   %this.clear();
+   
+   // Find all the types.
+   %count = getWordCount(%this.behavior.stateFields);   
+   %index = 0;
+   for (%j = 0; %j < %count; %j++)
+   {
+      %item = getWord(%this.behavior.stateFields, %j);
+      %this.add(%item, %index);
+      %this.fieldType[%index] = %item;
+      %index++;
+   }
+}
+
+function stateMachineFieldList::onSelect(%this)
+{
+   //if(%this.getParent().fieldValueCtrl $= "")
+   %this.fieldType = %this.fieldType[%this.getSelected()];
+   
+   if(%this.fieldType $= "transitionOnAnimEnd" || %this.fieldType $= "transitionOnAnimTrigger" 
+      || %this.fieldType $= "transitionOnTimeout")
+   {
+      %fieldCtrl = new GuiPopUpMenuCtrlEx() 
+      {
+         class = "stateMachineFieldList";
+         Profile = "GuiPopupMenuProfile";
+         HorizSizing = "width";
+         VertSizing = "bottom";
+         position = "124 1";
+         Extent = "120 18";
+      };
+   }
+   else if(%this.fieldType $= "animation")
+   {
+      %fieldCtrl = new GuiPopUpMenuCtrlEx() 
+      {
+         class = "stateMachineFieldList";
+         Profile = "GuiPopupMenuProfile";
+         HorizSizing = "width";
+         VertSizing = "bottom";
+         position = "124 1";
+         Extent = "120 18";
+      };
+      
+      %index = 0;
+      %animBhvr = %this.behavior.owner.getBehavior("AnimationController");
+      for(%i = 0; %i < %animBhvr.getAnimationCount(); %i++)
+      {
+         %item = %animBhvr.getAnimationName(%i);
+         %fieldCtrl.add(%item, %index);
+         %fieldCtrl.fieldValue[%index] = %item;
+         %index++;
+      }
+   }
+   else
+   {
+      %fieldCtrl = new GuiTextEditCtrl() {
+         position = "124 1";
+         extent = "120 10";
+         text = "";
+      };
+   }
+   
+   //get the state machine entry
+   %index = %this.getParent().fieldID;
+   
+   %oldValue = %this.behavior.stateMachine.getValue(%index);
+   %this.behavior.stateMachine.setValue(%fieldType SPC %oldValue.y);
+   
+   %this.getParent().add(%fieldCtrl);
+}
+
+//==============================================================================
+
+//Now for the unique field types
+/*function stateMachineFieldList::refresh(%this)
+{
+   
+}*/

+ 40 - 0
Templates/BaseGame/game/tools/componentEditor/interface/typeMaskFieldType.cs

@@ -0,0 +1,40 @@
+function GuiInspectorComponentGroup::buildTypeMaskField(%this, %component, %fieldName)
+{
+   %extent = 200;
+
+   %container = new GuiControl() {
+      canSaveDynamicFields = "0";
+      Profile = "EditorContainerProfile";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "0 0";
+      Extent = "300 110";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = %tooltip;
+      tooltipProfile = "EditorToolTipProfile";
+   };
+
+   %labelControl = new GuiTextCtrl() {
+      canSaveDynamicFields = "0";
+      Profile = "EditorFontHLBold";
+      HorizSizing = "right";
+      VertSizing = "bottom";
+      Position = "16 3";
+      Extent = "100 18";
+      MinExtent = "8 2";
+      canSave = "0";
+      Visible = "1";
+      hovertime = "100";
+      tooltip = %tooltip;
+      tooltipProfile = "EditorToolTipProfile";
+      text = %fieldName;
+      maxLength = "1024";
+   };
+   
+   %container.add(%container);
+
+   %this.stack.add(%container);
+}

+ 7 - 0
Templates/BaseGame/game/tools/componentEditor/main.cs

@@ -22,7 +22,14 @@
 
 //Scripts
 exec("./scripts/componentEditor.ed.cs");
+exec("./scripts/stateMachineEditor.ed.cs");
 exec("./scripts/superToolTipDlg.ed.cs");
 
 //gui
 exec("./gui/superToolTipDlg.ed.gui");
+exec("./gui/stateMachineDlg.ed.gui");
+
+//field types
+exec("./interface/materialFieldType.cs");
+exec("./interface/typeMaskFieldType.cs");
+exec("./interface/stateMachineField.cs");

+ 2 - 2
Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs

@@ -154,13 +154,13 @@ function QuickEditComponentList::onHotTrackItem( %this, %itemID )
       SuperTooltipDlg.setTitle(%componentObj.friendlyName);
       SuperTooltipDlg.addParam("", %componentObj.description @ "\n");
       
-      /*%fieldCount = %componentObj.getComponentFieldCount();
+      %fieldCount = %componentObj.getComponentFieldCount();
       for (%i = 0; %i < %fieldCount; %i++)
       {
          %name = getField(%componentObj.getComponentField(%i), 0);
 
          SuperTooltipDlg.addParam(%name, %description @ "\n");
-      }*/
+      }
       %position = %this.getGlobalPosition();
       SuperTooltipDlg.processTooltip( %position,0,1 );
       %this.opened = true;