Parcourir la source

Updates to component classes -
Shift from ghosted components to entity-managed for networking
Initial implementation of Systems through the Mesh Component

Areloch il y a 7 ans
Parent
commit
68efd8e22a

+ 37 - 49
Engine/source/T3D/components/animation/animationComponent.cpp

@@ -72,7 +72,6 @@ IMPLEMENT_CALLBACK(AnimationComponent, onAnimationTrigger, void, (Component* obj
 AnimationComponent::AnimationComponent() : Component()
 {
    mNetworked = true;
-   mNetFlags.set(Ghostable | ScopeAlways);
 
    mFriendlyName = "Animation(Component)";
    mComponentType = "Render";
@@ -223,31 +222,19 @@ U32 AnimationComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stre
 {
    U32 retMask = Parent::packUpdate(con, mask, stream);
 
-   //early test if we lack an owner, ghost-wise
-   //no point in trying, just re-queue the mask and go
-   if (!mOwner || con->getGhostIndex(mOwner) == -1)
+   /*for (int i = 0; i < MaxScriptThreads; i++)
    {
-      stream->writeFlag(false);
-      return retMask |= ThreadMask;
-   }
-   else
-   {
-      stream->writeFlag(true);
-
-      for (int i = 0; i < MaxScriptThreads; i++) 
+      Thread& st = mAnimationThreads[i];
+      if (stream->writeFlag((st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i))))
       {
-         Thread& st = mAnimationThreads[i];
-         if (stream->writeFlag( (st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)) ) ) 
-         {
-            stream->writeInt(st.sequence,ThreadSequenceBits);
-            stream->writeInt(st.state,2);
-            stream->write(st.timescale);
-            stream->write(st.position);
-            stream->writeFlag(st.atEnd);
-            stream->writeFlag(st.transition);
-         }
+         stream->writeInt(st.sequence, ThreadSequenceBits);
+         stream->writeInt(st.state, 2);
+         stream->write(st.timescale);
+         stream->write(st.position);
+         stream->writeFlag(st.atEnd);
+         stream->writeFlag(st.transition);
       }
-   }
+   }*/
 
    return retMask;
 }
@@ -256,29 +243,26 @@ void AnimationComponent::unpackUpdate(NetConnection *con, BitStream *stream)
 {
    Parent::unpackUpdate(con, stream);
 
-   if (stream->readFlag()) 
+   /*for (S32 i = 0; i < MaxScriptThreads; i++) 
    {
-      for (S32 i = 0; i < MaxScriptThreads; i++) 
+      if (stream->readFlag()) 
       {
-         if (stream->readFlag()) 
-         {
-            Thread& st = mAnimationThreads[i];
-            U32 seq = stream->readInt(ThreadSequenceBits);
-            st.state = stream->readInt(2);
-            stream->read( &st.timescale );
-            stream->read( &st.position );
-            st.atEnd = stream->readFlag();
-            bool transition = stream->readFlag();
-
-            if (!st.thread || st.sequence != seq && st.state != Thread::Destroy)
-               setThreadSequence(i, seq, false, transition);
-            else
-               updateThread(st);
-
-         }
+         Thread& st = mAnimationThreads[i];
+         U32 seq = stream->readInt(ThreadSequenceBits);
+         st.state = stream->readInt(2);
+         stream->read( &st.timescale );
+         stream->read( &st.position );
+         st.atEnd = stream->readFlag();
+         bool transition = stream->readFlag();
+
+         if (!st.thread || st.sequence != seq && st.state != Thread::Destroy)
+            setThreadSequence(i, seq, false, transition);
+         else
+            updateThread(st);
       }
-   }
+   }*/
 }
+
 void AnimationComponent::processTick()
 {
    Parent::processTick();
@@ -327,9 +311,6 @@ const char *AnimationComponent::getThreadSequenceName(U32 slot)
 
 bool AnimationComponent::setThreadSequence(U32 slot, S32 seq, bool reset, bool transition, F32 transTime)
 {
-   if (!mOwnerShapeInstance)
-      return false;
-
    Thread& st = mAnimationThreads[slot];
    if (st.thread && st.sequence == seq && st.state == Thread::Play && !reset)
       return true;
@@ -340,7 +321,6 @@ bool AnimationComponent::setThreadSequence(U32 slot, S32 seq, bool reset, bool t
 
    if (seq < MaxSequenceIndex)
    {
-      setMaskBits(-1);
       setMaskBits(ThreadMaskN << slot);
       st.sequence = seq;
       st.transition = transition;
@@ -647,7 +627,7 @@ void AnimationComponent::advanceThreads(F32 dt)
             st.atEnd = true;
             updateThread(st);
 
-            if (!isGhost())
+            if (!isClientObject())
             {
                Con::executef(this, "onAnimationEnd", st.thread->getSequenceName());
             }
@@ -660,7 +640,7 @@ void AnimationComponent::advanceThreads(F32 dt)
             mOwnerShapeInstance->advanceTime(dt, st.thread);
          }
 
-         if (mOwnerShapeInstance && !isGhost())
+         if (mOwnerShapeInstance && !isClientObject())
          {
             for (U32 i = 1; i < 32; i++)
             {
@@ -672,8 +652,16 @@ void AnimationComponent::advanceThreads(F32 dt)
             }
          }
 
-         if (isGhost())
+         if (isClientObject())
+         {
             mOwnerShapeInstance->animate();
+            /*mOwnerShapeInstance->animateGround();
+            MatrixF groundTransform = mOwnerShapeInstance->getGroundTransform();
+            if (groundTransform != MatrixF::Identity)
+            {
+               mOwner->setPosition(groundTransform.getPosition());
+            }*/
+         }
       }
    }
 }

+ 8 - 3
Engine/source/T3D/components/camera/cameraComponent.cpp

@@ -79,6 +79,7 @@ CameraComponent::CameraComponent() : Component()
    mTargetNode = "";
 
    mUseParentTransform = true;
+   mNetworked = true;
 
    mFriendlyName = "Camera(Component)";
 }
@@ -202,7 +203,7 @@ void CameraComponent::setCameraFov(F32 fov)
 void CameraComponent::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)
 {
    // update the camera query
-   query->camera = this;
+   query->camera = mOwner;//this;
 
    if(GameConnection * con = dynamic_cast<GameConnection*>(cr))
    {
@@ -357,7 +358,8 @@ U32 CameraComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
          mTargetNodeIdx = nodeIndex;
       }
 
-      stream->writeInt(mTargetNodeIdx, 32);
+      if(stream->writeFlag(mTargetNodeIdx > -1))
+         stream->writeInt(mTargetNodeIdx, 32);
       //send offsets here
 
       stream->writeCompressedPoint(mPosOffset);
@@ -382,7 +384,10 @@ void CameraComponent::unpackUpdate(NetConnection *con, BitStream *stream)
 
    if(stream->readFlag())
    {
-      mTargetNodeIdx = stream->readInt(32);
+      if (stream->readFlag())
+         mTargetNodeIdx = stream->readInt(32);
+      else
+         mTargetNodeIdx = -1;
 
       stream->readCompressedPoint(&mPosOffset);
 

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

@@ -125,8 +125,6 @@ EndImplementEnumType;
 //
 CollisionComponent::CollisionComponent() : Component()
 {
-   mNetFlags.set(Ghostable | ScopeAlways);
-
    mFriendlyName = "Collision(Component)";
 
    mOwnerRenderInterface = NULL;

+ 22 - 15
Engine/source/T3D/components/component.cpp

@@ -31,6 +31,7 @@
 #include "console/engineAPI.h"
 #include "sim/netConnection.h"
 #include "console/consoleInternal.h"
+#include "T3D/assets/MaterialAsset.h"
 
 #define DECLARE_NATIVE_COMPONENT( ComponentType )                   \
 	 Component* staticComponentTemplate = new ComponentType; \
@@ -52,7 +53,6 @@ Component::Component()
 
    mNetworked = false;
 
-
    // [tom, 1/12/2007] We manage the memory for the description since it
    // could be loaded from a file and thus massive. This is accomplished with
    // protected fields, but since they still call Con::getData() the field
@@ -66,7 +66,7 @@ Component::Component()
 
    mOriginatingAssetId = StringTable->EmptyString();
 
-   mNetFlags.set(Ghostable);
+   mIsServerObject = true;
 }
 
 Component::~Component()
@@ -198,7 +198,6 @@ void Component::onComponentRemove()
    {
       mOwner->onComponentAdded.remove(this, &Component::componentAddedToOwner);
       mOwner->onComponentRemoved.remove(this, &Component::componentRemovedFromOwner);
-      mOwner->onTransformSet.remove(this, &Component::ownerTransformSet);
    }
 
    mOwner = NULL;
@@ -212,7 +211,6 @@ void Component::setOwner(Entity* owner)
    {
       mOwner->onComponentAdded.remove(this, &Component::componentAddedToOwner);
       mOwner->onComponentRemoved.remove(this, &Component::componentRemovedFromOwner);
-      mOwner->onTransformSet.remove(this, &Component::ownerTransformSet);
 
       mOwner->removeComponent(this, false);
    }
@@ -223,11 +221,18 @@ void Component::setOwner(Entity* owner)
    {
       mOwner->onComponentAdded.notify(this, &Component::componentAddedToOwner);
       mOwner->onComponentRemoved.notify(this, &Component::componentRemovedFromOwner);
-      mOwner->onTransformSet.notify(this, &Component::ownerTransformSet);
    }
 
    if (isServerObject())
+   {
       setMaskBits(OwnerMask);
+
+      //if we have any outstanding maskbits, push them along to have the network update happen on the entity
+      if (mDirtyMaskBits != 0 && mOwner)
+      {
+         mOwner->setMaskBits(Entity::ComponentsUpdateMask);
+      }
+   }
 }
 
 void Component::componentAddedToOwner(Component *comp)
@@ -240,16 +245,19 @@ void Component::componentRemovedFromOwner(Component *comp)
    return;
 }
 
-void Component::ownerTransformSet(MatrixF *mat)
+void Component::setMaskBits(U32 orMask)
 {
-   return;
+   AssertFatal(orMask != 0, "Invalid net mask bits set.");
+   
+   if (mOwner)
+      mOwner->setComponentNetMask(this, orMask);
 }
 
 U32 Component::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
 {
-   U32 retMask = Parent::packUpdate(con, mask, stream);
+   U32 retMask = 0;
 
-   if (mask & OwnerMask)
+   /*if (mask & OwnerMask)
    {
       if (mOwner != NULL)
       {
@@ -274,7 +282,7 @@ U32 Component::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
       }
    }
    else
-      stream->writeFlag(false);
+      stream->writeFlag(false);*/
 
    if (stream->writeFlag(mask & EnableMask))
    {
@@ -299,9 +307,7 @@ U32 Component::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
 
 void Component::unpackUpdate(NetConnection *con, BitStream *stream)
 {
-   Parent::unpackUpdate(con, stream);
-
-   if (stream->readFlag())
+   /*if (stream->readFlag())
    {
       if (stream->readFlag())
       {
@@ -317,7 +323,7 @@ void Component::unpackUpdate(NetConnection *con, BitStream *stream)
          //it's being nulled out
          setOwner(NULL);
       }
-   }
+   }*/
 
    if (stream->readFlag())
    {
@@ -467,7 +473,7 @@ void Component::addComponentField(const char *fieldName, const char *desc, const
    else if (fieldType == StringTable->insert("vector"))
       fieldTypeMask = TypePoint3F;
    else if (fieldType == StringTable->insert("material"))
-      fieldTypeMask = TypeMaterialName;
+      fieldTypeMask = TypeMaterialAssetPtr;
    else if (fieldType == StringTable->insert("image"))
       fieldTypeMask = TypeImageFilename;
    else if (fieldType == StringTable->insert("shape"))
@@ -488,6 +494,7 @@ void Component::addComponentField(const char *fieldName, const char *desc, const
       fieldTypeMask = TypeGameObjectAssetPtr;
    else
       fieldTypeMask = TypeString;
+   field.mFieldTypeName = fieldType;
 
    field.mFieldType = fieldTypeMask;
 

+ 18 - 3
Engine/source/T3D/components/component.h

@@ -64,9 +64,9 @@ struct ComponentField
 /// 
 /// 
 //////////////////////////////////////////////////////////////////////////
-class Component : public NetObject, public UpdateInterface
+class Component : public SimObject, public UpdateInterface
 {
-   typedef NetObject Parent;
+   typedef SimObject Parent;
 
 protected:
    StringTableEntry mFriendlyName;
@@ -92,6 +92,10 @@ protected:
    StringTableEntry		      mOriginatingAssetId;
    AssetPtr<ComponentAsset>  mOriginatingAsset;
 
+   U32                        mDirtyMaskBits;
+
+   bool                 mIsServerObject;
+
 public:
    Component();
    virtual ~Component();
@@ -113,7 +117,8 @@ public:
    //This is called when a different component is removed from our owner entity
    virtual void componentRemovedFromOwner(Component *comp);  
 
-   virtual void ownerTransformSet(MatrixF *mat);
+   //Overridden by components that actually care
+   virtual void ownerTransformSet(MatrixF *mat) {}
 
    void setOwner(Entity* pOwner);
    inline Entity *getOwner() { return mOwner ? mOwner : NULL; }
@@ -190,6 +195,16 @@ public:
       NextFreeMask = BIT(5)
    };
 
+   virtual void setMaskBits(U32 orMask);
+   virtual void clearMaskBits() {
+      mDirtyMaskBits = 0;
+   }
+
+   bool isServerObject() { return mIsServerObject; }
+   bool isClientObject() { return !mIsServerObject; }
+
+   void setIsServerObject(bool isServerObj) { mIsServerObject = isServerObj; }
+
    virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
    virtual void unpackUpdate(NetConnection *con, BitStream *stream);
    /// @}

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

@@ -57,7 +57,6 @@ StateMachineComponent::StateMachineComponent() : Component()
 
    //doesn't need to be networked
    mNetworked = false;
-   mNetFlags.clear();
 }
 
 StateMachineComponent::~StateMachineComponent()

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

@@ -288,7 +288,7 @@ void RigidBodyComponent::processTick()
       return;
 
    // SINGLE PLAYER HACK!!!!
-   if (PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject())
+   /*if (PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject())
    {
       RigidBodyComponent *servObj = (RigidBodyComponent*)getServerObject();
       mOwner->setTransform(servObj->mState.getTransform());
@@ -296,7 +296,7 @@ void RigidBodyComponent::processTick()
       mRenderState[1] = servObj->mRenderState[1];
 
       return;
-   }
+   }*/
 
    // Store the last render state.
    mRenderState[0] = mRenderState[1];

+ 237 - 134
Engine/source/T3D/components/render/meshComponent.cpp

@@ -45,52 +45,49 @@
 #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()
 {
-   mShapeName = StringTable->insert("");
-   mShapeAsset = StringTable->insert("");
-   mShapeInstance = NULL;
-
-   mChangingMaterials.clear();
-
-   mMaterials.clear();
-
    mFriendlyName = "Mesh Component";
    mComponentType = "Render";
 
    mDescription = getDescriptionText("Causes the object to render a non-animating 3d shape using the file provided.");
 
    mNetworked = true;
-   mNetFlags.set(Ghostable | ScopeAlways);
-}
 
-MeshComponent::~MeshComponent(){}
+   mShapeName = StringTable->EmptyString();
+   mShapeAsset = StringTable->EmptyString();
 
-IMPLEMENT_CO_NETOBJECT_V1(MeshComponent);
+   mMeshAsset = StringTable->EmptyString();
+   mMeshAssetId = StringTable->EmptyString();
 
-//==========================================================================================
-void MeshComponent::boneObject::addObject(SimObject* object)
-{
-   SceneObject* sc = dynamic_cast<SceneObject*>(object);
-
-   if(sc && mOwner)
-   {
-      if(TSShape* shape = mOwner->getShape())
-      {
-         S32 nodeID = shape->findNode(mBoneName);
+   mInterfaceData = new MeshRenderSystemInterface();
 
-         //we may have a offset on the shape's center
-         //so make sure we accomodate for that when setting up the mount offsets
-         MatrixF mat = mOwner->getNodeTransform(nodeID);
+   mRenderMode = Individual;
+}
 
-         mOwner->getOwner()->mountObject(sc, nodeID, mat);
-      }
-   }
+MeshComponent::~MeshComponent()
+{
+   if (mInterfaceData)
+      SAFE_DELETE(mInterfaceData);
 }
 
+IMPLEMENT_CO_NETOBJECT_V1(MeshComponent);
+
+//==========================================================================================
 bool MeshComponent::onAdd()
 {
    if(! Parent::onAdd())
@@ -106,6 +103,12 @@ void MeshComponent::onComponentAdd()
 {
    Parent::onComponentAdd();
 
+   if (isClientObject())
+      mInterfaceData->mIsClient = true;
+
+  // if (mInterfaceData != nullptr)
+  //   mInterfaceData->mIsClient = isClientObject();
+
    //get the default shape, if any
    updateShape();
 }
@@ -113,10 +116,6 @@ void MeshComponent::onComponentAdd()
 void MeshComponent::onRemove()
 {
    Parent::onRemove();
-
-   mMeshAsset.clear();
-
-   SAFE_DELETE(mShapeInstance);
 }
 
 void MeshComponent::onComponentRemove()
@@ -135,9 +134,14 @@ 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", TypeAssetId, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn, 
+   addProtectedField("MeshAsset", TypeShapeAssetPtr, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn,
       "The asset Id used for the mesh.", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
    endGroup("Model");
 }
@@ -165,6 +169,9 @@ bool MeshComponent::_setShape( void *object, const char *index, const char *data
 bool MeshComponent::setMeshAsset(const char* assetName)
 {
    // Fetch the asset Id.
+   if (mInterfaceData == nullptr)
+      return false;
+
    mMeshAssetId = StringTable->insert(assetName);
 
    mMeshAsset = mMeshAssetId;
@@ -183,9 +190,129 @@ bool MeshComponent::setMeshAsset(const char* assetName)
    return true;
 }
 
+void MeshComponent::updateShape()
+{
+   if (mInterfaceData == nullptr)
+      return;
+
+   //if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
+   if ((mShapeName && mShapeName[0] != '\0') || (mMeshAssetId && mMeshAssetId[0] != '\0'))
+
+   {
+      if (mMeshAsset == NULL)
+         return;
+
+      mShape = mMeshAsset->getShape();
+
+      if (!mMeshAsset->getShape())
+         return;
+
+      setupShape();
+
+      //Do this on both the server and client
+      S32 materialCount = mMeshAsset->getShape()->materialList->getMaterialNameList().size();
+
+      if (isServerObject())
+      {
+         //we need to update the editor
+         for (U32 i = 0; i < mFields.size(); i++)
+         {
+            //find any with the materialslot title and clear them out
+            if (FindMatch::isMatch("MaterialSlot*", mFields[i].mFieldName, false))
+            {
+               setDataField(mFields[i].mFieldName, NULL, "");
+               mFields.erase(i);
+               continue;
+            }
+         }
+
+         //next, get a listing of our materials in the shape, and build our field list for them
+         char matFieldName[128];
+
+         if (materialCount > 0)
+            mComponentGroup = StringTable->insert("Materials");
+
+         for (U32 i = 0; i < materialCount; i++)
+         {
+            String materialname = mMeshAsset->getShape()->materialList->getMaterialName(i);
+            if (materialname == String("ShapeBounds"))
+               continue;
+
+            dSprintf(matFieldName, 128, "MaterialSlot%d", i);
+
+            addComponentField(matFieldName, "A material used in the shape file", "Material", materialname, "");
+         }
+
+         if (materialCount > 0)
+            mComponentGroup = "";
+      }
+
+      if (mOwner != NULL)
+      {
+         Point3F min, max, pos;
+         pos = mOwner->getPosition();
+
+         mOwner->getWorldToObj().mulP(pos);
+
+         min = mMeshAsset->getShape()->bounds.minExtents;
+         max = mMeshAsset->getShape()->bounds.maxExtents;
+
+         if (mInterfaceData)
+         {
+            mInterfaceData->mBounds.set(min, max);
+            mInterfaceData->mScale = mOwner->getScale();
+            mInterfaceData->mTransform = mOwner->getRenderTransform();
+         }
+
+         mOwner->setObjectBox(Box3F(min, max));
+
+         mOwner->resetWorldBox();
+
+         if (mOwner->getSceneManager() != NULL)
+            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);
+   }
+}
+
+void MeshComponent::setupShape()
+{
+   mInterfaceData->mShapeInstance = new TSShapeInstance(mMeshAsset->getShape(), true);
+}
+
 void MeshComponent::_onResourceChanged( const Torque::Path &path )
 {
-   if ( path != Torque::Path( mShapeName ) )
+   if (mInterfaceData == nullptr)
+      return;
+
+   String filePath;
+   if (mMeshAsset)
+      filePath = Torque::Path(mMeshAsset->getShapeFilename());
+
+   if (!mMeshAsset || path != Torque::Path(mMeshAsset->getShapeFilename()) )
       return;
 
    updateShape();
@@ -216,6 +343,8 @@ 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 ))
@@ -226,7 +355,7 @@ U32 MeshComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
       {
          stream->writeInt(mChangingMaterials[i].slot, 16);
 
-         NetStringHandle matNameStr = mChangingMaterials[i].matName.c_str();
+         NetStringHandle matNameStr = mChangingMaterials[i].assetId.c_str();
          con->packNetStringHandleU(stream, matNameStr);
       }
 
@@ -243,6 +372,8 @@ void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
    if(stream->readFlag())
    {
       mShapeName = stream->readSTString();
+
+      mRenderMode = (RenderMode)stream->readInt(8);
       setMeshAsset(mShapeName);
       updateShape();
    }
@@ -256,7 +387,10 @@ void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
       {
          matMap newMatMap;
          newMatMap.slot = stream->readInt(16);
-         newMatMap.matName = String(con->unpackNetStringHandleU(stream).getString());
+         newMatMap.assetId = String(con->unpackNetStringHandleU(stream).getString());
+
+         //do the lookup, now
+         newMatMap.matAsset = AssetDatabase.acquireAsset<MaterialAsset>(newMatMap.assetId);
 
          mChangingMaterials.push_back(newMatMap);
       }
@@ -267,7 +401,7 @@ void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
 
 void MeshComponent::prepRenderImage( SceneRenderState *state )
 {
-   if (!mEnabled || !mOwner || !mShapeInstance)
+   /*if (!mEnabled || !mOwner || !mShapeInstance)
       return;
 
    Point3F cameraOffset;
@@ -300,114 +434,41 @@ void MeshComponent::prepRenderImage( SceneRenderState *state )
    rdata.setLightQuery(&query);
 
    MatrixF mat = mOwner->getRenderTransform();
-   Point3F renderPos = mat.getPosition();
-   EulerF renderRot = mat.toEuler();
-   mat.scale(objScale);
-   GFX->setWorldMatrix(mat);
-
-   mShapeInstance->render(rdata);
-}
-
-void MeshComponent::updateShape()
-{
-   bool isServer = isServerObject();
 
-   if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
+   if (mOwner->isMounted())
    {
-      if (mMeshAsset == NULL)
-         return;
-
-      mShape = mMeshAsset->getShape();
-
-      if (!mShape)
-         return;
-
-      setupShape();
-
-      //Do this on both the server and client
-      S32 materialCount = mShape->materialList->getMaterialNameList().size();
-
-      if(isServerObject())
-      {
-         //we need to update the editor
-         for (U32 i = 0; i < mFields.size(); i++)
-         {
-            //find any with the materialslot title and clear them out
-            if (FindMatch::isMatch("MaterialSlot*", mFields[i].mFieldName, false))
-            {
-               setDataField(mFields[i].mFieldName, NULL, "");
-               mFields.erase(i);
-               continue;
-            }
-         }
-
-         //next, get a listing of our materials in the shape, and build our field list for them
-         char matFieldName[128];
-
-         if(materialCount > 0)
-            mComponentGroup = StringTable->insert("Materials");
-
-         for(U32 i=0; i < materialCount; i++)
-         {
-            String materialname = mShape->materialList->getMaterialName(i);
-            if(materialname == String("ShapeBounds"))
-               continue;
+      MatrixF wrldPos = mOwner->getWorldTransform();
+      Point3F wrldPosPos = wrldPos.getPosition();
 
-            dSprintf(matFieldName, 128, "MaterialSlot%d", i);
-            
-            addComponentField(matFieldName, "A material used in the shape file", "TypeAssetId", materialname, "");
-         }
-
-         if(materialCount > 0)
-            mComponentGroup = "";
-      }
-
-      if(mOwner != NULL)
-      {
-         Point3F min, max, pos;
-         pos = mOwner->getPosition();
-
-         mOwner->getWorldToObj().mulP(pos);
+      Point3F mntPs = mat.getPosition();
+      EulerF mntRt = RotationF(mat).asEulerF();
 
-         min = mShape->bounds.minExtents;
-         max = mShape->bounds.maxExtents;
-
-         mShapeBounds.set(min, max);
-
-         mOwner->setObjectBox(Box3F(min, max));
-
-         if( mOwner->getSceneManager() != NULL )
-            mOwner->getSceneManager()->notifyObjectDirty( mOwner );
-      }
-
-      //finally, notify that our shape was changed
-      onShapeInstanceChanged.trigger(this);
+      bool tr = true;
    }
-}
 
-void MeshComponent::setupShape()
-{
-   mShapeInstance = new TSShapeInstance(mShape, true);
+   mat.scale(objScale);
+   GFX->setWorldMatrix(mat);
+
+   mShapeInstance->render(rdata);*/
 }
 
 void MeshComponent::updateMaterials()
 {
-   if (mChangingMaterials.empty() || !mShape)
+   if (mChangingMaterials.empty() || !mMeshAsset->getShape())
       return;
 
-   TSMaterialList* pMatList = mShapeInstance->getMaterialList();
+   TSMaterialList* pMatList = mInterfaceData->mShapeInstance->getMaterialList();
    pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
 
    const Vector<String> &materialNames = pMatList->getMaterialNameList();
    for ( S32 i = 0; i < materialNames.size(); i++ )
    {
-      const String &pName = materialNames[i];
-
       for(U32 m=0; m < mChangingMaterials.size(); m++)
       {
          if(mChangingMaterials[m].slot == i)
          {
-            pMatList->renameMaterial( i, mChangingMaterials[m].matName );
+            //Fetch the actual material asset
+            pMatList->renameMaterial( i, mChangingMaterials[m].matAsset->getMaterialDefinitionName());
          }
       }
 
@@ -415,22 +476,31 @@ void MeshComponent::updateMaterials()
    }
 
    // Initialize the material instances
-   mShapeInstance->initMaterialList();
+   mInterfaceData->mShapeInstance->initMaterialList();
 }
 
 MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
 {
-   if (mShape)
+   if (mInterfaceData != nullptr && mMeshAsset->getShape())
    {
       S32 nodeCount = getShape()->nodes.size();
 
       if(nodeIdx >= 0 && nodeIdx < nodeCount)
       {
          //animate();
-         MatrixF mountTransform = mShapeInstance->mNodeTransforms[nodeIdx];
-         mountTransform.mul(mOwner->getRenderTransform());
+         MatrixF nodeTransform = mInterfaceData->mShapeInstance->mNodeTransforms[nodeIdx];
+         const Point3F& scale = mOwner->getScale();
+
+         // The position of the node needs to be scaled.
+         Point3F position = nodeTransform.getPosition();
+         position.convolve(scale);
+         nodeTransform.setPosition(position);
+
+         MatrixF finalTransform = MatrixF::Identity;
+
+         finalTransform.mul(mOwner->getRenderTransform(), nodeTransform);
 
-         return mountTransform;
+         return finalTransform;
       }
    }
 
@@ -439,7 +509,7 @@ MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
 
 S32 MeshComponent::getNodeByName(String nodeName)
 {
-   if (mShape)
+   if (mMeshAsset->getShape())
    {
       S32 nodeIdx = getShape()->findNode(nodeName);
 
@@ -485,12 +555,18 @@ void MeshComponent::onDynamicModified(const char* slotName, const char* newValue
       if(slot == -1)
          return;
 
+      //Safe to assume the inbound value for the material will be a MaterialAsset, so lets do a lookup on the name
+      MaterialAsset* matAsset = AssetDatabase.acquireAsset<MaterialAsset>(newValue);
+      if (!matAsset)
+         return;
+
       bool found = false;
       for(U32 i=0; i < mChangingMaterials.size(); i++)
       {
          if(mChangingMaterials[i].slot == slot)
          {
-            mChangingMaterials[i].matName = String(newValue);
+            mChangingMaterials[i].matAsset = matAsset;
+            mChangingMaterials[i].assetId = newValue;
             found = true;
          }
       }
@@ -499,7 +575,8 @@ void MeshComponent::onDynamicModified(const char* slotName, const char* newValue
       {
          matMap newMatMap;
          newMatMap.slot = slot;
-         newMatMap.matName = String(newValue);
+         newMatMap.matAsset = matAsset;
+         newMatMap.assetId = newValue;
 
          mChangingMaterials.push_back(newMatMap);
       }
@@ -510,14 +587,31 @@ void MeshComponent::onDynamicModified(const char* slotName, const char* newValue
    Parent::onDynamicModified(slotName, newValue);
 }
 
-void MeshComponent::changeMaterial(U32 slot, const char* newMat)
+void MeshComponent::changeMaterial(U32 slot, MaterialAsset* newMat)
 {
    
    char fieldName[512];
 
    //update our respective field
    dSprintf(fieldName, 512, "materialSlot%d", slot);
-   setDataField(fieldName, NULL, newMat);
+   setDataField(fieldName, NULL, newMat->getAssetId());
+}
+
+bool MeshComponent::setMatInstField(U32 slot, const char* field, const char* value)
+{
+   TSMaterialList* pMatList = mInterfaceData->mShapeInstance->getMaterialList();
+   pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
+
+   MaterialParameters* params = pMatList->getMaterialInst(slot)->getMaterialParameters();
+
+   if (pMatList->getMaterialInst(slot)->getFeatures().hasFeature(MFT_DiffuseColor))
+   {
+      MaterialParameterHandle* handle = pMatList->getMaterialInst(slot)->getMaterialParameterHandle("DiffuseColor");
+
+      params->set(handle, LinearColorF(0, 0, 0));
+   }
+
+   return true;
 }
 
 void MeshComponent::onInspect()
@@ -526,4 +620,13 @@ void MeshComponent::onInspect()
 
 void MeshComponent::onEndInspect()
 {
+}
+
+void MeshComponent::ownerTransformSet(MatrixF *mat)
+{
+   if (mInterfaceData != nullptr)
+   {
+      MatrixF newTransform = *mat;
+      mInterfaceData->mTransform = newTransform;
+   }
 }

+ 25 - 16
Engine/source/T3D/components/render/meshComponent.h

@@ -60,6 +60,8 @@
 #include "gfx/gfxVertexFormat.h"
 #endif
 
+#include "T3D/systems/render/meshRenderSystem.h"
+
 class TSShapeInstance;
 class SceneRenderState;
 //////////////////////////////////////////////////////////////////////////
@@ -84,37 +86,38 @@ protected:
    StringTableEntry		mShapeName;
    StringTableEntry		mShapeAsset;
    TSShape*		         mShape;
-   Box3F						mShapeBounds;
+   //Box3F						mShapeBounds;
    Point3F					mCenterOffset;
 
+   MeshRenderSystemInterface*  mInterfaceData;
+
    struct matMap
    {
-      String matName;
+      MaterialAsset* matAsset;
+      String assetId;
       U32 slot;
    };
 
    Vector<matMap>  mChangingMaterials;
    Vector<matMap>  mMaterials;
 
-   class boneObject : public SimGroup
+public:
+   enum RenderMode
    {
-      MeshComponent *mOwner;
-   public:
-      boneObject(MeshComponent *owner){ mOwner = owner; }
-
-      StringTableEntry mBoneName;
-      S32 mItemID;
-
-      virtual void addObject(SimObject *obj);
+      Individual = 0,
+      DynamicBatch,
+      StaticBatch,
+      Instanced
    };
 
-   Vector<boneObject*> mNodesList;
+protected:
+   RenderMode           mRenderMode;
 
 public:
    StringTableEntry       mMeshAssetId;
    AssetPtr<ShapeAsset>   mMeshAsset;
 
-   TSShapeInstance*       mShapeInstance;
+   //TSShapeInstance*       mShapeInstance;
 
 public:
    MeshComponent();
@@ -132,7 +135,7 @@ public:
    virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
    virtual void unpackUpdate(NetConnection *con, BitStream *stream);
 
-   Box3F getShapeBounds() { return mShapeBounds; }
+   Box3F getShapeBounds() { return mInterfaceData->mBounds; }
 
    virtual MatrixF getNodeTransform(S32 nodeIdx);
    S32 getNodeByName(String nodeName);
@@ -144,6 +147,8 @@ public:
    virtual void onComponentRemove();
    virtual void onComponentAdd();
 
+   virtual void ownerTransformSet(MatrixF *mat);
+
    static bool _setMesh(void *object, const char *index, const char *data);
    static bool _setShape(void *object, const char *index, const char *data);
    const char* _getShape(void *object, const char *data);
@@ -151,7 +156,7 @@ public:
    bool setMeshAsset(const char* assetName);
 
    virtual TSShape* getShape() { if (mMeshAsset)  return mMeshAsset->getShape(); else return NULL; }
-   virtual TSShapeInstance* getShapeInstance() { return mShapeInstance; }
+   virtual TSShapeInstance* getShapeInstance() { return mInterfaceData->mShapeInstance; }
 
    Resource<TSShape> getShapeResource() { return mMeshAsset->getShapeResource(); }
 
@@ -163,7 +168,8 @@ public:
 
    virtual void onDynamicModified(const char* slotName, const char* newValue);
 
-   void changeMaterial(U32 slot, const char* newMat);
+   void changeMaterial(U32 slot, MaterialAsset* newMat);
+   bool setMatInstField(U32 slot, const char* field, const char* value);
 
    virtual void onInspect();
    virtual void onEndInspect();
@@ -180,4 +186,7 @@ public:
    }
 };
 
+typedef MeshComponent::RenderMode BatchingMode;
+DefineEnumType(BatchingMode);
+
 #endif

+ 29 - 1
Engine/source/T3D/components/render/meshComponent_ScriptBinding.h

@@ -126,6 +126,28 @@ DefineEngineMethod(MeshComponent, getNodePosition, Point3F,
    return Point3F(0, 0, 0);
 }
 
+DefineEngineMethod(MeshComponent, getNodeRotation, EulerF,
+   (S32 node), (-1),
+   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
+
+   "@param objB  Object to mount onto us\n"
+   "@param slot  Mount slot ID\n"
+   "@param txfm (optional) mount offset transform\n"
+   "@return true if successful, false if failed (objB is not valid)")
+{
+   if (node != -1)
+   {
+      //BUG: Unsure how it broke, but atm the default transform passed in here is rotated 180 degrees. This doesn't happen
+      //for the SceneObject mountobject method. Hackish, but for now, just default to a clean MatrixF::Identity
+      //object->mountObjectToNode( objB, node, /*MatrixF::Identity*/txfm.getMatrix() );
+      RotationF mat = object->getNodeTransform(node);
+
+      return mat.asEulerF(RotationF::Degrees);
+   }
+
+   return EulerF(0, 0, 0);
+}
+
 DefineEngineMethod(MeshComponent, getNodeByName, S32,
    (String nodeName), ,
    "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
@@ -148,8 +170,14 @@ DefineEngineMethod(MeshComponent, getNodeByName, S32,
    return -1;
 }
 
-DefineEngineMethod(MeshComponent, changeMaterial, void, (U32 slot, const char* newMat), (0, ""),
+DefineEngineMethod(MeshComponent, changeMaterial, void, (U32 slot, MaterialAsset* newMat), (0, nullAsType<MaterialAsset*>()),
    "@brief Change one of the materials on the shape.\n\n")
 {
    object->changeMaterial(slot, newMat);
+}
+
+DefineEngineMethod(MeshComponent, setMatInstField, bool, (U32 slot, const char* field, const char* value), (0, "", ""),
+   "@brief Change one of the materials on the shape.\n\n")
+{
+   return object->setMatInstField(slot, field, value);
 }

+ 265 - 58
Engine/source/T3D/entity.cpp

@@ -47,7 +47,9 @@
 #include "T3D/gameBase/std/stdMoveList.h"
 
 #include "T3D/prefab.h"
+#include "T3D/gameBase/gameConnection.h"
 
+#include <thread>
 //
 #include "gfx/sim/debugDraw.h"
 //
@@ -118,6 +120,8 @@ Entity::Entity()
 
    mInitialized = false;
 
+   mLifetimeMS = 0;
+
    mGameObjectAssetId = StringTable->insert("");
 
 }
@@ -147,6 +151,10 @@ void Entity::initPersistFields()
 
    endGroup("Transform");
 
+   addGroup("Misc");
+   addField("LifetimeMS", TypeS32, Offset(mLifetimeMS, Entity), "Object world orientation.");
+   endGroup("Misc");
+
    addGroup("GameObject");
    addProtectedField("gameObjectName", TypeGameObjectAssetPtr, Offset(mGameObjectAsset, Entity), &_setGameObject, &defaultProtectedGetFn,
       "The asset Id used for the game object this entity is based on.");
@@ -231,8 +239,19 @@ bool Entity::onAdd()
    addToScene();
 
    //Make sure we get positioned
-   setMaskBits(TransformMask);
-   setMaskBits(NamespaceMask);
+   if (isServerObject())
+   {
+      setMaskBits(TransformMask);
+      setMaskBits(NamespaceMask);
+   }
+   else
+   {
+      //We can shortcut the initialization here because stuff generally ghosts down in order, and onPostAdd isn't called on ghosts.
+      onPostAdd();
+   }
+
+   if (mLifetimeMS != 0)
+      mStartTimeMS = Platform::getRealMilliseconds();
 
    return true;
 }
@@ -245,6 +264,8 @@ void Entity::onRemove()
 
    onDataSet.removeAll();
 
+   mGameObjectAsset.clear();
+
    Parent::onRemove();
 }
 
@@ -258,6 +279,27 @@ void Entity::onPostAdd()
       mComponents[i]->onComponentAdd();
    }
 
+   //Set up the networked components
+   mNetworkedComponents.clear();
+   for (U32 i = 0; i < mComponents.size(); i++)
+   {
+      if (mComponents[i]->isNetworked())
+      {
+         NetworkedComponent netComp;
+         netComp.componentIndex = i;
+         netComp.updateState = NetworkedComponent::Adding;
+         netComp.updateMaskBits = -1;
+
+         mNetworkedComponents.push_back(netComp);
+      }
+   }
+
+   if (!mNetworkedComponents.empty())
+   {
+      setMaskBits(AddComponentsMask);
+      setMaskBits(ComponentsUpdateMask);
+   }
+
    if (isMethod("onAdd"))
       Con::executef(this, "onAdd");
 }
@@ -396,6 +438,14 @@ void Entity::processTick(const Move* move)
       mDelta.rot[1] = mRot.asQuatF();
 
       setTransform(getPosition(), mRot);
+
+      //Lifetime test
+      if (mLifetimeMS != 0)
+      {
+         S32 currentTime = Platform::getRealMilliseconds();
+         if (currentTime - mStartTimeMS >= mLifetimeMS)
+            deleteObject();
+      }
    }
 }
 
@@ -446,62 +496,107 @@ U32 Entity::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
       mathWrite(*stream, mObjBox);
    }
 
-   //pass our behaviors around
-   if (mask & ComponentsMask || mask & InitialUpdateMask)
+   if (stream->writeFlag(mask & AddComponentsMask))
    {
-      stream->writeFlag(true);
-      //now, we run through a list of our to-be-sent behaviors and begin sending them
-      //if any fail, we keep our list and re-queue the mask
-      S32 componentCount = mToLoadComponents.size();
+      U32 toAddComponentCount = 0;
 
-      //build our 'ready' list
-      //This requires both the instance and the instances' template to be prepped(if the template hasn't been ghosted,
-      //then we know we shouldn't be passing the instance's ghosts around yet)
-      U32 ghostedCompCnt = 0;
-      for (U32 i = 0; i < componentCount; i++)
+      for (U32 i = 0; i < mNetworkedComponents.size(); i++)
       {
-         if (con->getGhostIndex(mToLoadComponents[i]) != -1)
-            ghostedCompCnt++;
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::Adding)
+         {
+            toAddComponentCount++;
+         }
       }
 
-      if (ghostedCompCnt != 0)
+      //you reaaaaally shouldn't have >255 networked components on a single entity
+      stream->writeInt(toAddComponentCount, 8);
+
+      for (U32 i = 0; i < mNetworkedComponents.size(); i++)
       {
-         stream->writeFlag(true);
+         NetworkedComponent::UpdateState state = mNetworkedComponents[i].updateState;
 
-         stream->writeFlag(mStartComponentUpdate);
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::Adding)
+         {
+            const char* className = mComponents[mNetworkedComponents[i].componentIndex]->getClassName();
+            stream->writeString(className, strlen(className));
 
-         //if not all the behaviors have been ghosted, we'll need another pass
-         if (ghostedCompCnt != componentCount)
-            retMask |= ComponentsMask;
+            mNetworkedComponents[i].updateState = NetworkedComponent::Updating;
+         }
+      }
+   }
 
-         //write the currently ghosted behavior count
-         stream->writeInt(ghostedCompCnt, 16);
+   if (stream->writeFlag(mask & RemoveComponentsMask))
+   {
+      /*U32 toRemoveComponentCount = 0;
 
-         for (U32 i = 0; i < mToLoadComponents.size(); i++)
+      for (U32 i = 0; i < mNetworkedComponents.size(); i++)
+      {
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::Adding)
          {
-            //now fetch them and pass the ghost
-            S32 ghostIndex = con->getGhostIndex(mToLoadComponents[i]);
-            if (ghostIndex != -1)
-            {
-               stream->writeInt(ghostIndex, NetConnection::GhostIdBitSize);
-               mToLoadComponents.erase(i);
-               i--;
+            toRemoveComponentCount++;
+         }
+      }
+
+      //you reaaaaally shouldn't have >255 networked components on a single entity
+      stream->writeInt(toRemoveComponentCount, 8);
+
+      for (U32 i = 0; i < mNetworkedComponents.size(); i++)
+      {
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::Removing)
+         {
+            stream->writeInt(i, 16);
+         }
+      }*/
+
+      /*for (U32 i = 0; i < mNetworkedComponents.size(); i++)
+      {
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::UpdateState::Removing)
+         {
+            removeComponent(mComponents[mNetworkedComponents[i].componentIndex], true);
+            mNetworkedComponents.erase(i);
+            i--;
 
-               mStartComponentUpdate = false;
-            }
+         }
+      }*/
+   }
+
+   //Update our components
+   if (stream->writeFlag(mask & ComponentsUpdateMask))
+   {
+      U32 toUpdateComponentCount = 0;
+
+      for (U32 i = 0; i < mNetworkedComponents.size(); i++)
+      {
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::Updating)
+         {
+            toUpdateComponentCount++;
          }
       }
-      else if (componentCount)
+
+      //you reaaaaally shouldn't have >255 networked components on a single entity
+      stream->writeInt(toUpdateComponentCount, 8);
+
+      bool forceUpdate = false;
+
+      for (U32 i = 0; i < mNetworkedComponents.size(); i++)
       {
-         //on the odd chance we have behaviors to ghost, but NONE of them have been yet, just set the flag now
-         stream->writeFlag(false);
-         retMask |= ComponentsMask;
+         if (mNetworkedComponents[i].updateState == NetworkedComponent::Updating)
+         {
+            stream->writeInt(i, 8);
+
+            mNetworkedComponents[i].updateMaskBits = mComponents[mNetworkedComponents[i].componentIndex]->packUpdate(con, mNetworkedComponents[i].updateMaskBits, stream);
+
+            if (mNetworkedComponents[i].updateMaskBits != 0)
+               forceUpdate = true;
+            else
+               mNetworkedComponents[i].updateState = NetworkedComponent::None;
+         }
       }
-      else
-         stream->writeFlag(false);
+
+      //If we have leftover, we need to re-iterate our packing
+      if (forceUpdate)
+         setMaskBits(ComponentsUpdateMask);
    }
-   else
-      stream->writeFlag(false);
 
    /*if (stream->writeFlag(mask & NamespaceMask))
    {
@@ -594,25 +689,52 @@ void Entity::unpackUpdate(NetConnection *con, BitStream *stream)
       resetWorldBox();
    }
 
+   //AddComponentMask
    if (stream->readFlag())
    {
-      //are we passing any behaviors currently?
-      if (stream->readFlag())
+      U32 addedComponentCount = stream->readInt(8);
+
+      for (U32 i = 0; i < addedComponentCount; i++)
       {
-         //if we've just started the update, clear our behaviors
-         if (stream->readFlag())
-            clearComponents(false);
+         char className[256] = "";
+         stream->readString(className);
+
+         //Change to components, so iterate our list and create any new components
+         // Well, looks like we have to create a new object.
+         const char* componentType = className;
+
+         ConsoleObject *object = ConsoleObject::create(componentType);
 
-         S32 componentCount = stream->readInt(16);
+         // Finally, set currentNewObject to point to the new one.
+         Component* newComponent = dynamic_cast<Component *>(object);
 
-         for (U32 i = 0; i < componentCount; i++)
+         if (newComponent)
          {
-            S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
-            addComponent(dynamic_cast<Component*>(con->resolveGhost(gIndex)));
+            addComponent(newComponent);
          }
       }
    }
 
+   //RemoveComponentMask
+   if (stream->readFlag())
+   {
+      
+   }
+
+   //ComponentUpdateMask
+   if (stream->readFlag())
+   {
+      U32 updatingComponents = stream->readInt(8);
+
+      for (U32 i = 0; i < updatingComponents; i++)
+      {
+         U32 updateComponentIndex = stream->readInt(8);
+
+         Component* comp = mComponents[updateComponentIndex];
+         comp->unpackUpdate(con, stream);
+      }
+   }
+
    /*if (stream->readFlag())
    {
       if (stream->readFlag())
@@ -640,6 +762,26 @@ void Entity::unpackUpdate(NetConnection *con, BitStream *stream)
    }*/
 }
 
+void Entity::setComponentNetMask(Component* comp, U32 mask)
+{
+   setMaskBits(Entity::ComponentsUpdateMask);
+
+   for (U32 i = 0; i < mNetworkedComponents.size(); i++)
+   {
+      U32 netCompId = mComponents[mNetworkedComponents[i].componentIndex]->getId();
+      U32 compId = comp->getId();
+
+      if (netCompId == compId && 
+         (mNetworkedComponents[i].updateState == NetworkedComponent::None || mNetworkedComponents[i].updateState == NetworkedComponent::Updating))
+      {
+         mNetworkedComponents[i].updateState = NetworkedComponent::Updating;
+         mNetworkedComponents[i].updateMaskBits |= mask;
+
+         break;
+      }
+   }
+}
+
 //Manipulation
 void Entity::setTransform(const MatrixF &mat)
 {
@@ -758,7 +900,11 @@ void Entity::setTransform(Point3F position, RotationF rotation)
       // Update the transforms.
       Parent::setTransform(newMat);
 
-      onTransformSet.trigger(&newMat);
+      U32 compCount = mComponents.size();
+      for (U32 i = 0; i < compCount; ++i)
+      {
+         mComponents[i]->ownerTransformSet(&newMat);
+      }
 
       Point3F newPos = newMat.getPosition();
       RotationF newRot = newMat;
@@ -800,7 +946,11 @@ void Entity::setRenderTransform(Point3F position, RotationF rotation)
 
       Parent::setRenderTransform(newMat);
 
-      onTransformSet.trigger(&newMat);
+      U32 compCount = mComponents.size();
+      for (U32 i = 0; i < compCount; ++i)
+      {
+         mComponents[i]->ownerTransformSet(&newMat);
+      }
    }
 }
 
@@ -1155,11 +1305,28 @@ bool Entity::addComponent(Component *comp)
    // Register the component with this owner.
    comp->setOwner(this);
 
+   comp->setIsServerObject(isServerObject());
+
    //if we've already been added and this is being added after the fact(at runtime), 
    //then just go ahead and call it's onComponentAdd so it can get to work
-   if (mInitialized)
+   //if (mInitialized)
+   {
       comp->onComponentAdd();
 
+      if (comp->isNetworked())
+      {
+         NetworkedComponent netComp;
+         netComp.componentIndex = mComponents.size() - 1;
+         netComp.updateState = NetworkedComponent::Adding;
+         netComp.updateMaskBits = -1;
+
+         mNetworkedComponents.push_back(netComp);
+
+         setMaskBits(AddComponentsMask);
+         setMaskBits(ComponentsUpdateMask);
+      }
+   }
+
    onComponentAdded.trigger(comp);
 
    return true;
@@ -1269,7 +1436,7 @@ Component *Entity::getComponent(String componentType)
          Namespace *NS = comp->getNamespace();
 
          //we shouldn't ever go past Component into net object, as we're no longer dealing with component classes
-         while (dStrcmp(NS->getName(), "NetObject"))
+         while (dStrcmp(NS->getName(), "SimObject"))
          {
             String namespaceName = NS->getName();
 
@@ -1497,7 +1664,8 @@ void Entity::notifyComponents(String signalFunction, String argA, String argB, S
 
 void Entity::setComponentsDirty()
 {
-   if (mToLoadComponents.empty())
+   bool tmp = true;
+   /*if (mToLoadComponents.empty())
       mStartComponentUpdate = true;
 
    //we need to build a list of behaviors that need to be pushed across the network
@@ -1522,7 +1690,7 @@ void Entity::setComponentsDirty()
       }
    }
 
-   setMaskBits(ComponentsMask);
+   setMaskBits(ComponentsMask);*/
 }
 
 void Entity::setComponentDirty(Component *comp, bool forceUpdate)
@@ -1654,7 +1822,7 @@ ConsoleMethod(Entity, addComponents, void, 2, 2, "() - Add all fielded behaviors
    object->addComponents();
 }*/
 
-ConsoleMethod(Entity, addComponent, bool, 3, 3, "(Component* bi) - Add a behavior to the object\n"
+ConsoleMethod(Entity, addComponent, bool, 3, 3, "(ComponentInstance bi) - Add a behavior to the object\n"
    "@param bi The behavior instance to add"
    "@return (bool success) Whether or not the behavior was successfully added")
 {
@@ -1679,7 +1847,7 @@ ConsoleMethod(Entity, addComponent, bool, 3, 3, "(Component* bi) - Add a behavio
    return false;
 }
 
-ConsoleMethod(Entity, removeComponent, bool, 3, 4, "(Component* bi, [bool deleteBehavior = true])\n"
+ConsoleMethod(Entity, removeComponent, bool, 3, 4, "(ComponentInstance bi, [bool deleteBehavior = true])\n"
    "@param bi The behavior instance to remove\n"
    "@param deleteBehavior Whether or not to delete the behavior\n"
    "@return (bool success) Whether the behavior was successfully removed")
@@ -1834,4 +2002,43 @@ DefineConsoleMethod(Entity, notify, void, (String signalFunction, String argA, S
       return;
 
    object->notifyComponents(signalFunction, argA, argB, argC, argD, argE);
+}
+
+DefineConsoleFunction(findEntitiesByTag, const char*, (SimGroup* searchingGroup, String tags), (nullAsType<SimGroup*>(), ""),
+"Finds all entities that have the provided tags.\n"
+"@param searchingGroup The SimGroup to search inside. If null, we'll search the entire dictionary(this can be slow!).\n"
+"@param tags Word delimited list of tags to search for. If multiple tags are included, the list is eclusively parsed, requiring all tags provided to be found on an entity for a match.\n"
+"@return A word list of IDs of entities that match the tag search terms.")
+{
+   //if (tags.isEmpty())
+      return "";
+
+   /*if (searchingGroup == nullptr)
+   {
+      searchingGroup = Sim::getRootGroup();
+   }
+
+   StringTableEntry entityStr = StringTable->insert("Entity");
+
+   std::thread threadBob;
+
+   std::thread::id a = threadBob.get_id();
+   std::thread::id b = std::this_thread::get_id().;
+
+   if (a == b)
+   {
+      //do
+   }
+
+   for (SimGroup::iterator itr = searchingGroup->begin(); itr != searchingGroup->end(); itr++)
+   {
+      Entity* ent = dynamic_cast<Entity*>((*itr));
+
+      if (ent != nullptr)
+      {
+         ent->mTags.
+      }
+   }
+
+   object->notifyComponents(signalFunction, argA, argB, argC, argD, argE);*/
 }

+ 37 - 6
Engine/source/T3D/entity.h

@@ -58,7 +58,27 @@ private:
 
    Vector<Component*>         mComponents;
 
-   Vector<Component*>         mToLoadComponents;
+   //Bit of helper data to let us track and manage the adding, removal and updating of networked components
+   struct NetworkedComponent
+   {
+      U32 componentIndex;
+
+      enum UpdateState
+      {
+         None,
+         Adding,
+         Removing,
+         Updating
+      };
+
+      UpdateState updateState;
+
+      U32 updateMaskBits;
+   };
+
+   Vector<NetworkedComponent> mNetworkedComponents;
+
+   U32                        mComponentNetMask;
 
    bool                       mStartComponentUpdate;
 
@@ -69,10 +89,12 @@ private:
 
    bool mInitialized;
 
+   String mTags;
+
    Signal< void(Component*) > onComponentAdded;
    Signal< void(Component*) > onComponentRemoved;
 
-   Signal< void(MatrixF*) > onTransformSet;
+   S32                       mLifetimeMS;
 
 protected:
 
@@ -105,10 +127,12 @@ public:
    {
       TransformMask = Parent::NextFreeMask << 0,
       BoundsMask = Parent::NextFreeMask << 1,
-      ComponentsMask = Parent::NextFreeMask << 2,
-      NoWarpMask = Parent::NextFreeMask << 3,
-      NamespaceMask = Parent::NextFreeMask << 4,
-      NextFreeMask = Parent::NextFreeMask << 5
+      ComponentsUpdateMask = Parent::NextFreeMask << 2,
+      AddComponentsMask = Parent::NextFreeMask << 3,
+      RemoveComponentsMask = Parent::NextFreeMask << 4,
+      NoWarpMask = Parent::NextFreeMask << 5,
+      NamespaceMask = Parent::NextFreeMask << 6,
+      NextFreeMask = Parent::NextFreeMask << 7
    };
 
    StateDelta mDelta;
@@ -116,6 +140,8 @@ public:
 
    Move lastMove;
 
+   S32      mStartTimeMS;
+
    //
    Entity();
    ~Entity();
@@ -163,6 +189,9 @@ public:
    /// @param  client   Client that is now controlling this object
    virtual void setControllingClient(GameConnection *client);
 
+   //
+   //Networking
+   //
    // NetObject
    U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
    void unpackUpdate(NetConnection *conn, BitStream *stream);
@@ -170,6 +199,8 @@ public:
    void setComponentsDirty();
    void setComponentDirty(Component *comp, bool forceUpdate = false);
 
+   void setComponentNetMask(Component* comp, U32 mask);
+
    //Components
    virtual bool deferAddingComponents() const { return true; }
 

+ 5 - 0
Engine/source/scene/sceneManager.cpp

@@ -38,6 +38,9 @@
 #include "T3D/gameBase/gameConnection.h"
 #include "math/mathUtils.h"
 
+#include "T3D/components/render/renderComponentInterface.h"
+#include "T3D/systems/render/meshRenderSystem.h"
+
 // For player object bounds workaround.
 #include "T3D/player.h"
 
@@ -358,6 +361,8 @@ void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZ
    if( gEditingMission && state->isDiffusePass() )
       objectMask = EDITOR_RENDER_TYPEMASK;
 
+   MeshRenderSystem::render(this, state);
+
    // Update the zoning state and traverse zones.
 
    if( getZoneManager() )

+ 0 - 11
Engine/source/scene/sceneRenderState.cpp

@@ -106,17 +106,6 @@ void SceneRenderState::renderObjects( SceneObject** objects, U32 numObjects )
       object->prepRenderImage( this );
    }
 
-   U32 interfaceCount = RenderComponentInterface::all.size();
-   for (U32 i = 0; i < RenderComponentInterface::all.size(); i++)
-   {
-      Component* comp = dynamic_cast<Component*>(RenderComponentInterface::all[i]);
-
-      if (comp->isClientObject() && comp->isActive())
-      {
-         RenderComponentInterface::all[i]->prepRenderImage(this);
-      }
-   }
-
    PROFILE_END();
 
    // Render what the objects have batched.

+ 10 - 0
Templates/BaseGame/game/core/CoreComponents.cs

@@ -0,0 +1,10 @@
+
+function CoreComponentsModule::onCreate(%this)
+{
+   %classList = enumerateConsoleClasses( "Component" );
+
+   foreach$( %componentClass in %classList )
+   {
+      echo("Native Component of type: " @ %componentClass);
+   }  
+}