Browse Source

Maximum amount of sorted instances can be configured in Renderer. By default 1000.
Moved the NetworkState pointer back to Serializable, as there was not much practical benefit.

Lasse Öörni 13 years ago
parent
commit
31e027b354

+ 1 - 0
Docs/ScriptAPI.dox

@@ -2340,6 +2340,7 @@ Properties:<br>
 - bool reuseShadowMaps
 - bool reuseShadowMaps
 - bool dynamicInstancing
 - bool dynamicInstancing
 - int maxInstanceTriangles
 - int maxInstanceTriangles
+- int maxSortedInstances
 - int maxOccluderTriangles
 - int maxOccluderTriangles
 - int occlusionBufferSize
 - int occlusionBufferSize
 - float occluderSizeThreshold
 - float occluderSizeThreshold

+ 2 - 0
Engine/Engine/GraphicsAPI.cpp

@@ -845,6 +845,8 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "bool get_dynamicInstancing() const", asMETHOD(Renderer, GetDynamicInstancing), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool get_dynamicInstancing() const", asMETHOD(Renderer, GetDynamicInstancing), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxInstanceTriangles(int)", asMETHOD(Renderer, SetMaxInstanceTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxInstanceTriangles(int)", asMETHOD(Renderer, SetMaxInstanceTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxInstanceTriangles() const", asMETHOD(Renderer, GetMaxInstanceTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxInstanceTriangles() const", asMETHOD(Renderer, GetMaxInstanceTriangles), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_maxSortedInstances(int)", asMETHOD(Renderer, SetMaxSortedInstances), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "int get_maxSortedInstances() const", asMETHOD(Renderer, GetMaxSortedInstances), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxOccluderTriangles(int)", asMETHOD(Renderer, SetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxOccluderTriangles(int)", asMETHOD(Renderer, SetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxOccluderTriangles() const", asMETHOD(Renderer, GetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxOccluderTriangles() const", asMETHOD(Renderer, GetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_occlusionBufferSize(int)", asMETHOD(Renderer, SetOcclusionBufferSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_occlusionBufferSize(int)", asMETHOD(Renderer, SetOcclusionBufferSize), asCALL_THISCALL);

+ 34 - 4
Engine/Graphics/Batch.cpp

@@ -62,7 +62,7 @@ inline bool CompareInstancesFrontToBack(const InstanceData& lhs, const InstanceD
 
 
 inline bool CompareBatchGroupsFrontToBack(BatchGroup* lhs, BatchGroup* rhs)
 inline bool CompareBatchGroupsFrontToBack(BatchGroup* lhs, BatchGroup* rhs)
 {
 {
-    return lhs->instances_[0].distance_ < rhs->instances_[0].distance_;
+    return lhs->distance_ < rhs->distance_;
 }
 }
 
 
 void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split, Renderer* renderer, const Vector3& translation)
 void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split, Renderer* renderer, const Vector3& translation)
@@ -741,13 +741,14 @@ void BatchGroup::Draw(Graphics* graphics, Renderer* renderer) const
     }
     }
 }
 }
 
 
-void BatchQueue::Clear()
+void BatchQueue::Clear(int maxSortedInstances)
 {
 {
     batches_.Clear();
     batches_.Clear();
     sortedBaseBatches_.Clear();
     sortedBaseBatches_.Clear();
     sortedBatches_.Clear();
     sortedBatches_.Clear();
     baseBatchGroups_.Clear();
     baseBatchGroups_.Clear();
     batchGroups_.Clear();
     batchGroups_.Clear();
+    maxSortedInstances_ = maxSortedInstances;
 }
 }
 
 
 void BatchQueue::SortBackToFront()
 void BatchQueue::SortBackToFront()
@@ -792,9 +793,38 @@ void BatchQueue::SortFrontToBack()
     
     
     // Sort each group front to back
     // Sort each group front to back
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
-        Sort(i->second_.instances_.Begin(), i->second_.instances_.End(), CompareInstancesFrontToBack);
+    {
+        if (i->second_.instances_.Size() <= maxSortedInstances_)
+        {
+            Sort(i->second_.instances_.Begin(), i->second_.instances_.End(), CompareInstancesFrontToBack);
+            if (i->second_.instances_.Size())
+                i->second_.distance_ = i->second_.instances_[0].distance_;
+        }
+        else
+        {
+            float minDistance = M_INFINITY;
+            for (PODVector<InstanceData>::ConstIterator j = i->second_.instances_.Begin(); j != i->second_.instances_.End(); ++j)
+                minDistance = Min(minDistance, j->distance_);
+            i->second_.distance_ = minDistance;
+        }
+    }
+    
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
-        Sort(i->second_.instances_.Begin(), i->second_.instances_.End(), CompareInstancesFrontToBack);
+    {
+        if (i->second_.instances_.Size() <= maxSortedInstances_)
+        {
+            Sort(i->second_.instances_.Begin(), i->second_.instances_.End(), CompareInstancesFrontToBack);
+            if (i->second_.instances_.Size())
+                i->second_.distance_ = i->second_.instances_[0].distance_;
+        }
+        else
+        {
+            float minDistance = M_INFINITY;
+            for (PODVector<InstanceData>::ConstIterator j = i->second_.instances_.Begin(); j != i->second_.instances_.End(); ++j)
+                minDistance = Min(minDistance, j->distance_);
+            i->second_.distance_ = minDistance;
+        }
+    }
     
     
     // Now sort batch groups by the distance of the first batch
     // Now sort batch groups by the distance of the first batch
     sortedBaseBatchGroups_.Resize(baseBatchGroups_.Size());
     sortedBaseBatchGroups_.Resize(baseBatchGroups_.Size());

+ 6 - 4
Engine/Graphics/Batch.h

@@ -197,8 +197,8 @@ struct BatchGroupKey
 struct BatchQueue
 struct BatchQueue
 {
 {
 public:
 public:
-    /// Clear everything.
-    void Clear();
+    /// Clear for new frame by clearing all groups and batches.
+    void Clear(int maxSortedInstances);
     /// Sort non-instanced draw calls back to front.
     /// Sort non-instanced draw calls back to front.
     void SortBackToFront();
     void SortBackToFront();
     /// Sort instanced and non-instanced draw calls front to back.
     /// Sort instanced and non-instanced draw calls front to back.
@@ -228,6 +228,8 @@ public:
     PODVector<BatchGroup*> sortedBaseBatchGroups_;
     PODVector<BatchGroup*> sortedBaseBatchGroups_;
     /// Sorted instanced draw calls.
     /// Sorted instanced draw calls.
     PODVector<BatchGroup*> sortedBatchGroups_;
     PODVector<BatchGroup*> sortedBatchGroups_;
+    /// Maximum sorted instances.
+    unsigned maxSortedInstances_;
 };
 };
 
 
 /// Queue for shadow map draw calls
 /// Queue for shadow map draw calls
@@ -250,10 +252,10 @@ struct LightBatchQueue
 {
 {
     /// Per-pixel light.
     /// Per-pixel light.
     Light* light_;
     Light* light_;
-    /// Lit geometry draw calls.
-    BatchQueue litBatches_;
     /// Shadow map depth texture.
     /// Shadow map depth texture.
     Texture2D* shadowMap_;
     Texture2D* shadowMap_;
+    /// Lit geometry draw calls.
+    BatchQueue litBatches_;
     /// Shadow map split queues.
     /// Shadow map split queues.
     Vector<ShadowBatchQueue> shadowSplits_;
     Vector<ShadowBatchQueue> shadowSplits_;
     /// Per-vertex lights.
     /// Per-vertex lights.

+ 6 - 0
Engine/Graphics/Renderer.cpp

@@ -274,6 +274,7 @@ Renderer::Renderer(Context* context) :
     maxShadowMaps_(1),
     maxShadowMaps_(1),
     maxShadowCascades_(MAX_CASCADE_SPLITS),
     maxShadowCascades_(MAX_CASCADE_SPLITS),
     maxInstanceTriangles_(500),
     maxInstanceTriangles_(500),
+    maxSortedInstances_(1000),
     maxOccluderTriangles_(5000),
     maxOccluderTriangles_(5000),
     occlusionBufferSize_(256),
     occlusionBufferSize_(256),
     occluderSizeThreshold_(0.025f),
     occluderSizeThreshold_(0.025f),
@@ -478,6 +479,11 @@ void Renderer::SetMaxInstanceTriangles(int triangles)
     maxInstanceTriangles_ = Max(triangles, 0);
     maxInstanceTriangles_ = Max(triangles, 0);
 }
 }
 
 
+void Renderer::SetMaxSortedInstances(int instances)
+{
+    maxSortedInstances_ = Max(instances, 0);
+}
+
 void Renderer::SetMaxOccluderTriangles(int triangles)
 void Renderer::SetMaxOccluderTriangles(int triangles)
 {
 {
     maxOccluderTriangles_ = Max(triangles, 0);
     maxOccluderTriangles_ = Max(triangles, 0);

+ 7 - 1
Engine/Graphics/Renderer.h

@@ -197,6 +197,8 @@ public:
     void SetDynamicInstancing(bool enable);
     void SetDynamicInstancing(bool enable);
     /// %Set maximum number of triangles per object for instancing.
     /// %Set maximum number of triangles per object for instancing.
     void SetMaxInstanceTriangles(int triangles);
     void SetMaxInstanceTriangles(int triangles);
+    /// %Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
+    void SetMaxSortedInstances(int instances);
     /// %Set maximum number of occluder trianges.
     /// %Set maximum number of occluder trianges.
     void SetMaxOccluderTriangles(int triangles);
     void SetMaxOccluderTriangles(int triangles);
     /// %Set occluder buffer width.
     /// %Set occluder buffer width.
@@ -234,7 +236,9 @@ public:
     /// Return whether dynamic instancing is in use.
     /// Return whether dynamic instancing is in use.
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
     /// Return maximum number of triangles per object for instancing.
     /// Return maximum number of triangles per object for instancing.
-    int GetMaxInstanceTriangles() { return maxInstanceTriangles_; }
+    int GetMaxInstanceTriangles() const { return maxInstanceTriangles_; }
+    /// Return maximum number of sorted instances per batch group.
+    int GetMaxSortedInstances() const { return maxSortedInstances_; }
     /// Return maximum number of occluder triangles.
     /// Return maximum number of occluder triangles.
     int GetMaxOccluderTriangles() const { return maxOccluderTriangles_; }
     int GetMaxOccluderTriangles() const { return maxOccluderTriangles_; }
     /// Return occlusion buffer width.
     /// Return occlusion buffer width.
@@ -447,6 +451,8 @@ private:
     int maxShadowCascades_;
     int maxShadowCascades_;
     /// Maximum triangles per object for instancing.
     /// Maximum triangles per object for instancing.
     int maxInstanceTriangles_;
     int maxInstanceTriangles_;
+    /// Maximum sorted instances per batch group.
+    int maxSortedInstances_;
     /// Maximum occluder triangles.
     /// Maximum occluder triangles.
     int maxOccluderTriangles_;
     int maxOccluderTriangles_;
     /// Occlusion buffer width.
     /// Occlusion buffer width.

+ 13 - 11
Engine/Graphics/View.cpp

@@ -395,6 +395,8 @@ void View::Update(const FrameInfo& frame)
     frame_.frameNumber_ = frame.frameNumber_;
     frame_.frameNumber_ = frame.frameNumber_;
     frame_.viewSize_ = viewSize_;
     frame_.viewSize_ = viewSize_;
     
     
+    int maxSortedInstances = renderer_->GetMaxSortedInstances();
+    
     // Clear screen buffers, geometry, light, occluder & batch lists
     // Clear screen buffers, geometry, light, occluder & batch lists
     screenBuffers_.Clear();
     screenBuffers_.Clear();
     geometries_.Clear();
     geometries_.Clear();
@@ -402,12 +404,11 @@ void View::Update(const FrameInfo& frame)
     lights_.Clear();
     lights_.Clear();
     zones_.Clear();
     zones_.Clear();
     occluders_.Clear();
     occluders_.Clear();
-    baseQueue_.Clear();
-    preAlphaQueue_.Clear();
-    gbufferQueue_.Clear();
-    alphaQueue_.Clear();
-    postAlphaQueue_.Clear();
-    lightQueues_.Clear();
+    baseQueue_.Clear(maxSortedInstances);
+    preAlphaQueue_.Clear(maxSortedInstances);
+    gbufferQueue_.Clear(maxSortedInstances);
+    alphaQueue_.Clear(maxSortedInstances);
+    postAlphaQueue_.Clear(maxSortedInstances);
     vertexLightQueues_.Clear();
     vertexLightQueues_.Clear();
     
     
     // Do not update if camera projection is illegal
     // Do not update if camera projection is illegal
@@ -700,8 +701,6 @@ void View::GetBatches()
     {
     {
         PROFILE(GetLightBatches);
         PROFILE(GetLightBatches);
         
         
-        maxLightsDrawables_.Clear();
-        
         // Preallocate light queues: per-pixel lights which have lit geometries
         // Preallocate light queues: per-pixel lights which have lit geometries
         unsigned numLightQueues = 0;
         unsigned numLightQueues = 0;
         unsigned usedLightQueues = 0;
         unsigned usedLightQueues = 0;
@@ -712,6 +711,8 @@ void View::GetBatches()
         }
         }
         
         
         lightQueues_.Resize(numLightQueues);
         lightQueues_.Resize(numLightQueues);
+        maxLightsDrawables_.Clear();
+        unsigned maxSortedInstances = renderer_->GetMaxSortedInstances();
         
         
         for (Vector<LightQueryResult>::Iterator i = lightQueryResults_.Begin(); i != lightQueryResults_.End(); ++i)
         for (Vector<LightQueryResult>::Iterator i = lightQueryResults_.Begin(); i != lightQueryResults_.End(); ++i)
         {
         {
@@ -728,15 +729,15 @@ void View::GetBatches()
             {
             {
                 unsigned shadowSplits = query.numSplits_;
                 unsigned shadowSplits = query.numSplits_;
                 
                 
-                // Initialize light queue. Store light-to-queue mapping so that the queue can be found later
+                // Initialize light queue and store it to the light so that it can be found later
                 LightBatchQueue& lightQueue = lightQueues_[usedLightQueues++];
                 LightBatchQueue& lightQueue = lightQueues_[usedLightQueues++];
                 light->SetLightQueue(&lightQueue);
                 light->SetLightQueue(&lightQueue);
                 lightQueue.light_ = light;
                 lightQueue.light_ = light;
-                lightQueue.litBatches_.Clear();
+                lightQueue.shadowMap_ = 0;
+                lightQueue.litBatches_.Clear(maxSortedInstances);
                 lightQueue.volumeBatches_.Clear();
                 lightQueue.volumeBatches_.Clear();
                 
                 
                 // Allocate shadow map now
                 // Allocate shadow map now
-                lightQueue.shadowMap_ = 0;
                 if (shadowSplits > 0)
                 if (shadowSplits > 0)
                 {
                 {
                     lightQueue.shadowMap_ = renderer_->GetShadowMap(light, camera_, viewSize_.x_, viewSize_.y_);
                     lightQueue.shadowMap_ = renderer_->GetShadowMap(light, camera_, viewSize_.x_, viewSize_.y_);
@@ -754,6 +755,7 @@ void View::GetBatches()
                     shadowQueue.shadowCamera_ = shadowCamera;
                     shadowQueue.shadowCamera_ = shadowCamera;
                     shadowQueue.nearSplit_ = query.shadowNearSplits_[j];
                     shadowQueue.nearSplit_ = query.shadowNearSplits_[j];
                     shadowQueue.farSplit_ = query.shadowFarSplits_[j];
                     shadowQueue.farSplit_ = query.shadowFarSplits_[j];
+                    shadowQueue.shadowBatches_.Clear(maxSortedInstances);
                     
                     
                     // Setup the shadow split viewport and finalize shadow camera parameters
                     // Setup the shadow split viewport and finalize shadow camera parameters
                     shadowQueue.shadowViewport_ = GetShadowMapViewport(light, j, lightQueue.shadowMap_);
                     shadowQueue.shadowViewport_ = GetShadowMapViewport(light, j, lightQueue.shadowMap_);

+ 7 - 7
Engine/Network/Connection.cpp

@@ -1049,7 +1049,7 @@ void Connection::ProcessNewNode(Node* node)
     node->AddReplicationState(&nodeState);
     node->AddReplicationState(&nodeState);
     
     
     // Write node's attributes
     // Write node's attributes
-    node->WriteInitialDeltaUpdate(msg_, node->GetNetworkState());
+    node->WriteInitialDeltaUpdate(msg_);
     
     
     // Write node's user variables
     // Write node's user variables
     const VariantMap& vars = node->GetVars();
     const VariantMap& vars = node->GetVars();
@@ -1078,7 +1078,7 @@ void Connection::ProcessNewNode(Node* node)
         
         
         msg_.WriteShortStringHash(component->GetType());
         msg_.WriteShortStringHash(component->GetType());
         msg_.WriteNetID(component->GetID());
         msg_.WriteNetID(component->GetID());
-        component->WriteInitialDeltaUpdate(msg_, component->GetNetworkState());
+        component->WriteInitialDeltaUpdate(msg_);
     }
     }
     
     
     SendMessage(MSG_CREATENODE, true, true, msg_);
     SendMessage(MSG_CREATENODE, true, true, msg_);
@@ -1129,7 +1129,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
         {
         {
             msg_.Clear();
             msg_.Clear();
             msg_.WriteNetID(node->GetID());
             msg_.WriteNetID(node->GetID());
-            node->WriteLatestDataUpdate(msg_, node->GetNetworkState());
+            node->WriteLatestDataUpdate(msg_);
             
             
             SendMessage(MSG_NODELATESTDATA, true, false, msg_, node->GetID());
             SendMessage(MSG_NODELATESTDATA, true, false, msg_, node->GetID());
         }
         }
@@ -1139,7 +1139,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
         {
         {
             msg_.Clear();
             msg_.Clear();
             msg_.WriteNetID(node->GetID());
             msg_.WriteNetID(node->GetID());
-            node->WriteDeltaUpdate(msg_, node->GetNetworkState(), nodeState.dirtyAttributes_);
+            node->WriteDeltaUpdate(msg_, nodeState.dirtyAttributes_);
             
             
             // Write changed variables
             // Write changed variables
             msg_.WriteVLE(nodeState.dirtyVars_.Size());
             msg_.WriteVLE(nodeState.dirtyVars_.Size());
@@ -1207,7 +1207,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                 {
                 {
                     msg_.Clear();
                     msg_.Clear();
                     msg_.WriteNetID(component->GetID());
                     msg_.WriteNetID(component->GetID());
-                    component->WriteLatestDataUpdate(msg_, component->GetNetworkState());
+                    component->WriteLatestDataUpdate(msg_);
                     
                     
                     SendMessage(MSG_COMPONENTLATESTDATA, true, false, msg_, component->GetID());
                     SendMessage(MSG_COMPONENTLATESTDATA, true, false, msg_, component->GetID());
                 }
                 }
@@ -1217,7 +1217,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                 {
                 {
                     msg_.Clear();
                     msg_.Clear();
                     msg_.WriteNetID(component->GetID());
                     msg_.WriteNetID(component->GetID());
-                    component->WriteDeltaUpdate(msg_, component->GetNetworkState(), componentState.dirtyAttributes_);
+                    component->WriteDeltaUpdate(msg_, componentState.dirtyAttributes_);
                     
                     
                     SendMessage(MSG_COMPONENTDELTAUPDATE, true, true, msg_);
                     SendMessage(MSG_COMPONENTDELTAUPDATE, true, true, msg_);
                     
                     
@@ -1252,7 +1252,7 @@ void Connection::ProcessExistingNode(Node* node, NodeReplicationState& nodeState
                 msg_.WriteNetID(node->GetID());
                 msg_.WriteNetID(node->GetID());
                 msg_.WriteShortStringHash(component->GetType());
                 msg_.WriteShortStringHash(component->GetType());
                 msg_.WriteNetID(component->GetID());
                 msg_.WriteNetID(component->GetID());
-                component->WriteInitialDeltaUpdate(msg_, component->GetNetworkState());
+                component->WriteInitialDeltaUpdate(msg_);
                 
                 
                 SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
                 SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
             }
             }

+ 2 - 11
Engine/Scene/Component.cpp

@@ -36,15 +36,12 @@ Component::Component(Context* context) :
     Serializable(context),
     Serializable(context),
     node_(0),
     node_(0),
     id_(0),
     id_(0),
-    networkState_(0),
     networkUpdate_(false)
     networkUpdate_(false)
 {
 {
 }
 }
 
 
 Component::~Component()
 Component::~Component()
 {
 {
-    delete networkState_;
-    networkState_ = 0;
 }
 }
 
 
 void Component::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 void Component::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
@@ -91,10 +88,7 @@ Scene* Component::GetScene() const
 void Component::AddReplicationState(ComponentReplicationState* state)
 void Component::AddReplicationState(ComponentReplicationState* state)
 {
 {
     if (!networkState_)
     if (!networkState_)
-    {
-        networkState_ = new NetworkState();
-        networkState_->attributes_ = GetNetworkAttributes();
-    }
+        AllocateNetworkState();
     
     
     networkState_->replicationStates_.Push(state);
     networkState_->replicationStates_.Push(state);
 }
 }
@@ -102,10 +96,7 @@ void Component::AddReplicationState(ComponentReplicationState* state)
 void Component::PrepareNetworkUpdate()
 void Component::PrepareNetworkUpdate()
 {
 {
     if (!networkState_)
     if (!networkState_)
-    {
-        networkState_ = new NetworkState();
-        networkState_->attributes_ = GetNetworkAttributes();
-    }
+        AllocateNetworkState();
     
     
     const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     if (!attributes)
     if (!attributes)

+ 0 - 4
Engine/Scene/Component.h

@@ -80,8 +80,6 @@ public:
     void CleanupConnection(Connection* connection);
     void CleanupConnection(Connection* connection);
     /// Mark for attribute check on the next network update.
     /// Mark for attribute check on the next network update.
     void MarkNetworkUpdate();
     void MarkNetworkUpdate();
-    /// Return the network attribute state.
-    NetworkState* GetNetworkState() const { return networkState_; }
     
     
 protected:
 protected:
     /// Handle scene node being assigned at creation.
     /// Handle scene node being assigned at creation.
@@ -97,8 +95,6 @@ protected:
     Node* node_;
     Node* node_;
     /// Unique ID within the scene.
     /// Unique ID within the scene.
     unsigned id_;
     unsigned id_;
-    /// Network attribute state.
-    NetworkState* networkState_;
     /// Network update queued flag.
     /// Network update queued flag.
     bool networkUpdate_;
     bool networkUpdate_;
 };
 };

+ 3 - 13
Engine/Scene/Node.cpp

@@ -51,8 +51,7 @@ Node::Node(Context* context) :
     position_(Vector3::ZERO),
     position_(Vector3::ZERO),
     rotation_(Quaternion::IDENTITY),
     rotation_(Quaternion::IDENTITY),
     scale_(Vector3::ONE),
     scale_(Vector3::ONE),
-    owner_(0),
-    networkState_(0)
+    owner_(0)
 {
 {
 }
 }
 
 
@@ -64,9 +63,6 @@ Node::~Node()
     // Remove from the scene
     // Remove from the scene
     if (scene_)
     if (scene_)
         scene_->NodeRemoved(this);
         scene_->NodeRemoved(this);
-    
-    delete networkState_;
-    networkState_ = 0;
 }
 }
 
 
 void Node::RegisterObject(Context* context)
 void Node::RegisterObject(Context* context)
@@ -225,10 +221,7 @@ void Node::ApplyAttributes()
 void Node::AddReplicationState(NodeReplicationState* state)
 void Node::AddReplicationState(NodeReplicationState* state)
 {
 {
     if (!networkState_)
     if (!networkState_)
-    {
-        networkState_ = new NetworkState();
-        networkState_->attributes_ = GetNetworkAttributes();
-    }
+        AllocateNetworkState();
     
     
     networkState_->replicationStates_.Push(state);
     networkState_->replicationStates_.Push(state);
 }
 }
@@ -865,10 +858,7 @@ void Node::PrepareNetworkUpdate()
     
     
     // Then check for node attribute changes
     // Then check for node attribute changes
     if (!networkState_)
     if (!networkState_)
-    {
-        networkState_ = new NetworkState();
-        networkState_->attributes_ = GetNetworkAttributes();
-    }
+        AllocateNetworkState();
     
     
     const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     unsigned numAttributes = attributes->Size();
     unsigned numAttributes = attributes->Size();

+ 0 - 4
Engine/Scene/Node.h

@@ -317,8 +317,6 @@ public:
     void MarkNetworkUpdate();
     void MarkNetworkUpdate();
     /// Mark node dirty in scene replication states.
     /// Mark node dirty in scene replication states.
     void MarkReplicationDirty();
     void MarkReplicationDirty();
-    /// Return the network attribute state.
-    NetworkState* GetNetworkState() const { return networkState_; }
     
     
 protected:
 protected:
     /// Create a component with specific ID.
     /// Create a component with specific ID.
@@ -376,8 +374,6 @@ private:
     mutable VectorBuffer attrBuffer_;
     mutable VectorBuffer attrBuffer_;
     
     
 protected:
 protected:
-    /// Network attribute state.
-    NetworkState* networkState_;
     /// User variables.
     /// User variables.
     VariantMap vars_;
     VariantMap vars_;
 };
 };

+ 29 - 16
Engine/Scene/Serializable.cpp

@@ -35,12 +35,15 @@
 OBJECTTYPESTATIC(Serializable);
 OBJECTTYPESTATIC(Serializable);
 
 
 Serializable::Serializable(Context* context) :
 Serializable::Serializable(Context* context) :
-    Object(context)
+    Object(context),
+    networkState_(0)
 {
 {
 }
 }
 
 
 Serializable::~Serializable()
 Serializable::~Serializable()
 {
 {
+    delete networkState_;
+    networkState_ = 0;
 }
 }
 
 
 void Serializable::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 void Serializable::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
@@ -412,15 +415,24 @@ bool Serializable::SetAttribute(const String& name, const Variant& value)
     return false;
     return false;
 }
 }
 
 
-void Serializable::WriteInitialDeltaUpdate(Serializer& dest, NetworkState* state)
+void Serializable::AllocateNetworkState()
 {
 {
-    if (!state)
+    if (!networkState_)
+    {
+        networkState_ = new NetworkState();
+        networkState_->attributes_ = context_->GetNetworkAttributes(GetType());
+    }
+}
+
+void Serializable::WriteInitialDeltaUpdate(Serializer& dest)
+{
+    if (!networkState_)
     {
     {
         LOGERROR("WriteInitialDeltaUpdate called without allocated NetworkState");
         LOGERROR("WriteInitialDeltaUpdate called without allocated NetworkState");
         return;
         return;
     }
     }
     
     
-    const Vector<AttributeInfo>* attributes = state->attributes_;
+    const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     if (!attributes)
     if (!attributes)
         return;
         return;
     
     
@@ -431,7 +443,7 @@ void Serializable::WriteInitialDeltaUpdate(Serializer& dest, NetworkState* state
     for (unsigned i = 0; i < numAttributes; ++i)
     for (unsigned i = 0; i < numAttributes; ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (state->currentValues_[i] != attr.defaultValue_)
+        if (networkState_->currentValues_[i] != attr.defaultValue_)
             attributeBits.Set(i);
             attributeBits.Set(i);
     }
     }
     
     
@@ -441,19 +453,19 @@ void Serializable::WriteInitialDeltaUpdate(Serializer& dest, NetworkState* state
     for (unsigned i = 0; i < numAttributes; ++i)
     for (unsigned i = 0; i < numAttributes; ++i)
     {
     {
         if (attributeBits.IsSet(i))
         if (attributeBits.IsSet(i))
-            dest.WriteVariantData(state->currentValues_[i]);
+            dest.WriteVariantData(networkState_->currentValues_[i]);
     }
     }
 }
 }
 
 
-void Serializable::WriteDeltaUpdate(Serializer& dest, NetworkState* state, const DirtyBits& attributeBits)
+void Serializable::WriteDeltaUpdate(Serializer& dest, const DirtyBits& attributeBits)
 {
 {
-    if (!state)
+    if (!networkState_)
     {
     {
         LOGERROR("WriteDeltaUpdate called without allocated NetworkState");
         LOGERROR("WriteDeltaUpdate called without allocated NetworkState");
         return;
         return;
     }
     }
     
     
-    const Vector<AttributeInfo>* attributes = state->attributes_;
+    const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     if (!attributes)
     if (!attributes)
         return;
         return;
     
     
@@ -466,19 +478,19 @@ void Serializable::WriteDeltaUpdate(Serializer& dest, NetworkState* state, const
     for (unsigned i = 0; i < numAttributes; ++i)
     for (unsigned i = 0; i < numAttributes; ++i)
     {
     {
         if (attributeBits.IsSet(i))
         if (attributeBits.IsSet(i))
-            dest.WriteVariantData(state->currentValues_[i]);
+            dest.WriteVariantData(networkState_->currentValues_[i]);
     }
     }
 }
 }
 
 
-void Serializable::WriteLatestDataUpdate(Serializer& dest, NetworkState* state)
+void Serializable::WriteLatestDataUpdate(Serializer& dest)
 {
 {
-    if (!state)
+    if (!networkState_)
     {
     {
         LOGERROR("WriteLatestDataUpdate called without allocated NetworkState");
         LOGERROR("WriteLatestDataUpdate called without allocated NetworkState");
         return;
         return;
     }
     }
     
     
-    const Vector<AttributeInfo>* attributes = state->attributes_;
+    const Vector<AttributeInfo>* attributes = networkState_->attributes_;
     if (!attributes)
     if (!attributes)
         return;
         return;
     
     
@@ -487,7 +499,7 @@ void Serializable::WriteLatestDataUpdate(Serializer& dest, NetworkState* state)
     for (unsigned i = 0; i < numAttributes; ++i)
     for (unsigned i = 0; i < numAttributes; ++i)
     {
     {
         if (attributes->At(i).mode_ & AM_LATESTDATA)
         if (attributes->At(i).mode_ & AM_LATESTDATA)
-            dest.WriteVariantData(state->currentValues_[i]);
+            dest.WriteVariantData(networkState_->currentValues_[i]);
     }
     }
 }
 }
 
 
@@ -572,7 +584,8 @@ unsigned Serializable::GetNumAttributes() const
 
 
 unsigned Serializable::GetNumNetworkAttributes() const
 unsigned Serializable::GetNumNetworkAttributes() const
 {
 {
-    const Vector<AttributeInfo>* attributes = context_->GetNetworkAttributes(GetType());
+    const Vector<AttributeInfo>* attributes = networkState_ ? networkState_->attributes_ :
+        context_->GetNetworkAttributes(GetType());
     return attributes ? attributes->Size() : 0;
     return attributes ? attributes->Size() : 0;
 }
 }
 
 
@@ -583,5 +596,5 @@ const Vector<AttributeInfo>* Serializable::GetAttributes() const
 
 
 const Vector<AttributeInfo>* Serializable::GetNetworkAttributes() const
 const Vector<AttributeInfo>* Serializable::GetNetworkAttributes() const
 {
 {
-    return context_->GetNetworkAttributes(GetType());
+    return networkState_ ? networkState_->attributes_ : context_->GetNetworkAttributes(GetType());
 }
 }

+ 9 - 3
Engine/Scene/Serializable.h

@@ -67,12 +67,14 @@ public:
     bool SetAttribute(unsigned index, const Variant& value);
     bool SetAttribute(unsigned index, const Variant& value);
     /// %Set attribute by name. Return true if successfully set.
     /// %Set attribute by name. Return true if successfully set.
     bool SetAttribute(const String& name, const Variant& value);
     bool SetAttribute(const String& name, const Variant& value);
+    /// Allocate network attribute state.
+    void AllocateNetworkState();
     /// Write initial delta network update.
     /// Write initial delta network update.
-    void WriteInitialDeltaUpdate(Serializer& dest, NetworkState* state);
+    void WriteInitialDeltaUpdate(Serializer& dest);
     /// Write a delta network update according to dirty attribute bits.
     /// Write a delta network update according to dirty attribute bits.
-    void WriteDeltaUpdate(Serializer& dest, NetworkState* state, const DirtyBits& attributeBits);
+    void WriteDeltaUpdate(Serializer& dest, const DirtyBits& attributeBits);
     /// Write a latest data network update.
     /// Write a latest data network update.
-    void WriteLatestDataUpdate(Serializer& dest, NetworkState* state);
+    void WriteLatestDataUpdate(Serializer& dest);
     /// Read and apply a network delta update.
     /// Read and apply a network delta update.
     void ReadDeltaUpdate(Deserializer& source);
     void ReadDeltaUpdate(Deserializer& source);
     /// Read and apply a network latest data update.
     /// Read and apply a network latest data update.
@@ -90,6 +92,10 @@ public:
     const Vector<AttributeInfo>* GetAttributes() const;
     const Vector<AttributeInfo>* GetAttributes() const;
     /// Return network replication attribute descriptions, or null if none defined.
     /// Return network replication attribute descriptions, or null if none defined.
     const Vector<AttributeInfo>* GetNetworkAttributes() const;
     const Vector<AttributeInfo>* GetNetworkAttributes() const;
+    
+protected:
+    /// Network attribute state.
+    NetworkState* networkState_;
 };
 };
 
 
 /// Template implementation of the attribute accessor invoke helper class.
 /// Template implementation of the attribute accessor invoke helper class.