Browse Source

Removed most OnGetAttribute() / OnSetAttribute() functions in favor of accessor attributes.
Added compare to attribute default values when sending initial node or component attributes.
Network updates for parent nodes are now sent before their child nodes.
Renamed PostLoad() to OnFinishUpdate().
Fixed kNet null pointer assert in debug mode.
Re-enabled memory leak checking in debug mode.

Lasse Öörni 14 years ago
parent
commit
7b15b34c83
44 changed files with 1024 additions and 996 deletions
  1. 33 47
      Engine/Audio/SoundSource.cpp
  2. 9 5
      Engine/Audio/SoundSource.h
  3. 3 3
      Engine/Audio/SoundSource3D.cpp
  4. 8 6
      Engine/Core/Attribute.h
  5. 4 4
      Engine/Core/Variant.cpp
  6. 2 2
      Engine/Engine/PhysicsAPI.cpp
  7. 5 4
      Engine/Engine/SceneAPI.cpp
  8. 89 113
      Engine/Graphics/AnimatedModel.cpp
  9. 18 7
      Engine/Graphics/AnimatedModel.h
  10. 30 51
      Engine/Graphics/AnimationController.cpp
  11. 7 7
      Engine/Graphics/AnimationController.h
  12. 50 81
      Engine/Graphics/BillboardSet.cpp
  13. 9 4
      Engine/Graphics/BillboardSet.h
  14. 12 12
      Engine/Graphics/Camera.cpp
  15. 9 9
      Engine/Graphics/Drawable.cpp
  16. 46 67
      Engine/Graphics/Light.cpp
  17. 9 4
      Engine/Graphics/Light.h
  18. 5 14
      Engine/Graphics/Octree.cpp
  19. 73 91
      Engine/Graphics/ParticleEmitter.cpp
  20. 12 8
      Engine/Graphics/ParticleEmitter.h
  21. 26 45
      Engine/Graphics/StaticModel.cpp
  22. 9 6
      Engine/Graphics/StaticModel.h
  23. 10 13
      Engine/Graphics/Zone.cpp
  24. 115 91
      Engine/Network/Connection.cpp
  25. 9 2
      Engine/Network/Connection.h
  26. 100 1
      Engine/Network/Network.cpp
  27. 2 0
      Engine/Network/Network.h
  28. 6 0
      Engine/Network/NetworkEvents.h
  29. 10 8
      Engine/Network/Protocol.h
  30. 23 40
      Engine/Physics/CollisionShape.cpp
  31. 7 6
      Engine/Physics/CollisionShape.h
  32. 36 51
      Engine/Physics/Joint.cpp
  33. 15 8
      Engine/Physics/Joint.h
  34. 15 15
      Engine/Physics/PhysicsWorld.cpp
  35. 13 13
      Engine/Physics/RigidBody.cpp
  36. 62 35
      Engine/Scene/Node.cpp
  37. 7 4
      Engine/Scene/Node.h
  38. 7 15
      Engine/Scene/Scene.cpp
  39. 1 3
      Engine/Scene/Scene.h
  40. 6 7
      Engine/Scene/Serializable.cpp
  41. 41 12
      Engine/Scene/Serializable.h
  42. 56 76
      Engine/Script/ScriptInstance.cpp
  43. 14 5
      Engine/Script/ScriptInstance.h
  44. 1 1
      Urho3D/Urho3D.cpp

+ 33 - 47
Engine/Audio/SoundSource.cpp

@@ -134,53 +134,14 @@ void SoundSource::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<SoundSource>();
     context->RegisterFactory<SoundSource>();
     
     
-    ENUM_ATTRIBUTE(SoundSource, "Sound Type", soundType_, typeNames, SOUND_EFFECT);
-    ATTRIBUTE(SoundSource, VAR_FLOAT, "Frequency", frequency_, 0.0f);
-    ATTRIBUTE(SoundSource, VAR_FLOAT, "Gain", gain_, 1.0f);
-    ATTRIBUTE(SoundSource, VAR_FLOAT, "Attenuation", attenuation_, 1.0f);
-    ATTRIBUTE(SoundSource, VAR_FLOAT, "Panning", panning_, 0.0f);
-    ATTRIBUTE(SoundSource, VAR_BOOL, "Autoremove on Stop", autoRemove_, false);
-    ATTRIBUTE(SoundSource, VAR_RESOURCEREF, "Sound", sound_, ResourceRef(Sound::GetTypeStatic()));
-    ATTRIBUTE(SoundSource, VAR_INT, "Play Position", position_, 0);
-}
-
-void SoundSource::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(SoundSource, sound_):
-        Play(cache->GetResource<Sound>(value.GetResourceRef().id_));
-        break;
-        
-    case offsetof(SoundSource, position_):
-        if (sound_)
-            SetPlayPosition(sound_->GetStart() + value.GetInt());
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant SoundSource::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(SoundSource, sound_):
-        return GetResourceRef(sound_, Sound::GetTypeStatic());
-        
-    case offsetof(SoundSource, position_):
-        if (sound_)
-            return (int)(GetPlayPosition() - sound_->GetStart());
-        else
-            return 0;
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
+    ENUM_ATTRIBUTE(SoundSource, "Sound Type", soundType_, typeNames, SOUND_EFFECT, AM_DEFAULT);
+    ATTRIBUTE(SoundSource, VAR_FLOAT, "Frequency", frequency_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(SoundSource, VAR_FLOAT, "Gain", gain_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(SoundSource, VAR_FLOAT, "Attenuation", attenuation_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(SoundSource, VAR_FLOAT, "Panning", panning_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(SoundSource, VAR_BOOL, "Autoremove on Stop", autoRemove_, false, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(SoundSource, VAR_RESOURCEREF, "Sound", GetSoundAttr, SetSoundAttr, ResourceRef, ResourceRef(Sound::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(SoundSource, VAR_INT, "Play Position", GetPositionAttr, SetPositionAttr, int, 0, AM_DEFAULT);
 }
 }
 
 
 void SoundSource::Play(Sound* sound)
 void SoundSource::Play(Sound* sound)
@@ -522,6 +483,31 @@ void SoundSource::Mix(int* dest, unsigned samples, int mixRate, bool stereo, boo
         timePosition_ += ((float)samples / (float)mixRate) * frequency_ / sound_->GetFrequency();
         timePosition_ += ((float)samples / (float)mixRate) * frequency_ / sound_->GetFrequency();
 }
 }
 
 
+void SoundSource::SetSoundAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    Play(cache->GetResource<Sound>(value.id_));
+}
+
+void SoundSource::SetPositionAttr(int value)
+{
+    if (sound_)
+        SetPlayPosition(sound_->GetStart() + value);
+}
+
+ResourceRef SoundSource::GetSoundAttr() const
+{
+    return GetResourceRef(sound_, Sound::GetTypeStatic());
+}
+
+int SoundSource::GetPositionAttr() const
+{
+    if (sound_)
+        return (int)(GetPlayPosition() - sound_->GetStart());
+    else
+        return 0;
+}
+
 void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
 void SoundSource::MixMonoToMono(Sound* sound, int* dest, unsigned samples, int mixRate)
 {
 {
     float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;
     float totalGain = audio_->GetSoundSourceMasterGain(soundType_) * attenuation_ * gain_;

+ 9 - 5
Engine/Audio/SoundSource.h

@@ -45,11 +45,6 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
-    
     /// Play a sound
     /// Play a sound
     void Play(Sound* sound);
     void Play(Sound* sound);
     /// Play a sound with specified frequency
     /// Play a sound with specified frequency
@@ -107,6 +102,15 @@ public:
     /// Mix sound source output to a 32-bit clipping buffer. Called by Sound
     /// Mix sound source output to a 32-bit clipping buffer. Called by Sound
     void Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolate);
     void Mix(int* dest, unsigned samples, int mixRate, bool stereo, bool interpolate);
     
     
+    /// Set sound attribute
+    void SetSoundAttr(ResourceRef value);
+    /// Set sound position attribute
+    void SetPositionAttr(int value);
+    /// Return sound attribute
+    ResourceRef GetSoundAttr() const;
+    /// Return sound position attribute
+    int GetPositionAttr() const;
+    
 protected:
 protected:
     /// Audio subsystem
     /// Audio subsystem
     WeakPtr<Audio> audio_;
     WeakPtr<Audio> audio_;

+ 3 - 3
Engine/Audio/SoundSource3D.cpp

@@ -52,9 +52,9 @@ void SoundSource3D::RegisterObject(Context* context)
     context->RegisterFactory<SoundSource3D>();
     context->RegisterFactory<SoundSource3D>();
     context->CopyBaseAttributes<SoundSource, SoundSource3D>();
     context->CopyBaseAttributes<SoundSource, SoundSource3D>();
     
     
-    ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Near Distance", nearDistance_, DEFAULT_NEARDISTANCE);
-    ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Far Distance", farDistance_, DEFAULT_FARDISTANCE);
-    ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Rolloff Factor", rolloffFactor_, DEFAULT_ROLLOFF);
+    ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Near Distance", nearDistance_, DEFAULT_NEARDISTANCE, AM_DEFAULT);
+    ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Far Distance", farDistance_, DEFAULT_FARDISTANCE, AM_DEFAULT);
+    ATTRIBUTE(SoundSource3D, VAR_FLOAT, "Rolloff Factor", rolloffFactor_, DEFAULT_ROLLOFF, AM_DEFAULT);
 }
 }
 
 
 void SoundSource3D::Update(float timeStep)
 void SoundSource3D::Update(float timeStep)

+ 8 - 6
Engine/Core/Attribute.h

@@ -26,14 +26,16 @@
 #include "Ptr.h"
 #include "Ptr.h"
 #include "Variant.h"
 #include "Variant.h"
 
 
-/// Attribute used only for disk serialization
-static const unsigned AM_SERIALIZATION = 0x1;
+/// Attribute used for file serialization
+static const unsigned AM_FILE = 0x1;
 /// Attribute used for network replication
 /// Attribute used for network replication
-static const unsigned AM_NETWORK = 0x2;
-/// Attribute used for both (default)
-static const unsigned AM_BOTH = 0x3;
+static const unsigned AM_NET = 0x2;
+/// Attribute used for both file serialization and network replication (default)
+static const unsigned AM_DEFAULT = 0x3;
 /// Attribute should use latest data grouping instead of delta update in network replication
 /// Attribute should use latest data grouping instead of delta update in network replication
 static const unsigned AM_LATESTDATA = 0x4;
 static const unsigned AM_LATESTDATA = 0x4;
+/// Attribute should not be shown in the editor
+static const unsigned AM_NOEDIT = 0x8;
 
 
 class Serializable;
 class Serializable;
 
 
@@ -55,7 +57,7 @@ struct AttributeInfo
         type_(VAR_NONE),
         type_(VAR_NONE),
         offset_(0),
         offset_(0),
         enumNames_(0),
         enumNames_(0),
-        mode_(AM_BOTH)
+        mode_(AM_DEFAULT)
     {
     {
     }
     }
     
     

+ 4 - 4
Engine/Core/Variant.cpp

@@ -436,22 +436,22 @@ template<> void* Variant::Get<void*>() const
     return GetPtr();
     return GetPtr();
 }
 }
 
 
-template<> const ResourceRef& Variant::Get<const ResourceRef&>() const
+template<> ResourceRef Variant::Get<ResourceRef>() const
 {
 {
     return GetResourceRef();
     return GetResourceRef();
 }
 }
 
 
-template<> const ResourceRefList& Variant::Get<const ResourceRefList&>() const
+template<> ResourceRefList Variant::Get<ResourceRefList>() const
 {
 {
     return GetResourceRefList();
     return GetResourceRefList();
 }
 }
 
 
-template<> const VariantVector& Variant::Get<const VariantVector&>() const
+template<> VariantVector Variant::Get<VariantVector>() const
 {
 {
     return GetVariantVector();
     return GetVariantVector();
 }
 }
 
 
-template<> const VariantMap& Variant::Get<const VariantMap&>() const
+template<> VariantMap Variant::Get<VariantMap>() const
 {
 {
     return GetVariantMap();
     return GetVariantMap();
 }
 }

+ 2 - 2
Engine/Engine/PhysicsAPI.cpp

@@ -197,9 +197,9 @@ static void RegisterJoint(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Joint", "void Clear()", asMETHOD(Joint, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "void Clear()", asMETHOD(Joint, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "bool SetBall(const Vector3&in, RigidBody@+, RigidBody@+)", asMETHOD(Joint, SetBall), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "bool SetBall(const Vector3&in, RigidBody@+, RigidBody@+)", asMETHOD(Joint, SetBall), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "bool SetHinge(const Vector3&in, const Vector3&in, RigidBody@+, RigidBody@+)", asMETHOD(Joint, SetHinge), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "bool SetHinge(const Vector3&in, const Vector3&in, RigidBody@+, RigidBody@+)", asMETHOD(Joint, SetHinge), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "void set_position(const Vector3&in)", asMETHOD(Joint, SetPosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Joint", "void set_position(Vector3)", asMETHOD(Joint, SetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "Vector3 get_position() const", asMETHOD(Joint, GetPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "Vector3 get_position() const", asMETHOD(Joint, GetPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Joint", "void set_axis(const Vector3&in)", asMETHOD(Joint, SetAxis), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Joint", "void set_axis(Vector3)", asMETHOD(Joint, SetAxis), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "Vector3 get_axis() const", asMETHOD(Joint, GetAxis), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "Vector3 get_axis() const", asMETHOD(Joint, GetAxis), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "RigidBody@+ get_bodyA() const", asMETHOD(Joint, GetBodyA), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "RigidBody@+ get_bodyA() const", asMETHOD(Joint, GetBodyA), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "RigidBody@+ get_bodyB() const", asMETHOD(Joint, GetBodyB), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "RigidBody@+ get_bodyB() const", asMETHOD(Joint, GetBodyB), asCALL_THISCALL);

+ 5 - 4
Engine/Engine/SceneAPI.cpp

@@ -28,9 +28,11 @@
 
 
 static void RegisterSerializable(asIScriptEngine* engine)
 static void RegisterSerializable(asIScriptEngine* engine)
 {
 {
-    engine->RegisterGlobalProperty("const uint AM_SERIALIZATION", (void*)AM_SERIALIZATION);
-    engine->RegisterGlobalProperty("const uint AM_NETWORK", (void*)AM_NETWORK);
-    engine->RegisterGlobalProperty("const uint AM_BOTH", (void*)AM_BOTH);
+    engine->RegisterGlobalProperty("const uint AM_FILE", (void*)AM_FILE);
+    engine->RegisterGlobalProperty("const uint AM_NET", (void*)AM_NET);
+    engine->RegisterGlobalProperty("const uint AM_DEFAULT", (void*)AM_DEFAULT);
+    engine->RegisterGlobalProperty("const uint AM_LATESTDATA", (void*)AM_LATESTDATA);
+    engine->RegisterGlobalProperty("const uint AM_NOEDIT", (void*)AM_NOEDIT);
     
     
     RegisterSerializable<Serializable>(engine, "Serializable");
     RegisterSerializable<Serializable>(engine, "Serializable");
 }
 }
@@ -90,7 +92,6 @@ static void RegisterScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "bool LoadAsyncXML(File@+)", asMETHOD(Scene, LoadAsyncXML), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool LoadAsyncXML(File@+)", asMETHOD(Scene, LoadAsyncXML), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void StopAsyncLoading()", asMETHOD(Scene, StopAsyncLoading), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void StopAsyncLoading()", asMETHOD(Scene, StopAsyncLoading), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void Clear()", asMETHOD(Scene, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void Clear()", asMETHOD(Scene, Clear), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Scene", "void ClearNonLocal()", asMETHOD(Scene, ClearNonLocal), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void AddRequiredPackageFile(PackageFile@+)", asMETHOD(Scene, AddRequiredPackageFile), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void AddRequiredPackageFile(PackageFile@+)", asMETHOD(Scene, AddRequiredPackageFile), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void ClearRequiredPackageFiles()", asMETHOD(Scene, ClearRequiredPackageFiles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void ClearRequiredPackageFiles()", asMETHOD(Scene, ClearRequiredPackageFiles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "Component@+ GetComponentByID(uint)", asMETHOD(Scene, GetComponentByID), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "Component@+ GetComponentByID(uint)", asMETHOD(Scene, GetComponentByID), asCALL_THISCALL);

+ 89 - 113
Engine/Graphics/AnimatedModel.cpp

@@ -67,7 +67,8 @@ AnimatedModel::AnimatedModel(Context* context) :
     animationOrderDirty_(true),
     animationOrderDirty_(true),
     morphsDirty_(true),
     morphsDirty_(true),
     skinningDirty_(true),
     skinningDirty_(true),
-    isMaster_(true)
+    isMaster_(true),
+    assignBonesPending_(false)
 {
 {
 }
 }
 
 
@@ -80,121 +81,18 @@ void AnimatedModel::RegisterObject(Context* context)
     context->RegisterFactory<AnimatedModel>();
     context->RegisterFactory<AnimatedModel>();
     context->CopyBaseAttributes<Drawable, AnimatedModel>();
     context->CopyBaseAttributes<Drawable, AnimatedModel>();
     
     
-    ATTRIBUTE(AnimatedModel, VAR_RESOURCEREF, "Model", model_, ResourceRef(Model::GetTypeStatic()));
-    ATTRIBUTE(AnimatedModel, VAR_RESOURCEREFLIST, "Materials", materials_, ResourceRefList(Material::GetTypeStatic()));
-    ATTRIBUTE(AnimatedModel, VAR_FLOAT, "Animation LOD Bias", animationLodBias_, 1.0f);
-    ATTRIBUTE(AnimatedModel, VAR_INT, "Raycast/Occlusion LOD Level", softwareLodLevel_, M_MAX_UNSIGNED);
-    ATTRIBUTE(AnimatedModel, VAR_BUFFER, "Bone Animation Enabled", skeleton_, PODVector<unsigned char>());
-    ATTRIBUTE(AnimatedModel, VAR_BUFFER, "Animation States", animationStates_, PODVector<unsigned char>());
+    ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_RESOURCEREF, "Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_RESOURCEREFLIST, "Materials", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()), AM_DEFAULT);
+    ATTRIBUTE(AnimatedModel, VAR_FLOAT, "Animation LOD Bias", animationLodBias_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(AnimatedModel, VAR_INT, "Raycast/Occlusion LOD Level", softwareLodLevel_, M_MAX_UNSIGNED, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_BUFFER, "Bone Animation Enabled", GetBonesEnabledAttr, SetBonesEnabledAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_FILE);
+    ACCESSOR_ATTRIBUTE(AnimatedModel, VAR_BUFFER, "Animation States", GetAnimationStatesAttr, SetAnimationStatesAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_FILE);
 }
 }
 
 
-void AnimatedModel::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
+void AnimatedModel::OnFinishUpdate()
 {
 {
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(AnimatedModel, model_):
-        // When loading a scene, set model without creating the bone nodes (will be assigned later during post-load)
-        SetModel(cache->GetResource<Model>(value.GetResourceRef().id_), !inSerialization_);
-        break;
-        
-    case offsetof(AnimatedModel, materials_):
-        {
-            const ResourceRefList& refs = value.GetResourceRefList();
-            for (unsigned i = 0; i < refs.ids_.Size(); ++i)
-                SetMaterial(i, cache->GetResource<Material>(refs.ids_[i]));
-        }
-        break;
-        
-    case offsetof(AnimatedModel, skeleton_):
-        {
-            MemoryBuffer buf(value.GetBuffer());
-            Vector<Bone>& bones = skeleton_.GetModifiableBones();
-            unsigned numBones = buf.ReadVLE();
-            for (unsigned i = 0; i < numBones && i < bones.Size(); ++i)
-                bones[i].animated_ = buf.ReadBool();
-        }
-        break;
-        
-    case offsetof(AnimatedModel, animationStates_):
-        {
-            // The animation states will at first be created without bone node references as well
-            /// \todo This format is unsuitable for network serialization
-            RemoveAllAnimationStates();
-            MemoryBuffer buf(value.GetBuffer());
-            unsigned numAnimations = buf.ReadVLE();
-            for (unsigned i = 0; i < numAnimations; ++i)
-            {
-                AnimationState* state = AddAnimationState(cache->GetResource<Animation>(buf.ReadStringHash()));
-                if (state)
-                {
-                    state->SetStartBone(skeleton_.GetBone(buf.ReadStringHash()));
-                    state->SetLooped(buf.ReadBool());
-                    state->SetWeight(buf.ReadFloat());
-                    state->SetTime(buf.ReadFloat());
-                    state->SetLayer(buf.ReadInt());
-                    state->SetUseNlerp(buf.ReadBool());
-                }
-                else
-                    buf.Seek(sizeof(StringHash) + 1 + sizeof(float) + sizeof(float) + sizeof(int) + 1);
-            }
-        }
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant AnimatedModel::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(AnimatedModel, model_):
-        return GetResourceRef(model_, Model::GetTypeStatic());
-        
-    case offsetof(AnimatedModel, materials_):
-        return GetResourceRefList(materials_);
-        
-    case offsetof(AnimatedModel, skeleton_):
-        {
-            VectorBuffer buf;
-            const Vector<Bone>& bones = skeleton_.GetBones();
-            buf.WriteVLE(bones.Size());
-            for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
-                buf.WriteBool(i->animated_);
-            return buf.GetBuffer();
-        }
-        
-    case offsetof(AnimatedModel, animationStates_):
-        {
-            VectorBuffer buf;
-            buf.WriteVLE(animationStates_.Size());
-            for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
-            {
-                AnimationState* state = *i;
-                Bone* startBone = state->GetStartBone();
-                buf.WriteStringHash(state->GetAnimation()->GetNameHash());
-                buf.WriteStringHash(startBone ? startBone->nameHash_ : StringHash());
-                buf.WriteBool(state->IsLooped());
-                buf.WriteFloat(state->GetWeight());
-                buf.WriteFloat(state->GetTime());
-                buf.WriteInt(state->GetLayer());
-                buf.WriteBool(state->GetUseNlerp());
-            }
-            return buf.GetBuffer();
-        }
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
-}
-
-void AnimatedModel::PostLoad()
-{
-    AssignBoneNodes();
+    if (assignBonesPending_)
+        AssignBoneNodes();
 }
 }
 
 
 void AnimatedModel::ProcessRayQuery(RayOctreeQuery& query, float initialDistance)
 void AnimatedModel::ProcessRayQuery(RayOctreeQuery& query, float initialDistance)
@@ -696,6 +594,82 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
     // Reserve space for skinning matrices
     // Reserve space for skinning matrices
     skinMatrices_.Resize(skeleton_.GetNumBones());
     skinMatrices_.Resize(skeleton_.GetNumBones());
     RefreshGeometryBoneMappings();
     RefreshGeometryBoneMappings();
+    
+    assignBonesPending_ = !createBones;
+}
+
+void AnimatedModel::SetModelAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    // When loading a scene, set model without creating the bone nodes (will be assigned later during post-load)
+    SetModel(cache->GetResource<Model>(value.id_), !inSerialization_);
+}
+
+void AnimatedModel::SetBonesEnabledAttr(PODVector<unsigned char> value)
+{
+    MemoryBuffer buf(value);
+    Vector<Bone>& bones = skeleton_.GetModifiableBones();
+    unsigned numBones = buf.ReadVLE();
+    for (unsigned i = 0; i < numBones && i < bones.Size(); ++i)
+        bones[i].animated_ = buf.ReadBool();
+}
+
+void AnimatedModel::SetAnimationStatesAttr(PODVector<unsigned char> value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    // The animation states will at first be created without bone node references
+    RemoveAllAnimationStates();
+    MemoryBuffer buf(value);
+    unsigned numAnimations = buf.ReadVLE();
+    for (unsigned i = 0; i < numAnimations; ++i)
+    {
+        AnimationState* state = AddAnimationState(cache->GetResource<Animation>(buf.ReadStringHash()));
+        if (state)
+        {
+            state->SetStartBone(skeleton_.GetBone(buf.ReadStringHash()));
+            state->SetLooped(buf.ReadBool());
+            state->SetWeight(buf.ReadFloat());
+            state->SetTime(buf.ReadFloat());
+            state->SetLayer(buf.ReadInt());
+            state->SetUseNlerp(buf.ReadBool());
+        }
+        else
+            buf.Seek(sizeof(StringHash) + 1 + sizeof(float) + sizeof(float) + sizeof(int) + 1);
+    }
+}
+
+ResourceRef AnimatedModel::GetModelAttr() const
+{
+    return GetResourceRef(model_, Model::GetTypeStatic());
+}
+
+PODVector<unsigned char> AnimatedModel::GetBonesEnabledAttr() const
+{
+    VectorBuffer buf;
+    const Vector<Bone>& bones = skeleton_.GetBones();
+    buf.WriteVLE(bones.Size());
+    for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
+        buf.WriteBool(i->animated_);
+    return buf.GetBuffer();
+}
+
+PODVector<unsigned char> AnimatedModel::GetAnimationStatesAttr() const
+{
+    VectorBuffer buf;
+    buf.WriteVLE(animationStates_.Size());
+    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
+    {
+        AnimationState* state = *i;
+        Bone* startBone = state->GetStartBone();
+        buf.WriteStringHash(state->GetAnimation()->GetNameHash());
+        buf.WriteStringHash(startBone ? startBone->nameHash_ : StringHash());
+        buf.WriteBool(state->IsLooped());
+        buf.WriteFloat(state->GetWeight());
+        buf.WriteFloat(state->GetTime());
+        buf.WriteInt(state->GetLayer());
+        buf.WriteBool(state->GetUseNlerp());
+    }
+    return buf.GetBuffer();
 }
 }
 
 
 void AnimatedModel::OnNodeSet(Node* node)
 void AnimatedModel::OnNodeSet(Node* node)
@@ -745,6 +719,8 @@ void AnimatedModel::OnWorldBoundingBoxUpdate()
 
 
 void AnimatedModel::AssignBoneNodes()
 void AnimatedModel::AssignBoneNodes()
 {
 {
+    assignBonesPending_ = false;
+    
     if (!node_)
     if (!node_)
         return;
         return;
     
     

+ 18 - 7
Engine/Graphics/AnimatedModel.h

@@ -46,12 +46,8 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
-    /// Perform post-load after the whole scene has been loaded
-    virtual void PostLoad();
+    /// Perform finalization after a scene load or network update
+    virtual void OnFinishUpdate();
     /// Process renderer raycast
     /// Process renderer raycast
     virtual void ProcessRayQuery(RayOctreeQuery& query, float initialDistance);
     virtual void ProcessRayQuery(RayOctreeQuery& query, float initialDistance);
     /// Update before octree reinsertion. Animation is updated here
     /// Update before octree reinsertion. Animation is updated here
@@ -125,6 +121,19 @@ public:
     /// Return whether is the master (first) animated model
     /// Return whether is the master (first) animated model
     bool IsMaster() const { return isMaster_; }
     bool IsMaster() const { return isMaster_; }
     
     
+    /// Set model attribute
+    void SetModelAttr(ResourceRef value);
+    /// Set bones' animation enabled attribute
+    void SetBonesEnabledAttr(PODVector<unsigned char> value);
+    /// Set animation states attribute
+    void SetAnimationStatesAttr(PODVector<unsigned char> value);
+    /// Return model attribute
+    ResourceRef GetModelAttr() const;
+    /// Return bones' animation enabled attribute
+    PODVector<unsigned char> GetBonesEnabledAttr() const;
+    /// Return animation states attribute
+    PODVector<unsigned char> GetAnimationStatesAttr() const;
+    
 protected:
 protected:
     /// Handle node being assigned
     /// Handle node being assigned
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);
@@ -134,7 +143,7 @@ protected:
     virtual void OnWorldBoundingBoxUpdate();
     virtual void OnWorldBoundingBoxUpdate();
     
     
 private:
 private:
-    /// Assign skeleton and animation bone node references as a postprocess. Called by PostLoad
+    /// Assign skeleton and animation bone node references as a postprocess. Called by OnFinishUpdate
     void AssignBoneNodes();
     void AssignBoneNodes();
     /// Mark animation and skinning to require an update
     /// Mark animation and skinning to require an update
     void MarkAnimationDirty();
     void MarkAnimationDirty();
@@ -195,4 +204,6 @@ private:
     bool skinningDirty_;
     bool skinningDirty_;
     /// Master model flag
     /// Master model flag
     bool isMaster_;
     bool isMaster_;
+    /// Bone nodes assignment pending flag
+    bool assignBonesPending_;
 };
 };

+ 30 - 51
Engine/Graphics/AnimationController.cpp

@@ -53,57 +53,7 @@ void AnimationController::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<AnimationController>();
     context->RegisterFactory<AnimationController>();
     
     
-    ATTRIBUTE(AnimationController, VAR_BUFFER, "Animations", animations_, PODVector<unsigned char>());
-}
-
-
-void AnimationController::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(AnimationController, animations_):
-        {
-            MemoryBuffer buf(value.GetBuffer());
-            animations_.Resize(buf.ReadVLE());
-            for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
-            {
-                i->hash_ = buf.ReadStringHash();
-                i->speed_ = buf.ReadFloat();
-                i->targetWeight_ = buf.ReadFloat();
-                i->fadeTime_ = buf.ReadFloat();
-                i->autoFadeTime_ = buf.ReadFloat();
-            }
-        }
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant AnimationController::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(AnimationController, animations_):
-        {
-            VectorBuffer buf;
-            buf.WriteVLE(animations_.Size());
-            for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
-            {
-                buf.WriteStringHash(i->hash_);
-                buf.WriteFloat(i->speed_);
-                buf.WriteFloat(i->targetWeight_);
-                buf.WriteFloat(i->fadeTime_);
-                buf.WriteFloat(i->autoFadeTime_);
-            }
-            return buf.GetBuffer();
-        }
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
+    ACCESSOR_ATTRIBUTE(AnimationController, VAR_BUFFER, "Animations", GetAnimationsAttr, SetAnimationsAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_DEFAULT);
 }
 }
 
 
 void AnimationController::Update(float timeStep)
 void AnimationController::Update(float timeStep)
@@ -542,6 +492,35 @@ float AnimationController::GetAutoFade(const String& name) const
     return animations_[index].autoFadeTime_;
     return animations_[index].autoFadeTime_;
 }
 }
 
 
+void AnimationController::SetAnimationsAttr(PODVector<unsigned char> value)
+{
+    MemoryBuffer buf(value);
+    animations_.Resize(buf.ReadVLE());
+    for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i)
+    {
+        i->hash_ = buf.ReadStringHash();
+        i->speed_ = buf.ReadFloat();
+        i->targetWeight_ = buf.ReadFloat();
+        i->fadeTime_ = buf.ReadFloat();
+        i->autoFadeTime_ = buf.ReadFloat();
+    }
+}
+
+PODVector<unsigned char> AnimationController::GetAnimationsAttr() const
+{
+    VectorBuffer buf;
+    buf.WriteVLE(animations_.Size());
+    for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
+    {
+        buf.WriteStringHash(i->hash_);
+        buf.WriteFloat(i->speed_);
+        buf.WriteFloat(i->targetWeight_);
+        buf.WriteFloat(i->fadeTime_);
+        buf.WriteFloat(i->autoFadeTime_);
+    }
+    return buf.GetBuffer();
+}
+
 void AnimationController::OnNodeSet(Node* node)
 void AnimationController::OnNodeSet(Node* node)
 {
 {
     if (node)
     if (node)

+ 7 - 7
Engine/Graphics/AnimationController.h

@@ -67,11 +67,6 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
-    
     /// Update the animations. Is called from HandleScenePostUpdate()
     /// Update the animations. Is called from HandleScenePostUpdate()
     void Update(float timeStep);
     void Update(float timeStep);
     /// Play an animation and set full target weight. Name must be the full resource name. Return true on success
     /// Play an animation and set full target weight. Name must be the full resource name. Return true on success
@@ -132,11 +127,16 @@ public:
     float GetFadeTime(const String& name) const;
     float GetFadeTime(const String& name) const;
     /// Return animation autofade time
     /// Return animation autofade time
     float GetAutoFade(const String& name) const;
     float GetAutoFade(const String& name) const;
-
+    
+    /// Set animations attribute
+    void SetAnimationsAttr(PODVector<unsigned char> value);
+    /// Return animations attribute
+    PODVector<unsigned char> GetAnimationsAttr() const;
+    
 protected:
 protected:
     /// Handle node being assigned
     /// Handle node being assigned
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);
-
+    
 private:
 private:
     /// Find the internal index and animation state of an animation
     /// Find the internal index and animation state of an animation
     void FindAnimation(const String& name, unsigned& index, AnimationState*& state) const;
     void FindAnimation(const String& name, unsigned& index, AnimationState*& state) const;

+ 50 - 81
Engine/Graphics/BillboardSet.cpp

@@ -76,87 +76,12 @@ void BillboardSet::RegisterObject(Context* context)
     context->RegisterFactory<BillboardSet>();
     context->RegisterFactory<BillboardSet>();
     context->CopyBaseAttributes<Drawable, BillboardSet>();
     context->CopyBaseAttributes<Drawable, BillboardSet>();
     
     
-    ATTRIBUTE(BillboardSet, VAR_FLOAT, "Animation LOD Bias", animationLodBias_, 1.0f);
-    ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Position", relative_, true);
-    ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Scale", scaled_, true);
-    ATTRIBUTE(BillboardSet, VAR_BOOL, "Sort By Distance", sorted_, false);
-    ATTRIBUTE(BillboardSet, VAR_RESOURCEREF, "Material", material_, ResourceRef(Material::GetTypeStatic()));
-    ATTRIBUTE(BillboardSet, VAR_BUFFER, "Billboards", billboards_, PODVector<unsigned char>());
-}
-
-void BillboardSet::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(BillboardSet, relative_):
-        SetRelative(value.GetBool());
-        break;
-    
-    case offsetof(BillboardSet, scaled_):
-        SetScaled(value.GetBool());
-        break;
-    
-    case offsetof(BillboardSet, sorted_):
-        SetSorted(value.GetBool());
-        break;
-    
-    case offsetof(BillboardSet, material_):
-        SetMaterial(cache->GetResource<Material>(value.GetResourceRef().id_));
-        break;
-        
-    case offsetof(BillboardSet, billboards_):
-        {
-            MemoryBuffer buf(value.GetBuffer());
-            unsigned numBillboards = buf.ReadVLE();
-            SetNumBillboards(numBillboards);
-            for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-            {
-                i->position_ = buf.ReadVector3();
-                i->size_ = buf.ReadVector2();
-                i->uv_ = buf.ReadRect();
-                i->color_ = buf.ReadColor();
-                i->rotation_ = buf.ReadFloat();
-                i->enabled_ = buf.ReadBool();
-            }
-            Updated();
-        }
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant BillboardSet::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(BillboardSet, material_):
-        return GetResourceRef(material_, Material::GetTypeStatic());
-        
-    case offsetof(BillboardSet, billboards_):
-        {
-            VectorBuffer buf;
-            buf.WriteVLE(billboards_.Size());
-            for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-            {
-                buf.WriteVector3(i->position_);
-                buf.WriteVector2(i->size_);
-                buf.WriteRect(i->uv_);
-                buf.WriteColor(i->color_);
-                buf.WriteFloat(i->rotation_);
-                buf.WriteBool(i->enabled_);
-            }
-            return buf.GetBuffer();
-        }
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
-    
+    ATTRIBUTE(BillboardSet, VAR_FLOAT, "Animation LOD Bias", animationLodBias_, 1.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_RESOURCEREF, "Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BUFFER, "Billboards", GetBillboardsAttr, SetBillboardsAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_DEFAULT);
 }
 }
 
 
 void BillboardSet::UpdateDistance(const FrameInfo& frame)
 void BillboardSet::UpdateDistance(const FrameInfo& frame)
@@ -279,6 +204,50 @@ Billboard* BillboardSet::GetBillboard(unsigned index)
     return index < billboards_.Size() ? &billboards_[index] : (Billboard*)0;
     return index < billboards_.Size() ? &billboards_[index] : (Billboard*)0;
 }
 }
 
 
+void BillboardSet::SetMaterialAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetMaterial(cache->GetResource<Material>(value.id_));
+}
+
+void BillboardSet::SetBillboardsAttr(PODVector<unsigned char> value)
+{
+    MemoryBuffer buf(value);
+    unsigned numBillboards = buf.ReadVLE();
+    SetNumBillboards(numBillboards);
+    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    {
+        i->position_ = buf.ReadVector3();
+        i->size_ = buf.ReadVector2();
+        i->uv_ = buf.ReadRect();
+        i->color_ = buf.ReadColor();
+        i->rotation_ = buf.ReadFloat();
+        i->enabled_ = buf.ReadBool();
+    }
+    Updated();
+}
+
+ResourceRef BillboardSet::GetMaterialAttr() const
+{
+    return GetResourceRef(material_, Material::GetTypeStatic());
+}
+
+PODVector<unsigned char> BillboardSet::GetBillboardsAttr() const
+{
+    VectorBuffer buf;
+    buf.WriteVLE(billboards_.Size());
+    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    {
+        buf.WriteVector3(i->position_);
+        buf.WriteVector2(i->size_);
+        buf.WriteRect(i->uv_);
+        buf.WriteColor(i->color_);
+        buf.WriteFloat(i->rotation_);
+        buf.WriteBool(i->enabled_);
+    }
+    return buf.GetBuffer();
+}
+
 void BillboardSet::OnMarkedDirty(Node* node)
 void BillboardSet::OnMarkedDirty(Node* node)
 {
 {
     if (node == node_)
     if (node == node_)

+ 9 - 4
Engine/Graphics/BillboardSet.h

@@ -63,10 +63,6 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
     /// Calculate distance for rendering
     /// Calculate distance for rendering
     virtual void UpdateDistance(const FrameInfo& frame);
     virtual void UpdateDistance(const FrameInfo& frame);
     /// Prepare geometry for rendering
     /// Prepare geometry for rendering
@@ -108,6 +104,15 @@ public:
     /// Return animation LOD bias
     /// Return animation LOD bias
     float GetAnimationLodBias() const { return animationLodBias_; }
     float GetAnimationLodBias() const { return animationLodBias_; }
     
     
+    /// Set material attribute
+    void SetMaterialAttr(ResourceRef value);
+    /// Set billboards attribute
+    void SetBillboardsAttr(PODVector<unsigned char> value);
+    /// Return material attribute
+    ResourceRef GetMaterialAttr() const;
+    /// Return billboards attribute
+    PODVector<unsigned char> GetBillboardsAttr() const;
+    
 protected:
 protected:
     /// Transform has changed. Mark billboards dirty if necessary
     /// Transform has changed. Mark billboards dirty if necessary
     virtual void OnMarkedDirty(Node* node);
     virtual void OnMarkedDirty(Node* node);

+ 12 - 12
Engine/Graphics/Camera.cpp

@@ -60,18 +60,18 @@ void Camera::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<Camera>();
     context->RegisterFactory<Camera>();
     
     
-    ATTRIBUTE(Camera, VAR_FLOAT, "Near Clip", nearClip_, DEFAULT_NEARCLIP);
-    ATTRIBUTE(Camera, VAR_FLOAT, "Far Clip", farClip_, DEFAULT_FARCLIP);
-    ATTRIBUTE(Camera, VAR_FLOAT, "FOV", fov_, DEFAULT_FOV);
-    ATTRIBUTE(Camera, VAR_FLOAT, "Aspect Ratio", aspectRatio_, 1.0f);
-    ATTRIBUTE(Camera, VAR_BOOL, "Auto Aspect Ratio", autoAspectRatio_, true);
-    ATTRIBUTE(Camera, VAR_BOOL, "Orthographic", orthographic_, false);
-    ATTRIBUTE(Camera, VAR_FLOAT, "Orthographic Size", orthoSize_, DEFAULT_ORTHOSIZE);
-    ATTRIBUTE(Camera, VAR_FLOAT, "Zoom", zoom_, 1.0f);
-    ATTRIBUTE(Camera, VAR_FLOAT, "LOD Bias", lodBias_, 1.0f);
-    ATTRIBUTE(Camera, VAR_INT, "View Mask", viewMask_, DEFAULT_VIEWMASK);
-    ATTRIBUTE(Camera, VAR_INT, "View Override Flags", viewOverrideFlags_, VOF_NONE);
-    ATTRIBUTE(Camera, VAR_VECTOR2, "Projection Offset", projectionOffset_, Vector2::ZERO);
+    ATTRIBUTE(Camera, VAR_FLOAT, "Near Clip", nearClip_, DEFAULT_NEARCLIP, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_FLOAT, "Far Clip", farClip_, DEFAULT_FARCLIP, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_FLOAT, "FOV", fov_, DEFAULT_FOV, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_FLOAT, "Aspect Ratio", aspectRatio_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_BOOL, "Auto Aspect Ratio", autoAspectRatio_, true, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_BOOL, "Orthographic", orthographic_, false, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_FLOAT, "Orthographic Size", orthoSize_, DEFAULT_ORTHOSIZE, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_FLOAT, "Zoom", zoom_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_FLOAT, "LOD Bias", lodBias_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_INT, "View Mask", viewMask_, DEFAULT_VIEWMASK, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_INT, "View Override Flags", viewOverrideFlags_, VOF_NONE, AM_DEFAULT);
+    ATTRIBUTE(Camera, VAR_VECTOR2, "Projection Offset", projectionOffset_, Vector2::ZERO, AM_DEFAULT);
 }
 }
 
 
 void Camera::SetNearClip(float nearClip)
 void Camera::SetNearClip(float nearClip)

+ 9 - 9
Engine/Graphics/Drawable.cpp

@@ -69,15 +69,15 @@ Drawable::~Drawable()
 
 
 void Drawable::RegisterObject(Context* context)
 void Drawable::RegisterObject(Context* context)
 {
 {
-    ATTRIBUTE(Drawable, VAR_BOOL, "Is Visible", visible_, true);
-    ATTRIBUTE(Drawable, VAR_BOOL, "Is Occluder", occluder_, false);
-    ATTRIBUTE(Drawable, VAR_BOOL, "Cast Shadows", castShadows_, false);
-    ATTRIBUTE(Drawable, VAR_FLOAT, "Draw Distance", drawDistance_, 0.0f);
-    ATTRIBUTE(Drawable, VAR_FLOAT, "Shadow Distance", shadowDistance_, 0.0f);
-    ATTRIBUTE(Drawable, VAR_FLOAT, "LOD Bias", lodBias_, 1.0f);
-    ATTRIBUTE(Drawable, VAR_INT, "View Mask", viewMask_, DEFAULT_VIEWMASK);
-    ATTRIBUTE(Drawable, VAR_INT, "Light Mask", lightMask_, DEFAULT_LIGHTMASK);
-    ATTRIBUTE(Drawable, VAR_INT, "Max Lights", maxLights_, 0);
+    ATTRIBUTE(Drawable, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_BOOL, "Is Occluder", occluder_, false, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_FLOAT, "Draw Distance", drawDistance_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_FLOAT, "Shadow Distance", shadowDistance_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_FLOAT, "LOD Bias", lodBias_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_INT, "View Mask", viewMask_, DEFAULT_VIEWMASK, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_INT, "Light Mask", lightMask_, DEFAULT_LIGHTMASK, AM_DEFAULT);
+    ATTRIBUTE(Drawable, VAR_INT, "Max Lights", maxLights_, 0, AM_DEFAULT);
 }
 }
 
 
 void Drawable::ProcessRayQuery(RayOctreeQuery& query, float initialDistance)
 void Drawable::ProcessRayQuery(RayOctreeQuery& query, float initialDistance)

+ 46 - 67
Engine/Graphics/Light.cpp

@@ -112,73 +112,30 @@ void Light::RegisterObject(Context* context)
     context->RemoveAttribute<Light>("LOD Bias");
     context->RemoveAttribute<Light>("LOD Bias");
     context->RemoveAttribute<Light>("Max Lights");
     context->RemoveAttribute<Light>("Max Lights");
     
     
-    ENUM_ATTRIBUTE(Light, "Light Type", lightType_, typeNames, DEFAULT_LIGHTTYPE);
-    ATTRIBUTE(Light, VAR_COLOR, "Color", color_, Color());
-    ATTRIBUTE(Light, VAR_FLOAT, "Specular Intensity", specularIntensity_, 0.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Range", range_, 0.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Spotlight FOV", fov_, DEFAULT_FOV);
-    ATTRIBUTE(Light, VAR_FLOAT, "Spotlight Aspect Ratio", aspectRatio_, 1.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Fade Distance", fadeDistance_, 0.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Fade Distance", shadowFadeDistance_, 0.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Intensity", shadowIntensity_, 0.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Map Resolution", shadowResolution_, 1.0f);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Camera Near/Far Ratio", shadowNearFarRatio_, DEFAULT_SHADOWNEARFARRATIO);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Constant Bias", shadowBias_.constantBias_, DEFAULT_CONSTANTBIAS);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Slope-scaled Bias", shadowBias_.slopeScaledBias_, DEFAULT_SLOPESCALEDBIAS);
-    ATTRIBUTE(Light, VAR_INT, "Shadow Splits", shadowCascade_.splits_, 1);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Split Lambda", shadowCascade_.lambda_, DEFAULT_LAMBDA);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Split Fade Range", shadowCascade_.splitFadeRange_, DEFAULT_SHADOWFADERANGE);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Split Max Range", shadowCascade_.shadowRange_, M_LARGE_VALUE);
-    ATTRIBUTE(Light, VAR_BOOL, "Shadow Focus", shadowFocus_.focus_, true);
-    ATTRIBUTE(Light, VAR_BOOL, "Shadow Focus Allow Non-uniform", shadowFocus_.nonUniform_, true);
-    ATTRIBUTE(Light, VAR_BOOL, "Shadow Focus Allow Zoom-out", shadowFocus_.zoomOut_, true);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Focus Quantization", shadowFocus_.quantize_, DEFAULT_SHADOWQUANTIZE);
-    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Focus Min. View", shadowFocus_.minView_, DEFAULT_SHADOWMINVIEW);
-    ATTRIBUTE(Light, VAR_RESOURCEREF, "Attenuation Ramp Texture", rampTexture_, ResourceRef(Texture2D::GetTypeStatic()));
-    ATTRIBUTE(Light, VAR_RESOURCEREF, "Light Shape Texture", shapeTexture_, ResourceRef(Texture2D::GetTypeStatic()));
-}
-
-void Light::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(Light, rampTexture_):
-        {
-            ResourceRef ref = value.GetResourceRef();
-            rampTexture_ = static_cast<Texture*>(cache->GetResource(ref.type_, ref.id_));
-        }
-        break;
-        
-    case offsetof(Light, shapeTexture_):
-        {
-            ResourceRef ref = value.GetResourceRef();
-            shapeTexture_ = static_cast<Texture*>(cache->GetResource(ref.type_, ref.id_));
-        }
-        break;
-    
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant Light::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(Light, rampTexture_):
-        return GetResourceRef(rampTexture_, Texture2D::GetTypeStatic());
-        break;
-        
-    case offsetof(Light, shapeTexture_):
-        return GetResourceRef(shapeTexture_, Texture2D::GetTypeStatic());
-        break;
-    
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
+    ENUM_ATTRIBUTE(Light, "Light Type", lightType_, typeNames, DEFAULT_LIGHTTYPE, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_COLOR, "Color", color_, Color(), AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Specular Intensity", specularIntensity_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Range", range_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Spotlight FOV", fov_, DEFAULT_FOV, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Spotlight Aspect Ratio", aspectRatio_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Fade Distance", fadeDistance_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Fade Distance", shadowFadeDistance_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Intensity", shadowIntensity_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Map Resolution", shadowResolution_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Camera Near/Far Ratio", shadowNearFarRatio_, DEFAULT_SHADOWNEARFARRATIO, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Constant Bias", shadowBias_.constantBias_, DEFAULT_CONSTANTBIAS, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Slope-scaled Bias", shadowBias_.slopeScaledBias_, DEFAULT_SLOPESCALEDBIAS, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_INT, "Shadow Splits", shadowCascade_.splits_, 1, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Split Lambda", shadowCascade_.lambda_, DEFAULT_LAMBDA, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Split Fade Range", shadowCascade_.splitFadeRange_, DEFAULT_SHADOWFADERANGE, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Split Max Range", shadowCascade_.shadowRange_, M_LARGE_VALUE, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_BOOL, "Shadow Focus", shadowFocus_.focus_, true, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_BOOL, "Shadow Focus Allow Non-uniform", shadowFocus_.nonUniform_, true, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_BOOL, "Shadow Focus Allow Zoom-out", shadowFocus_.zoomOut_, true, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Focus Quantization", shadowFocus_.quantize_, DEFAULT_SHADOWQUANTIZE, AM_DEFAULT);
+    ATTRIBUTE(Light, VAR_FLOAT, "Shadow Focus Min. View", shadowFocus_.minView_, DEFAULT_SHADOWMINVIEW, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Light, VAR_RESOURCEREF, "Attenuation Ramp Texture", GetRampTextureAttr, SetRampTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Light, VAR_RESOURCEREF, "Light Shape Texture", GetShapeTextureAttr, SetShapeTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
 }
 }
 
 
 void Light::UpdateDistance(const FrameInfo& frame)
 void Light::UpdateDistance(const FrameInfo& frame)
@@ -412,6 +369,28 @@ const Matrix3x4& Light::GetVolumeTransform(Camera& camera)
     return volumeTransform_;
     return volumeTransform_;
 }
 }
 
 
+void Light::SetRampTextureAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    rampTexture_ = static_cast<Texture*>(cache->GetResource(value.type_, value.id_));
+}
+
+void Light::SetShapeTextureAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    shapeTexture_ = static_cast<Texture*>(cache->GetResource(value.type_, value.id_));
+}
+
+ResourceRef Light::GetRampTextureAttr() const
+{
+    return GetResourceRef(rampTexture_, Texture2D::GetTypeStatic());
+}
+
+ResourceRef Light::GetShapeTextureAttr() const
+{
+    return GetResourceRef(shapeTexture_, Texture2D::GetTypeStatic());
+}
+
 void Light::OnWorldBoundingBoxUpdate()
 void Light::OnWorldBoundingBoxUpdate()
 {
 {
     switch (lightType_)
     switch (lightType_)

+ 9 - 4
Engine/Graphics/Light.h

@@ -145,10 +145,6 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
     /// Calculate distance for rendering
     /// Calculate distance for rendering
     virtual void UpdateDistance(const FrameInfo& frame);
     virtual void UpdateDistance(const FrameInfo& frame);
     /// Add debug geometry to the debug graphics
     /// Add debug geometry to the debug graphics
@@ -259,6 +255,15 @@ public:
     /// Return light volume model transform. For directional lights, the view transform must be overridden
     /// Return light volume model transform. For directional lights, the view transform must be overridden
     const Matrix3x4& GetVolumeTransform(Camera& camera);
     const Matrix3x4& GetVolumeTransform(Camera& camera);
     
     
+    /// Set ramp texture attribute
+    void SetRampTextureAttr(ResourceRef value);
+    /// Set shape texture attribute
+    void SetShapeTextureAttr(ResourceRef value);
+    /// Return ramp texture attribute
+    ResourceRef GetRampTextureAttr() const;
+    /// Return shape texture attribute
+    ResourceRef GetShapeTextureAttr() const;
+    
 protected:
 protected:
     /// Update world-space bounding box
     /// Update world-space bounding box
     virtual void OnWorldBoundingBoxUpdate();
     virtual void OnWorldBoundingBoxUpdate();

+ 5 - 14
Engine/Graphics/Octree.cpp

@@ -281,25 +281,16 @@ void Octree::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<Octree>();
     context->RegisterFactory<Octree>();
     
     
-    ATTRIBUTE(Octree, VAR_VECTOR3, "Bounding Box Min", worldBoundingBox_.min_, Vector3(-DEFAULT_OCTREE_SIZE, -DEFAULT_OCTREE_SIZE, -DEFAULT_OCTREE_SIZE));
-    ATTRIBUTE(Octree, VAR_VECTOR3, "Bounding Box Max", worldBoundingBox_.max_, Vector3(DEFAULT_OCTREE_SIZE, DEFAULT_OCTREE_SIZE, DEFAULT_OCTREE_SIZE));
-    ATTRIBUTE(Octree, VAR_INT, "Number of Levels", numLevels_, DEFAULT_OCTREE_LEVELS);
+    ATTRIBUTE(Octree, VAR_VECTOR3, "Bounding Box Min", worldBoundingBox_.min_, Vector3(-DEFAULT_OCTREE_SIZE, -DEFAULT_OCTREE_SIZE, -DEFAULT_OCTREE_SIZE), AM_DEFAULT);
+    ATTRIBUTE(Octree, VAR_VECTOR3, "Bounding Box Max", worldBoundingBox_.max_, Vector3(DEFAULT_OCTREE_SIZE, DEFAULT_OCTREE_SIZE, DEFAULT_OCTREE_SIZE), AM_DEFAULT);
+    ATTRIBUTE(Octree, VAR_INT, "Number of Levels", numLevels_, DEFAULT_OCTREE_LEVELS, AM_DEFAULT);
 }
 }
 
 
 void Octree::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
 void Octree::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
 {
 {
+    // If any of the (size) attributes changes, resize the octree
     Serializable::OnSetAttribute(attr, value);
     Serializable::OnSetAttribute(attr, value);
-    
-    // If any of the size attributes changes, resize the octree
-    /// \todo This may lead to unnecessary resizing, however it is fast once child nodes have been removed
-    switch (attr.offset_)
-    {
-    case offsetof(Octree, worldBoundingBox_.min_):
-    case offsetof(Octree, worldBoundingBox_.max_):
-    case offsetof(Octree, numLevels_):
-        Resize(worldBoundingBox_, numLevels_);
-        break;
-    }
+    Resize(worldBoundingBox_, numLevels_);
 }
 }
 
 
 void Octree::Resize(const BoundingBox& box, unsigned numLevels)
 void Octree::Resize(const BoundingBox& box, unsigned numLevels)

+ 73 - 91
Engine/Graphics/ParticleEmitter.cpp

@@ -80,79 +80,15 @@ ParticleEmitter::~ParticleEmitter()
 void ParticleEmitter::RegisterObject(Context* context)
 void ParticleEmitter::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<ParticleEmitter>();
     context->RegisterFactory<ParticleEmitter>();
-
-    ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Animation LOD Bias", animationLodBias_, 1.0f);
-    ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Relative Scale", scaled_, true);
-    ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Active", active_, true);
-    ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Period Timer", periodTimer_, 0.0f);
-    ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Timer", emissionTimer_, 0.0f);
-    ATTRIBUTE(ParticleEmitter, VAR_RESOURCEREF, "Parameter Source", parameterSource_, ResourceRef(XMLFile::GetTypeStatic()));
-    ATTRIBUTE_MODE(ParticleEmitter, VAR_BUFFER, "Particles", particles_, PODVector<unsigned char>(), AM_SERIALIZATION);
-    ATTRIBUTE_MODE(ParticleEmitter, VAR_BUFFER, "Billboards", billboards_, PODVector<unsigned char>(), AM_SERIALIZATION);
-}
-
-void ParticleEmitter::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
     
     
-    switch (attr.offset_)
-    {
-    case offsetof(ParticleEmitter, parameterSource_):
-        LoadParameters(cache->GetResource<XMLFile>(value.GetResourceRef().id_));
-        break;
-    
-    case offsetof(ParticleEmitter, particles_):
-        {
-            MemoryBuffer buf(value.GetBuffer());
-            SetNumParticles(buf.ReadVLE());
-            for (PODVector<Particle>::Iterator i = particles_.Begin(); i != particles_.End(); ++i)
-            {
-                i->velocity_ = buf.ReadVector3();
-                i->size_ = buf.ReadVector2();
-                i->timer_ = buf.ReadFloat();
-                i->timeToLive_ = buf.ReadFloat();
-                i->scale_ = buf.ReadFloat();
-                i->rotationSpeed_ = buf.ReadFloat();
-                i->colorIndex_ = buf.ReadVLE();
-                i->texIndex_ = buf.ReadVLE();
-            }
-        }
-        break;
-        
-    default:
-        BillboardSet::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant ParticleEmitter::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(ParticleEmitter, parameterSource_):
-        return GetResourceRef(parameterSource_, XMLFile::GetTypeStatic());
-        
-    case offsetof(ParticleEmitter, particles_):
-        {
-            VectorBuffer buf;
-            buf.WriteVLE(particles_.Size());
-            for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
-            {
-                buf.WriteVector3(i->velocity_);
-                buf.WriteVector2(i->size_);
-                buf.WriteFloat(i->timer_);
-                buf.WriteFloat(i->timeToLive_);
-                buf.WriteFloat(i->scale_);
-                buf.WriteFloat(i->rotationSpeed_);
-                buf.WriteVLE(i->colorIndex_);
-                buf.WriteVLE(i->texIndex_);
-            }
-            return buf.GetBuffer();
-        }
-        
-    default:
-        return BillboardSet::OnGetAttribute(attr);
-    }
+    ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Animation LOD Bias", animationLodBias_, 1.0f, AM_DEFAULT);
+    ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Relative Scale", scaled_, true, AM_DEFAULT);
+    ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Active", active_, true, AM_DEFAULT);
+    ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Period Timer", periodTimer_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Timer", emissionTimer_, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_RESOURCEREF, "Parameter Source", GetParameterSourceAttr, SetParameterSourceAttr, ResourceRef, ResourceRef(XMLFile::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BUFFER, "Particles", GetParticlesAttr, SetParticlesAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_FILE);
+    ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BUFFER, "Billboards", GetBillboardsAttr, SetBillboardsAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_FILE);
 }
 }
 
 
 void ParticleEmitter::Update(float timeStep)
 void ParticleEmitter::Update(float timeStep)
@@ -424,29 +360,50 @@ void ParticleEmitter::SetActive(bool enable, bool resetPeriod)
     }
     }
 }
 }
 
 
-void ParticleEmitter::SetNumParticles(int num)
+void ParticleEmitter::SetParameterSourceAttr(ResourceRef value)
 {
 {
-    num = Max(num, 0);
-    particles_.Resize(num);
-    SetNumBillboards(num);
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    LoadParameters(cache->GetResource<XMLFile>(value.id_));
 }
 }
 
 
-void ParticleEmitter::SetParticleColor(const Color& color)
+void ParticleEmitter::SetParticlesAttr(PODVector<unsigned char> value)
 {
 {
-    ColorFade newColor;
-    newColor.color_ = color;
-    newColor.time_ = 0.0f;
-    
-    colors_.Clear();
-    colors_.Push(newColor);
+    MemoryBuffer buf(value);
+    SetNumParticles(buf.ReadVLE());
+    for (PODVector<Particle>::Iterator i = particles_.Begin(); i != particles_.End(); ++i)
+    {
+        i->velocity_ = buf.ReadVector3();
+        i->size_ = buf.ReadVector2();
+        i->timer_ = buf.ReadFloat();
+        i->timeToLive_ = buf.ReadFloat();
+        i->scale_ = buf.ReadFloat();
+        i->rotationSpeed_ = buf.ReadFloat();
+        i->colorIndex_ = buf.ReadVLE();
+        i->texIndex_ = buf.ReadVLE();
+    }
 }
 }
 
 
-void ParticleEmitter::SetParticleColors(const Vector<ColorFade>& colors)
+ResourceRef ParticleEmitter::GetParameterSourceAttr() const
 {
 {
-    if (!colors.Size())
-        return;
-    
-    colors_ = colors;
+    return GetResourceRef(parameterSource_, XMLFile::GetTypeStatic());
+}
+
+PODVector<unsigned char> ParticleEmitter::GetParticlesAttr() const
+{
+    VectorBuffer buf;
+    buf.WriteVLE(particles_.Size());
+    for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
+    {
+        buf.WriteVector3(i->velocity_);
+        buf.WriteVector2(i->size_);
+        buf.WriteFloat(i->timer_);
+        buf.WriteFloat(i->timeToLive_);
+        buf.WriteFloat(i->scale_);
+        buf.WriteFloat(i->rotationSpeed_);
+        buf.WriteVLE(i->colorIndex_);
+        buf.WriteVLE(i->texIndex_);
+    }
+    return buf.GetBuffer();
 }
 }
 
 
 void ParticleEmitter::OnNodeSet(Node* node)
 void ParticleEmitter::OnNodeSet(Node* node)
@@ -461,11 +418,29 @@ void ParticleEmitter::OnNodeSet(Node* node)
     BillboardSet::OnNodeSet(node);
     BillboardSet::OnNodeSet(node);
 }
 }
 
 
-void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
+void ParticleEmitter::SetNumParticles(int num)
 {
 {
-    using namespace ScenePostUpdate;
+    num = Max(num, 0);
+    particles_.Resize(num);
+    SetNumBillboards(num);
+}
+
+void ParticleEmitter::SetParticleColor(const Color& color)
+{
+    ColorFade newColor;
+    newColor.color_ = color;
+    newColor.time_ = 0.0f;
     
     
-    Update(eventData[P_TIMESTEP].GetFloat());
+    colors_.Clear();
+    colors_.Push(newColor);
+}
+
+void ParticleEmitter::SetParticleColors(const Vector<ColorFade>& colors)
+{
+    if (!colors.Size())
+        return;
+    
+    colors_ = colors;
 }
 }
 
 
 bool ParticleEmitter::EmitNewParticle()
 bool ParticleEmitter::EmitNewParticle()
@@ -593,3 +568,10 @@ void ParticleEmitter::GetVector3MinMax(const XMLElement& element, Vector3& minVa
         maxValue = element.GetVector3("max");
         maxValue = element.GetVector3("max");
     }
     }
 }
 }
+
+void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace ScenePostUpdate;
+    
+    Update(eventData[P_TIMESTEP].GetFloat());
+}

+ 12 - 8
Engine/Graphics/ParticleEmitter.h

@@ -78,10 +78,6 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
     /// Update the particle system. Is called from HandleScenePostUpdate()
     /// Update the particle system. Is called from HandleScenePostUpdate()
     void Update(float timeStep);
     void Update(float timeStep);
     /// Load emitter parameters from an XML file. Return true if successful
     /// Load emitter parameters from an XML file. Return true if successful
@@ -96,7 +92,19 @@ public:
     /// Return whether emitter is active
     /// Return whether emitter is active
     bool IsActive() const { return active_; }
     bool IsActive() const { return active_; }
     
     
+    /// Set parameter source attribute
+    void SetParameterSourceAttr(ResourceRef value);
+    /// Set particles attribute
+    void SetParticlesAttr(PODVector<unsigned char> value);
+    /// Return parameter source attribute
+    ResourceRef GetParameterSourceAttr() const;
+    /// Return particles attribute
+    PODVector<unsigned char> GetParticlesAttr() const;
+    
 protected:
 protected:
+    /// Handle node being assigned
+    virtual void OnNodeSet(Node* node);
+    
     /// Set number of particles
     /// Set number of particles
     void SetNumParticles(int num);
     void SetNumParticles(int num);
     /// Set color of particles
     /// Set color of particles
@@ -114,10 +122,6 @@ protected:
     /// Read a Vector3 from an XML element
     /// Read a Vector3 from an XML element
     void GetVector3MinMax(const XMLElement& element, Vector3& minValue, Vector3& maxValue);
     void GetVector3MinMax(const XMLElement& element, Vector3& minValue, Vector3& maxValue);
     
     
-protected:
-    /// Handle node being assigned
-    virtual void OnNodeSet(Node* node);
-    
 private:
 private:
     /// Handle scene post-update event
     /// Handle scene post-update event
     void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
     void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);

+ 26 - 45
Engine/Graphics/StaticModel.cpp

@@ -57,53 +57,11 @@ void StaticModel::RegisterObject(Context* context)
     context->RegisterFactory<StaticModel>();
     context->RegisterFactory<StaticModel>();
     context->CopyBaseAttributes<Drawable, StaticModel>();
     context->CopyBaseAttributes<Drawable, StaticModel>();
     
     
-    ATTRIBUTE(StaticModel, VAR_RESOURCEREF, "Model", model_, ResourceRef(Model::GetTypeStatic()));
-    ATTRIBUTE(StaticModel, VAR_RESOURCEREFLIST, "Materials", materials_, ResourceRefList(Material::GetTypeStatic()));
-    ATTRIBUTE(StaticModel, VAR_INT, "Raycast/Occlusion LOD Level", softwareLodLevel_, M_MAX_UNSIGNED);
+    ACCESSOR_ATTRIBUTE(StaticModel, VAR_RESOURCEREF, "Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(StaticModel, VAR_RESOURCEREFLIST, "Materials", GetMaterialsAttr, SetMaterialsAttr, ResourceRefList, ResourceRefList(Material::GetTypeStatic()), AM_DEFAULT);
+    ATTRIBUTE(StaticModel, VAR_INT, "Raycast/Occlusion LOD Level", softwareLodLevel_, M_MAX_UNSIGNED, AM_DEFAULT);
 }
 }
 
 
-void StaticModel::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(StaticModel, model_):
-        SetModel(cache->GetResource<Model>(value.GetResourceRef().id_));
-        break;
-        
-    case offsetof(StaticModel, materials_):
-        {
-            const ResourceRefList& refs = value.GetResourceRefList();
-            for (unsigned i = 0; i < refs.ids_.Size(); ++i)
-                SetMaterial(i, cache->GetResource<Material>(refs.ids_[i]));
-        }
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant StaticModel::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(StaticModel, model_):
-        return GetResourceRef(model_, Model::GetTypeStatic());
-        break;
-        
-    case offsetof(StaticModel, materials_):
-        return GetResourceRefList(materials_);
-        break;
-    
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
-}
-
-
 void StaticModel::ProcessRayQuery(RayOctreeQuery& query, float initialDistance)
 void StaticModel::ProcessRayQuery(RayOctreeQuery& query, float initialDistance)
 {
 {
     PROFILE(RaycastStaticModel);
     PROFILE(RaycastStaticModel);
@@ -313,6 +271,29 @@ void StaticModel::SetNumGeometries(unsigned num)
     ResetLodLevels();
     ResetLodLevels();
 }
 }
 
 
+void StaticModel::SetModelAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetModel(cache->GetResource<Model>(value.id_));
+}
+
+void StaticModel::SetMaterialsAttr(ResourceRefList value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    for (unsigned i = 0; i < value.ids_.Size(); ++i)
+        SetMaterial(i, cache->GetResource<Material>(value.ids_[i]));
+}
+
+ResourceRef StaticModel::GetModelAttr() const
+{
+    return GetResourceRef(model_, Model::GetTypeStatic());
+}
+
+ResourceRefList StaticModel::GetMaterialsAttr() const
+{
+    return GetResourceRefList(materials_);
+}
+
 void StaticModel::OnWorldBoundingBoxUpdate()
 void StaticModel::OnWorldBoundingBoxUpdate()
 {
 {
     worldBoundingBox_ = boundingBox_.Transformed(GetWorldTransform());
     worldBoundingBox_ = boundingBox_.Transformed(GetWorldTransform());

+ 9 - 6
Engine/Graphics/StaticModel.h

@@ -40,10 +40,6 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
     /// Process renderer raycast
     /// Process renderer raycast
     virtual void ProcessRayQuery(RayOctreeQuery& query, float initialDistance);
     virtual void ProcessRayQuery(RayOctreeQuery& query, float initialDistance);
     /// Prepare geometry for rendering
     /// Prepare geometry for rendering
@@ -75,9 +71,16 @@ public:
     /// Return software LOD level
     /// Return software LOD level
     unsigned GetSoftwareLodLevel() const { return softwareLodLevel_; }
     unsigned GetSoftwareLodLevel() const { return softwareLodLevel_; }
     
     
+    /// Set model attribute
+    void SetModelAttr(ResourceRef value);
+    /// Set materials attribute
+    void SetMaterialsAttr(ResourceRefList value);
+    /// Return model attribute
+    ResourceRef GetModelAttr() const;
+    /// Return materials attribute
+    ResourceRefList GetMaterialsAttr() const;
+    
 protected:
 protected:
-    /// Construct with node flags, initial octant pointer and name
-    StaticModel(unsigned flags, Octant* octant, const String& name);
     /// Update the world bounding box
     /// Update the world bounding box
     virtual void OnWorldBoundingBoxUpdate();
     virtual void OnWorldBoundingBoxUpdate();
     /// Set the bounding box
     /// Set the bounding box

+ 10 - 13
Engine/Graphics/Zone.cpp

@@ -53,29 +53,26 @@ void Zone::RegisterObject(Context* context)
     context->RegisterFactory<Zone>();
     context->RegisterFactory<Zone>();
     context->CopyBaseAttributes<Drawable, Zone>();
     context->CopyBaseAttributes<Drawable, Zone>();
     
     
-    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Min", boundingBox_.min_, Vector3::ZERO);
-    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Max", boundingBox_.max_, Vector3::ZERO);
-    ATTRIBUTE(Zone, VAR_COLOR, "Ambient Color", ambientColor_, DEFAULT_AMBIENT_COLOR);
-    ATTRIBUTE(Zone, VAR_COLOR, "Fog Color", fogColor_, DEFAULT_FOG_COLOR);
-    ATTRIBUTE(Zone, VAR_FLOAT, "Fog Start", fogStart_, DEFAULT_FOGSTART);
-    ATTRIBUTE(Zone, VAR_FLOAT, "Fog End", fogEnd_, DEFAULT_FOGEND);
-    ATTRIBUTE(Zone, VAR_INT, "Zone Priority", priority_, 0);
+    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Min", boundingBox_.min_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_VECTOR3, "Bounding Box Max", boundingBox_.max_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_COLOR, "Ambient Color", ambientColor_, DEFAULT_AMBIENT_COLOR, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_COLOR, "Fog Color", fogColor_, DEFAULT_FOG_COLOR, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_FLOAT, "Fog Start", fogStart_, DEFAULT_FOGSTART, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_FLOAT, "Fog End", fogEnd_, DEFAULT_FOGEND, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_INT, "Zone Priority", priority_, 0, AM_DEFAULT);
 }
 }
 
 
 void Zone::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
 void Zone::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
 {
 {
+    Serializable::OnSetAttribute(attr, value);
+    
+    // If bounding box changes, dirty the drawable as applicable
     switch (attr.offset_)
     switch (attr.offset_)
     {
     {
     case offsetof(Zone, boundingBox_.min_):
     case offsetof(Zone, boundingBox_.min_):
     case offsetof(Zone, boundingBox_.max_):
     case offsetof(Zone, boundingBox_.max_):
-        Serializable::OnSetAttribute(attr, value);
-        // If bounding box changes, dirty the drawable as applicable
         OnMarkedDirty(node_);
         OnMarkedDirty(node_);
         break;
         break;
-    
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
     }
     }
 }
 }
 
 

+ 115 - 91
Engine/Network/Connection.cpp

@@ -130,7 +130,10 @@ void Connection::SetScene(Scene* newScene)
     scene_ = newScene;
     scene_ = newScene;
     sceneLoaded_ = false;
     sceneLoaded_ = false;
     
     
-    if (isClient_ && scene_)
+    if (!scene_)
+        return;
+    
+    if (isClient_)
     {
     {
         sceneState_.Clear();
         sceneState_.Clear();
         
         
@@ -140,6 +143,16 @@ void Connection::SetScene(Scene* newScene)
         msg_.WriteString(scene_->GetFileName());
         msg_.WriteString(scene_->GetFileName());
         SendMessage(MSG_LOADSCENE, true, true, msg_);
         SendMessage(MSG_LOADSCENE, true, true, msg_);
     }
     }
+    else
+    {
+        // Make sure there is no existing async loading
+        scene_->StopAsyncLoading();
+    }
+}
+
+void Connection::SetSceneLoaded(bool loaded)
+{
+    sceneLoaded_ = loaded;
 }
 }
 
 
 void Connection::SetIdentity(const VariantMap& identity)
 void Connection::SetIdentity(const VariantMap& identity)
@@ -171,18 +184,13 @@ void Connection::ProcessReplication()
     const Map<unsigned, Node*>& nodes = scene_->GetAllNodes();
     const Map<unsigned, Node*>& nodes = scene_->GetAllNodes();
     
     
     // Check for new or changed nodes
     // Check for new or changed nodes
-    /// \todo Send in correct order that takes node parenting into account
-    for (Map<unsigned, Node*>::ConstIterator i = nodes.Begin(); i != nodes.End(); ++i)
-    {
-        // Break when we reach local node IDs
-        if (i->first_ >= FIRST_LOCAL_ID)
-            break;
-        
-        if (sceneState_.Find(i->first_) == sceneState_.End())
-            ProcessNewNode(i->second_);
-        else
-            ProcessExistingNode(i->second_);
-    }
+    // Start from the root node (scene) so that the scene-wide components get sent first
+    processedNodes_.Clear();
+    ProcessNode(scene_);
+    
+    // Then go through the rest of the nodes
+    for (Map<unsigned, Node*>::ConstIterator i = nodes.Begin(); i != nodes.End() && i->first_ < FIRST_LOCAL_ID; ++i)
+        ProcessNode(i->second_);
     
     
     // Check for removed nodes
     // Check for removed nodes
     for (Map<unsigned, NodeReplicationState>::Iterator i = sceneState_.Begin(); i != sceneState_.End();)
     for (Map<unsigned, NodeReplicationState>::Iterator i = sceneState_.Begin(); i != sceneState_.End();)
@@ -234,6 +242,31 @@ String Connection::ToString() const
     return GetAddress() + ":" + String(GetPort());
     return GetAddress() + ":" + String(GetPort());
 }
 }
 
 
+void Connection::ProcessNode(Node* node)
+{
+    unsigned nodeID = node->GetID();
+    if (processedNodes_.Contains(nodeID))
+        return;
+    
+    processedNodes_.Insert(nodeID);
+    
+    // Process parent node first
+    Node* parent = node->GetParent();
+    if (parent)
+    {
+        // If parent is local, proceed to first non-local node in hierarchy
+        while (parent->GetID() >= FIRST_LOCAL_ID)
+            parent = parent->GetParent();
+        ProcessNode(parent);
+    }
+    
+    // Check if the client's scene state already has this node
+    if (sceneState_.Find(nodeID) != sceneState_.End())
+        ProcessExistingNode(node);
+    else
+        ProcessNewNode(node);
+}
+
 void Connection::ProcessNewNode(Node* node)
 void Connection::ProcessNewNode(Node* node)
 {
 {
     msg_.Clear();
     msg_.Clear();
@@ -243,18 +276,7 @@ void Connection::ProcessNewNode(Node* node)
     newNodeState.frameNumber_ = frameNumber_;
     newNodeState.frameNumber_ = frameNumber_;
     
     
     // Write node's attributes
     // Write node's attributes
-    msg_.WriteVLE(node->GetNumNetworkAttributes());
-    const Vector<AttributeInfo>* attributes = node->GetAttributes();
-    for (unsigned i = 0; i < attributes->Size(); ++i)
-    {
-        const AttributeInfo& attr = attributes->At(i);
-        if (!(attr.mode_ & AM_NETWORK))
-            continue;
-        
-        Variant value = node->GetAttribute(i);
-        msg_.WriteVariantData(value);
-        newNodeState.attributes_.Push(value);
-    }
+    WriteInitialAttributes(msg_, node, newNodeState.attributes_);
     
     
     // Write node's variable map
     // Write node's variable map
     const VariantMap& vars = node->GetVars();
     const VariantMap& vars = node->GetVars();
@@ -283,21 +305,7 @@ void Connection::ProcessNewNode(Node* node)
         newComponentState.type_ = component->GetType();
         newComponentState.type_ = component->GetType();
         
         
         // Write component's attributes
         // Write component's attributes
-        msg_.WriteVLE(component->GetNumNetworkAttributes());
-        const Vector<AttributeInfo>* attributes = component->GetAttributes();
-        if (attributes)
-        {
-            for (unsigned j = 0; j < attributes->Size(); ++j)
-            {
-                const AttributeInfo& attr = attributes->At(j);
-                if (!(attr.mode_ & AM_NETWORK))
-                    continue;
-                
-                Variant value = component->GetAttribute(j);
-                msg_.WriteVariantData(value);
-                newComponentState.attributes_.Push(value);
-            }
-        }
+        WriteInitialAttributes(msg_, component, newComponentState.attributes_);
         
         
         newNodeState.components_[component->GetID()] = newComponentState;
         newNodeState.components_[component->GetID()] = newComponentState;
     }
     }
@@ -320,20 +328,17 @@ void Connection::ProcessExistingNode(Node* node)
         deltaUpdateBits_[i] = 0;
         deltaUpdateBits_[i] = 0;
     unsigned index = 0;
     unsigned index = 0;
     
     
-    // Make sure the current attributes vector is large enough
-    if (currentAttributes_.Size() < nodeState.attributes_.Size())
-        currentAttributes_.Resize(nodeState.attributes_.Size());
-    
     for (unsigned i = 0; i < attributes->Size(); ++i)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (!(attr.mode_ & AM_NETWORK))
+        if (!(attr.mode_ & AM_NET))
             continue;
             continue;
         
         
         // Check for attribute change
         // Check for attribute change
-        currentAttributes_[index] = node->GetAttribute(i);
-        if (currentAttributes_[index] != nodeState.attributes_[index])
+        Variant value = node->GetAttribute(i);
+        if (value != nodeState.attributes_[index])
         {
         {
+            nodeState.attributes_[index] = value;
             if (attr.mode_ & AM_LATESTDATA)
             if (attr.mode_ & AM_LATESTDATA)
                 latestData = true;
                 latestData = true;
             else
             else
@@ -351,9 +356,10 @@ void Connection::ProcessExistingNode(Node* node)
     const VariantMap& vars = node->GetVars();
     const VariantMap& vars = node->GetVars();
     for (VariantMap::ConstIterator i = vars.Begin(); i != vars.End(); ++i)
     for (VariantMap::ConstIterator i = vars.Begin(); i != vars.End(); ++i)
     {
     {
-        VariantMap::ConstIterator j = nodeState.vars_.Find(i->first_);
+        VariantMap::Iterator j = nodeState.vars_.Find(i->first_);
         if (j == nodeState.vars_.End() || i->second_ != j->second_)
         if (j == nodeState.vars_.End() || i->second_ != j->second_)
         {
         {
+            j->second_ = i->second_;
             changedVars_.Insert(i->first_);
             changedVars_.Insert(i->first_);
             deltaUpdate = true;
             deltaUpdate = true;
         }
         }
@@ -372,14 +378,11 @@ void Connection::ProcessExistingNode(Node* node)
         for (unsigned i = 0; i < attributes->Size(); ++i)
         for (unsigned i = 0; i < attributes->Size(); ++i)
         {
         {
             const AttributeInfo& attr = attributes->At(i);
             const AttributeInfo& attr = attributes->At(i);
-            if (!(attr.mode_ & AM_NETWORK))
+            if (!(attr.mode_ & AM_NET))
                 continue;
                 continue;
             
             
             if (deltaUpdateBits_[index << 3] & (1 << (index & 7)))
             if (deltaUpdateBits_[index << 3] & (1 << (index & 7)))
-            {
-                msg_.WriteVariantData(currentAttributes_[index]);
-                nodeState.attributes_[index] = currentAttributes_[index];
-            }
+                msg_.WriteVariantData(nodeState.attributes_[index]);
             
             
             ++index;
             ++index;
         }
         }
@@ -391,7 +394,6 @@ void Connection::ProcessExistingNode(Node* node)
             VariantMap::ConstIterator j = vars.Find(*i);
             VariantMap::ConstIterator j = vars.Find(*i);
             msg_.WriteShortStringHash(j->first_);
             msg_.WriteShortStringHash(j->first_);
             msg_.WriteVariant(j->second_);
             msg_.WriteVariant(j->second_);
-            nodeState.vars_[j->first_] = j->second_;
         }
         }
         
         
         SendMessage(MSG_NODEDELTAUPDATE, true, true, msg_);
         SendMessage(MSG_NODEDELTAUPDATE, true, true, msg_);
@@ -408,14 +410,11 @@ void Connection::ProcessExistingNode(Node* node)
         for (unsigned i = 0; i < attributes->Size(); ++i)
         for (unsigned i = 0; i < attributes->Size(); ++i)
         {
         {
             const AttributeInfo& attr = attributes->At(i);
             const AttributeInfo& attr = attributes->At(i);
-            if (!(attr.mode_ & AM_NETWORK))
+            if (!(attr.mode_ & AM_NET))
                 continue;
                 continue;
             
             
             if (attr.mode_ & AM_LATESTDATA)
             if (attr.mode_ & AM_LATESTDATA)
-            {
-                msg_.WriteVariantData(currentAttributes_[index]);
-                nodeState.attributes_[index] = currentAttributes_[index];
-            }
+                msg_.WriteVariantData(nodeState.attributes_[index]);
             
             
             ++index;
             ++index;
         }
         }
@@ -445,21 +444,7 @@ void Connection::ProcessExistingNode(Node* node)
             newComponentState.type_ = component->GetType();
             newComponentState.type_ = component->GetType();
             
             
             // Write component's attributes
             // Write component's attributes
-            msg_.WriteVLE(component->GetNumNetworkAttributes());
-            const Vector<AttributeInfo>* attributes = component->GetAttributes();
-            if (attributes)
-            {
-                for (unsigned k = 0; k < attributes->Size(); ++k)
-                {
-                    const AttributeInfo& attr = attributes->At(k);
-                    if (!(attr.mode_ & AM_NETWORK))
-                        continue;
-                    
-                    Variant value = component->GetAttribute(k);
-                    msg_.WriteVariantData(value);
-                    newComponentState.attributes_.Push(value);
-                }
-            }
+            WriteInitialAttributes(msg_, component, newComponentState.attributes_);
             
             
             SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
             SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
             nodeState.components_[component->GetID()] = newComponentState;
             nodeState.components_[component->GetID()] = newComponentState;
@@ -478,20 +463,17 @@ void Connection::ProcessExistingNode(Node* node)
                 deltaUpdateBits_[k] = 0;
                 deltaUpdateBits_[k] = 0;
             index = 0;
             index = 0;
             
             
-            // Make sure the current attributes vector is large enough
-            if (currentAttributes_.Size() < componentState.attributes_.Size())
-                currentAttributes_.Resize(componentState.attributes_.Size());
-            
             for (unsigned k = 0; k < attributes->Size(); ++k)
             for (unsigned k = 0; k < attributes->Size(); ++k)
             {
             {
                 const AttributeInfo& attr = attributes->At(k);
                 const AttributeInfo& attr = attributes->At(k);
-                if (!(attr.mode_ & AM_NETWORK))
+                if (!(attr.mode_ & AM_NET))
                     continue;
                     continue;
                 
                 
                 // Check for attribute change
                 // Check for attribute change
-                currentAttributes_[index] = component->GetAttribute(k);
-                if (currentAttributes_[index] != nodeState.attributes_[index])
+                Variant value = component->GetAttribute(k);
+                if (value != componentState.attributes_[index])
                 {
                 {
+                    componentState.attributes_[index] = value;
                     if (attr.mode_ & AM_LATESTDATA)
                     if (attr.mode_ & AM_LATESTDATA)
                         latestData = true;
                         latestData = true;
                     else
                     else
@@ -517,14 +499,11 @@ void Connection::ProcessExistingNode(Node* node)
                 for (unsigned k = 0; k < attributes->Size(); ++k)
                 for (unsigned k = 0; k < attributes->Size(); ++k)
                 {
                 {
                     const AttributeInfo& attr = attributes->At(k);
                     const AttributeInfo& attr = attributes->At(k);
-                    if (!(attr.mode_ & AM_NETWORK))
+                    if (!(attr.mode_ & AM_NET))
                         continue;
                         continue;
                     
                     
                     if (deltaUpdateBits_[index << 3] & (1 << (index & 7)))
                     if (deltaUpdateBits_[index << 3] & (1 << (index & 7)))
-                    {
-                        msg_.WriteVariantData(currentAttributes_[index]);
-                        componentState.attributes_[index] = currentAttributes_[index];
-                    }
+                        msg_.WriteVariantData(componentState.attributes_[index]);
                     
                     
                     ++index;
                     ++index;
                 }
                 }
@@ -543,14 +522,11 @@ void Connection::ProcessExistingNode(Node* node)
                 for (unsigned k = 0; k < attributes->Size(); ++k)
                 for (unsigned k = 0; k < attributes->Size(); ++k)
                 {
                 {
                     const AttributeInfo& attr = attributes->At(k);
                     const AttributeInfo& attr = attributes->At(k);
-                    if (!(attr.mode_ & AM_NETWORK))
+                    if (!(attr.mode_ & AM_NET))
                         continue;
                         continue;
                     
                     
                     if (attr.mode_ & AM_LATESTDATA)
                     if (attr.mode_ & AM_LATESTDATA)
-                    {
-                        msg_.WriteVariantData(currentAttributes_[index]);
-                        componentState.attributes_[index] = currentAttributes_[index];
-                    }
+                        msg_.WriteVariantData(componentState.attributes_[index]);
                     
                     
                     ++index;
                     ++index;
                 }
                 }
@@ -575,3 +551,51 @@ void Connection::ProcessExistingNode(Node* node)
         }
         }
     }
     }
 }
 }
+
+void Connection::WriteInitialAttributes(VectorBuffer& msg, Serializable* serializable, Vector<Variant>& attributeState)
+{
+    const Vector<AttributeInfo>* attributes = serializable->GetAttributes();
+    unsigned numAttributes = serializable->GetNumNetworkAttributes();
+    if (!numAttributes)
+        return;
+    
+    attributeState.Resize(numAttributes);
+    
+    deltaUpdateBits_.Resize((numAttributes + 7) >> 3);
+    for (unsigned i = 0; i < deltaUpdateBits_.Size(); ++i)
+        deltaUpdateBits_[i] = 0;
+    
+    unsigned index = 0;
+    for (unsigned i = 0; i < attributes->Size(); ++i)
+    {
+        const AttributeInfo& attr = attributes->At(i);
+        if (!(attr.mode_ & AM_NET))
+            continue;
+        
+        Variant value = serializable->GetAttribute(i);
+        if (value != attr.defaultValue_)
+        {
+            attributeState[index] = value;
+            deltaUpdateBits_[index >> 3] |= (1 << (index & 7));
+        }
+        else
+            attributeState[index] = attr.defaultValue_;
+        
+        ++index;
+    }
+    
+    msg.Write(&deltaUpdateBits_[0], deltaUpdateBits_.Size());
+    
+    index = 0;
+    for (unsigned i = 0; i < attributes->Size(); ++i)
+    {
+        const AttributeInfo& attr = attributes->At(i);
+        if (!(attr.mode_ & AM_NET))
+            continue;
+        
+        if (deltaUpdateBits_[index << 3] & (1 << (index & 7)))
+            msg.WriteVariantData(attributeState[index]);
+        
+        ++index;
+    }
+}

+ 9 - 2
Engine/Network/Connection.h

@@ -38,6 +38,7 @@
 
 
 class Node;
 class Node;
 class Scene;
 class Scene;
+class Serializable;
 
 
 /// Connection in a networked scene
 /// Connection in a networked scene
 class Connection : public Object
 class Connection : public Object
@@ -64,6 +65,8 @@ public:
     void SendRemoteEvent(Node* receiver, StringHash eventType, bool inOrder, const VariantMap& eventData = VariantMap());
     void SendRemoteEvent(Node* receiver, StringHash eventType, bool inOrder, const VariantMap& eventData = VariantMap());
     /// Assign scene. On the server, this will cause the client to load it
     /// Assign scene. On the server, this will cause the client to load it
     void SetScene(Scene* newScene);
     void SetScene(Scene* newScene);
+    /// Set scene loaded flag
+    void SetSceneLoaded(bool loaded);
     /// Assign identity. Called by Network
     /// Assign identity. Called by Network
     void SetIdentity(const VariantMap& identity);
     void SetIdentity(const VariantMap& identity);
     /// Set new controls. Moves the current controls as previous
     /// Set new controls. Moves the current controls as previous
@@ -101,10 +104,14 @@ public:
     String ToString() const;
     String ToString() const;
     
     
 private:
 private:
+    /// Process a node during network update. Will recurse to process parent node(s) first
+    void ProcessNode(Node* node);
     /// Process a node that the client had not yet received
     /// Process a node that the client had not yet received
     void ProcessNewNode(Node* node);
     void ProcessNewNode(Node* node);
     /// Process a node that the client has already received
     /// Process a node that the client has already received
     void ProcessExistingNode(Node* node);
     void ProcessExistingNode(Node* node);
+    /// Write initial attributes (that differ from the default value) for a node or a component
+    void WriteInitialAttributes(VectorBuffer& msg, Serializable* serializable, Vector<Variant>& attributeState);
     
     
     /// kNet message connection
     /// kNet message connection
     kNet::SharedPtr<kNet::MessageConnection> connection_;
     kNet::SharedPtr<kNet::MessageConnection> connection_;
@@ -120,10 +127,10 @@ private:
     Map<unsigned, Vector<Variant> > componentLatestData_;
     Map<unsigned, Vector<Variant> > componentLatestData_;
     /// Internal vector for delta update
     /// Internal vector for delta update
     PODVector<unsigned char> deltaUpdateBits_;
     PODVector<unsigned char> deltaUpdateBits_;
-    /// Internal vector for comparing attributes
-    Vector<Variant> currentAttributes_;
     /// Internal set for node's variable map changes
     /// Internal set for node's variable map changes
     HashSet<ShortStringHash> changedVars_;
     HashSet<ShortStringHash> changedVars_;
+    /// Internal set for processed node ID's during an update
+    HashSet<unsigned> processedNodes_;
     /// Reused message buffer
     /// Reused message buffer
     VectorBuffer msg_;
     VectorBuffer msg_;
     /// Current controls
     /// Current controls

+ 100 - 1
Engine/Network/Network.cpp

@@ -24,6 +24,8 @@
 #include "Precompiled.h"
 #include "Precompiled.h"
 #include "Context.h"
 #include "Context.h"
 #include "CoreEvents.h"
 #include "CoreEvents.h"
+#include "File.h"
+#include "FileSystem.h"
 #include "Log.h"
 #include "Log.h"
 #include "MemoryBuffer.h"
 #include "MemoryBuffer.h"
 #include "Network.h"
 #include "Network.h"
@@ -31,6 +33,7 @@
 #include "Profiler.h"
 #include "Profiler.h"
 #include "Protocol.h"
 #include "Protocol.h"
 #include "Scene.h"
 #include "Scene.h"
+#include "SceneEvents.h"
 #include "StringUtils.h"
 #include "StringUtils.h"
 
 
 #include <kNet.h>
 #include <kNet.h>
@@ -51,6 +54,7 @@ Network::Network(Context* context) :
     network_ = new kNet::Network();
     network_ = new kNet::Network();
     
     
     SubscribeToEvent(E_BEGINFRAME, HANDLER(Network, HandleBeginFrame));
     SubscribeToEvent(E_BEGINFRAME, HANDLER(Network, HandleBeginFrame));
+    SubscribeToEvent(E_ASYNCLOADFINISHED, HANDLER(Network, HandleAsyncLoadFinished));
 }
 }
 
 
 Network::~Network()
 Network::~Network()
@@ -358,7 +362,7 @@ void Network::Update(float timeStep)
     }
     }
     
     
     // Process client connections if the server has been started
     // Process client connections if the server has been started
-    kNet::NetworkServer* server = network_->GetServer();
+    kNet::SharedPtr<kNet::NetworkServer> server = network_->GetServer();
     if (server)
     if (server)
     {
     {
         server->Process();
         server->Process();
@@ -429,6 +433,50 @@ bool Network::OnServerMessage(Connection* connection, int msgID, MemoryBuffer& m
 {
 {
     switch (msgID)
     switch (msgID)
     {
     {
+    case MSG_LOADSCENE:
+        {
+            String fileName = msg.ReadString();
+            Scene* scene = connection->GetScene();
+            if (scene)
+            {
+                if (fileName.Empty())
+                    scene->Clear();
+                else
+                {
+                    String extension = GetExtension(fileName);
+                    SharedPtr<File> file(new File(context_, fileName));
+                    bool success;
+                    
+                    if (extension == ".xml")
+                        success = scene->LoadAsyncXML(file);
+                    else
+                        success = scene->LoadAsync(file);
+                    
+                    if (!success)
+                    {
+                        using namespace NetworkSceneLoadFailed;
+                        
+                        VariantMap eventData;
+                        eventData[P_CONNECTION] = (void*)connection;
+                        connection->SendEvent(E_NETWORKSCENELOADFAILED, eventData);
+                    }
+                }
+            }
+            else
+                LOGERROR("Received a LoadScene message without an assigned scene");
+        }
+        return true;
+        
+    case MSG_SCENECHECKSUMERROR:
+        {
+            using namespace NetworkSceneLoadFailed;
+            
+            VariantMap eventData;
+            eventData[P_CONNECTION] = (void*)connection;
+            connection->SendEvent(E_NETWORKSCENELOADFAILED, eventData);
+        }
+        return true;
+        
     case MSG_REMOTEEVENT:
     case MSG_REMOTEEVENT:
     case MSG_REMOTENODEEVENT:
     case MSG_REMOTENODEEVENT:
         OnRemoteEvent(connection, msgID, msg);
         OnRemoteEvent(connection, msgID, msg);
@@ -470,6 +518,40 @@ bool Network::OnClientMessage(Connection* connection, int msgID, MemoryBuffer& m
         }
         }
         return true;
         return true;
         
         
+    case MSG_SCENELOADED:
+        {
+            unsigned checksum = msg.ReadUInt();
+            
+            Scene* scene = connection->GetScene();
+            if (scene)
+            {
+                if (checksum != scene->GetChecksum())
+                {
+                    VectorBuffer replyMsg;
+                    connection->SendMessage(MSG_SCENECHECKSUMERROR, true, true, replyMsg);
+                    
+                    using namespace NetworkSceneLoadFailed;
+                    
+                    VariantMap eventData;
+                    eventData[P_CONNECTION] = (void*)connection;
+                    connection->SendEvent(E_NETWORKSCENELOADFAILED, eventData);
+                }
+                else
+                {
+                    connection->SetSceneLoaded(true);
+                    
+                    using namespace ClientSceneLoaded;
+                    
+                    VariantMap eventData;
+                    eventData[P_CONNECTION] = (void*)connection;
+                    connection->SendEvent(E_CLIENTSCENELOADED, eventData);
+                }
+            }
+            else
+                LOGWARNING("Client sent a SceneLoaded message without an assigned scene");
+        }
+        return true;
+        
     case MSG_REMOTEEVENT:
     case MSG_REMOTEEVENT:
     case MSG_REMOTENODEEVENT:
     case MSG_REMOTENODEEVENT:
         OnRemoteEvent(connection, msgID, msg);
         OnRemoteEvent(connection, msgID, msg);
@@ -515,3 +597,20 @@ void Network::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
     
     
     Update(eventData[P_TIMESTEP].GetFloat());
     Update(eventData[P_TIMESTEP].GetFloat());
 }
 }
+
+void Network::HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData)
+{
+    if (!serverConnection_)
+        return;
+    
+    Scene* scene = serverConnection_->GetScene();
+    
+    using namespace AsyncLoadFinished;
+    
+    if ((void*)scene == eventData[P_SCENE].GetPtr())
+    {
+        VectorBuffer msg;
+        msg.WriteUInt(scene->GetChecksum());
+        serverConnection_->SendMessage(MSG_SCENELOADED, true, true, msg);
+    }
+}

+ 2 - 0
Engine/Network/Network.h

@@ -104,6 +104,8 @@ private:
     void OnRemoteEvent(Connection* connection, int msgID, MemoryBuffer& msg);
     void OnRemoteEvent(Connection* connection, int msgID, MemoryBuffer& msg);
     /// Handle begin frame event
     /// Handle begin frame event
     void HandleBeginFrame(StringHash eventType, VariantMap& eventData);
     void HandleBeginFrame(StringHash eventType, VariantMap& eventData);
+    /// Handle scene loaded event
+    void HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData);
     
     
     /// kNet Network instance
     /// kNet Network instance
     kNet::Network* network_;
     kNet::Network* network_;

+ 6 - 0
Engine/Network/NetworkEvents.h

@@ -77,3 +77,9 @@ EVENT(E_NETWORKMESSAGE, NetworkMessage)
 EVENT(E_NETWORKUPDATE, NetworkUpdate)
 EVENT(E_NETWORKUPDATE, NetworkUpdate)
 {
 {
 }
 }
+
+/// Scene load failed, either due to file not found or checksum error
+EVENT(E_NETWORKSCENELOADFAILED, NetworkSceneLoadFailed)
+{
+    PARAM(P_CONNECTION, Connection);      // Connection pointer
+}

+ 10 - 8
Engine/Network/Protocol.h

@@ -32,22 +32,24 @@ static const int MSG_SCENELOADED = 0x7;
 
 
 // Server->client: load new scene. In case of empty filename the client should just empty the scene
 // Server->client: load new scene. In case of empty filename the client should just empty the scene
 static const int MSG_LOADSCENE = 0x8;
 static const int MSG_LOADSCENE = 0x8;
+// Server->client: wrong scene checksum, can not participate
+static const int MSG_SCENECHECKSUMERROR = 0x9;
 // Server->client: create new node
 // Server->client: create new node
-static const int MSG_CREATENODE = 0x9;
+static const int MSG_CREATENODE = 0xa;
 // Server->client: node delta update
 // Server->client: node delta update
-static const int MSG_NODEDELTAUPDATE = 0xa;
+static const int MSG_NODEDELTAUPDATE = 0xb;
 // Server->client: node latest data update
 // Server->client: node latest data update
-static const int MSG_NODELATESTDATA = 0xb;
+static const int MSG_NODELATESTDATA = 0xc;
 // Server->client: remove node
 // Server->client: remove node
-static const int MSG_REMOVENODE = 0xc;
+static const int MSG_REMOVENODE = 0xd;
 // Server->client: create new component
 // Server->client: create new component
-static const int MSG_CREATECOMPONENT = 0xd;
+static const int MSG_CREATECOMPONENT = 0xe;
 // Server->client: component delta update
 // Server->client: component delta update
-static const int MSG_COMPONENTDELTAUPDATE = 0xe;
+static const int MSG_COMPONENTDELTAUPDATE = 0xf;
 // Server->client: component latest data update
 // Server->client: component latest data update
-static const int MSG_COMPONENTLATESTDATA = 0xf;
+static const int MSG_COMPONENTLATESTDATA = 0x10;
 // Server->client: remove component
 // Server->client: remove component
-static const int MSG_REMOVECOMPONENT = 0x10;
+static const int MSG_REMOVECOMPONENT = 0x11;
 
 
 // Client->server and server->client: remote event
 // Client->server and server->client: remote event
 static const int MSG_REMOTEEVENT = 0x11;
 static const int MSG_REMOTEEVENT = 0x11;

+ 23 - 40
Engine/Physics/CollisionShape.cpp

@@ -323,48 +323,20 @@ void CollisionShape::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<CollisionShape>();
     context->RegisterFactory<CollisionShape>();
     
     
-    ENUM_ATTRIBUTE(CollisionShape, "Shape Type", shapeType_, typeNames, SHAPE_NONE);
-    ATTRIBUTE(CollisionShape, VAR_VECTOR3, "Size", size_, Vector3::ZERO);
-    ATTRIBUTE(CollisionShape, VAR_FLOAT, "Hull Thickness", thickness_, 0.0f);
-    ATTRIBUTE(CollisionShape, VAR_INT, "Model LOD Level", lodLevel_, M_MAX_UNSIGNED);
-    ATTRIBUTE(CollisionShape, VAR_VECTOR3, "Offset Position", position_, Vector3::ZERO);
-    ATTRIBUTE(CollisionShape, VAR_QUATERNION, "Rotation", rotation_, Quaternion::IDENTITY);
-    ATTRIBUTE(CollisionShape, VAR_INT, "Collision Group", collisionGroup_, M_MAX_UNSIGNED);
-    ATTRIBUTE(CollisionShape, VAR_INT, "Collision Mask", collisionMask_, M_MAX_UNSIGNED);
-    ATTRIBUTE(CollisionShape, VAR_FLOAT, "Friction", friction_, DEFAULT_FRICTION);
-    ATTRIBUTE(CollisionShape, VAR_FLOAT, "Bounce", bounce_, DEFAULT_BOUNCE);
-    ATTRIBUTE(CollisionShape, VAR_RESOURCEREF, "Model", model_, ResourceRef(Model::GetTypeStatic()));
+    ENUM_ATTRIBUTE(CollisionShape, "Shape Type", shapeType_, typeNames, SHAPE_NONE, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_VECTOR3, "Size", size_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_FLOAT, "Hull Thickness", thickness_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_INT, "Model LOD Level", lodLevel_, M_MAX_UNSIGNED, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_VECTOR3, "Offset Position", position_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_QUATERNION, "Rotation", rotation_, Quaternion::IDENTITY, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_INT, "Collision Group", collisionGroup_, M_MAX_UNSIGNED, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_INT, "Collision Mask", collisionMask_, M_MAX_UNSIGNED, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_FLOAT, "Friction", friction_, DEFAULT_FRICTION, AM_DEFAULT);
+    ATTRIBUTE(CollisionShape, VAR_FLOAT, "Bounce", bounce_, DEFAULT_BOUNCE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(CollisionShape, VAR_RESOURCEREF, "Model", GetModelAttr, SetModelAttr, ResourceRef, ResourceRef(Model::GetTypeStatic()), AM_DEFAULT);
 }
 }
 
 
-void CollisionShape::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(CollisionShape, model_):
-        model_ = cache->GetResource<Model>(value.GetResourceRef().id_);
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant CollisionShape::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(CollisionShape, model_):
-        return GetResourceRef(model_, Model::GetTypeStatic());
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
-}
-
-void CollisionShape::PostLoad()
+void CollisionShape::OnFinishUpdate()
 {
 {
     CreateGeometry();
     CreateGeometry();
 }
 }
@@ -793,6 +765,17 @@ void CollisionShape::OnMarkedDirty(Node* node)
         UpdateTransform();
         UpdateTransform();
 }
 }
 
 
+void CollisionShape::SetModelAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    model_ = cache->GetResource<Model>(value.id_);
+}
+
+ResourceRef CollisionShape::GetModelAttr() const
+{
+    return GetResourceRef(model_, Model::GetTypeStatic());
+}
+
 void CollisionShape::OnNodeSet(Node* node)
 void CollisionShape::OnNodeSet(Node* node)
 {
 {
     if (node)
     if (node)

+ 7 - 6
Engine/Physics/CollisionShape.h

@@ -99,12 +99,8 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
-    /// Perform post-load after the whole scene has been loaded
-    virtual void PostLoad();
+    /// Perform finalization after a scene load or network update
+    virtual void OnFinishUpdate();
     
     
     /// Clear the collision geometry
     /// Clear the collision geometry
     void Clear();
     void Clear();
@@ -171,6 +167,11 @@ public:
     /// Add debug geometry to the debug graphics
     /// Add debug geometry to the debug graphics
     void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     
     
+    /// Set model attribute
+    void SetModelAttr(ResourceRef value);
+    /// Return model attribute
+    ResourceRef GetModelAttr() const;
+    
 protected:
 protected:
     /// Handle node being assigned
     /// Handle node being assigned
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);

+ 36 - 51
Engine/Physics/Joint.cpp

@@ -46,6 +46,7 @@ OBJECTTYPESTATIC(Joint);
 Joint::Joint(Context* context) :
 Joint::Joint(Context* context) :
     Component(context),
     Component(context),
     type_(JOINT_NONE),
     type_(JOINT_NONE),
+    createdType_(JOINT_NONE),
     joint_(0),
     joint_(0),
     position_(Vector3::ZERO),
     position_(Vector3::ZERO),
     axis_(Vector3::ZERO)
     axis_(Vector3::ZERO)
@@ -61,56 +62,18 @@ void Joint::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<Joint>();
     context->RegisterFactory<Joint>();
     
     
-    ENUM_ATTRIBUTE(Joint, "Joint Type", type_, typeNames, JOINT_NONE);
-    ATTRIBUTE(Joint, VAR_INT, "Body A", bodyA_, 0);
-    ATTRIBUTE(Joint, VAR_INT, "Body B", bodyB_, 0);
-    ATTRIBUTE(Joint, VAR_VECTOR3, "Position", position_, Vector3::ZERO);
-    ATTRIBUTE(Joint, VAR_VECTOR3, "Axis", axis_, Vector3::ZERO);
+    ENUM_ATTRIBUTE(Joint, "Joint Type", type_, typeNames, JOINT_NONE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Joint, VAR_INT, "Body A", GetBodyAAttr, SetBodyAAttr, int, 0, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Joint, VAR_INT, "Body B", GetBodyBAttr, SetBodyBAttr, int, 0, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Joint, VAR_VECTOR3, "Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Joint, VAR_VECTOR3, "Axis", GetAxis, SetAxis, Vector3, Vector3::ZERO, AM_DEFAULT);
 }
 }
 
 
-void Joint::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
+void Joint::OnFinishUpdate()
 {
 {
-    Scene* scene = node_ ? node_->GetScene() : 0;
+    if (type_ == createdType_)
+        return;
     
     
-    switch (attr.offset_)
-    {
-    case offsetof(Joint, bodyA_):
-        bodyA_ = scene ? dynamic_cast<RigidBody*>(scene->GetComponentByID(value.GetInt())) : (RigidBody*)0;
-        break;
-        
-    case offsetof(Joint, bodyB_):
-        bodyB_ = scene ? dynamic_cast<RigidBody*>(scene->GetComponentByID(value.GetInt())) : (RigidBody*)0;
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant Joint::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(Joint, bodyA_):
-        return bodyA_ ? bodyA_->GetID() : 0;
-        
-    case offsetof(Joint, bodyB_):
-        return bodyB_ ? bodyB_->GetID() : 0;
-        
-    case offsetof(Joint, position_):
-        return GetPosition();
-        
-    case offsetof(Joint, axis_):
-        return GetAxis();
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
-}
-
-void Joint::PostLoad()
-{
     switch (type_)
     switch (type_)
     {
     {
     case JOINT_NONE:
     case JOINT_NONE:
@@ -137,7 +100,7 @@ void Joint::Clear()
     
     
     bodyA_.Reset();
     bodyA_.Reset();
     bodyB_.Reset();
     bodyB_.Reset();
-    type_ = JOINT_NONE;
+    createdType_ = type_ = JOINT_NONE;
 }
 }
 
 
 bool Joint::SetBall(const Vector3& position, RigidBody* bodyA, RigidBody* bodyB)
 bool Joint::SetBall(const Vector3& position, RigidBody* bodyA, RigidBody* bodyB)
@@ -161,7 +124,7 @@ bool Joint::SetBall(const Vector3& position, RigidBody* bodyA, RigidBody* bodyB)
     dJointSetBallAnchor(joint_, position.x_, position.y_, position.z_);
     dJointSetBallAnchor(joint_, position.x_, position.y_, position.z_);
     dJointAttach(joint_, bodyA ? bodyA->GetBody() : 0, bodyB ? bodyB->GetBody() : 0);
     dJointAttach(joint_, bodyA ? bodyA->GetBody() : 0, bodyB ? bodyB->GetBody() : 0);
     
     
-    type_ = JOINT_BALL;
+    createdType_ = type_ = JOINT_BALL;
     bodyA_ = bodyA;
     bodyA_ = bodyA;
     bodyB_ = bodyB;
     bodyB_ = bodyB;
     
     
@@ -192,14 +155,14 @@ bool Joint::SetHinge(const Vector3& position, const Vector3& axis, RigidBody* bo
     dJointSetHingeAxis(joint_, NormalizedAxis.x_, NormalizedAxis.y_, NormalizedAxis.z_);
     dJointSetHingeAxis(joint_, NormalizedAxis.x_, NormalizedAxis.y_, NormalizedAxis.z_);
     dJointAttach(joint_, bodyA ? bodyA->GetBody() : 0, bodyB ? bodyB->GetBody() : 0);
     dJointAttach(joint_, bodyA ? bodyA->GetBody() : 0, bodyB ? bodyB->GetBody() : 0);
     
     
-    type_ = JOINT_HINGE;
+    createdType_ = type_ = JOINT_HINGE;
     bodyA_ = bodyA;
     bodyA_ = bodyA;
     bodyB_ = bodyB;
     bodyB_ = bodyB;
     
     
     return true;
     return true;
 }
 }
 
 
-void Joint::SetPosition(const Vector3& position)
+void Joint::SetPosition(Vector3 position)
 {
 {
     switch (type_)
     switch (type_)
     {
     {
@@ -213,7 +176,7 @@ void Joint::SetPosition(const Vector3& position)
     }
     }
 }
 }
 
 
-void Joint::SetAxis(const Vector3& axis)
+void Joint::SetAxis(Vector3 axis)
 {
 {
     switch (type_)
     switch (type_)
     {
     {
@@ -255,6 +218,28 @@ Vector3 Joint::GetAxis() const
     return Vector3::ZERO;
     return Vector3::ZERO;
 }
 }
 
 
+void Joint::SetBodyAAttr(int value)
+{
+    Scene* scene = node_ ? node_->GetScene() : 0;
+    bodyA_ = scene ? dynamic_cast<RigidBody*>(scene->GetComponentByID(value)) : (RigidBody*)0;
+}
+
+void Joint::SetBodyBAttr(int value)
+{
+    Scene* scene = node_ ? node_->GetScene() : 0;
+    bodyB_ = scene ? dynamic_cast<RigidBody*>(scene->GetComponentByID(value)) : (RigidBody*)0;
+}
+
+int Joint::GetBodyAAttr() const
+{
+    return bodyA_ ? bodyA_->GetID() : 0;
+}
+
+int Joint::GetBodyBAttr() const
+{
+    return bodyB_ ? bodyB_->GetID() : 0;
+}
+
 void Joint::OnNodeSet(Node* node)
 void Joint::OnNodeSet(Node* node)
 {
 {
     if (node)
     if (node)

+ 15 - 8
Engine/Physics/Joint.h

@@ -51,12 +51,8 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
-    /// Perform post-load after the whole scene has been loaded
-    virtual void PostLoad();
+    /// Perform finalization after a scene load or network update
+    virtual void OnFinishUpdate();
     
     
     /// Remove the joint
     /// Remove the joint
     void Clear();
     void Clear();
@@ -65,9 +61,9 @@ public:
     /// Set a hinge joint
     /// Set a hinge joint
     bool SetHinge(const Vector3& position, const Vector3& axis, RigidBody* bodyA, RigidBody* bodyB = 0);
     bool SetHinge(const Vector3& position, const Vector3& axis, RigidBody* bodyA, RigidBody* bodyB = 0);
     /// Set joint world position
     /// Set joint world position
-    void SetPosition(const Vector3& position);
+    void SetPosition(Vector3 position);
     /// Set joint world axis if applicable
     /// Set joint world axis if applicable
-    void SetAxis(const Vector3& axis);
+    void SetAxis(Vector3 axis);
     
     
     /// Return physics world
     /// Return physics world
     PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
     PhysicsWorld* GetPhysicsWorld() const { return physicsWorld_; }
@@ -84,6 +80,15 @@ public:
     /// Return joint world axis
     /// Return joint world axis
     Vector3 GetAxis() const;
     Vector3 GetAxis() const;
     
     
+    /// Set body A attribute
+    void SetBodyAAttr(int value);
+    /// Set body B attribute
+    void SetBodyBAttr(int value);
+    /// Return body A attribute
+    int GetBodyAAttr() const;
+    /// Return body B attribute
+    int GetBodyBAttr() const;
+    
 protected:
 protected:
     /// Handle node being assigned
     /// Handle node being assigned
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);
@@ -97,6 +102,8 @@ private:
     SharedPtr<RigidBody> bodyB_;
     SharedPtr<RigidBody> bodyB_;
     /// Joint type
     /// Joint type
     JointType type_;
     JointType type_;
+    /// Last created joint type
+    JointType createdType_;
     /// ODE joint ID
     /// ODE joint ID
     dJointID joint_;
     dJointID joint_;
     /// Joint position for creation during post-load
     /// Joint position for creation during post-load

+ 15 - 15
Engine/Physics/PhysicsWorld.cpp

@@ -134,21 +134,21 @@ void PhysicsWorld::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<PhysicsWorld>();
     context->RegisterFactory<PhysicsWorld>();
     
     
-    ATTRIBUTE(PhysicsWorld, VAR_INT, "Physics FPS", fps_, DEFAULT_FPS);
-    ATTRIBUTE(PhysicsWorld, VAR_INT, "Max Contacts", maxContacts_, DEFAULT_MAXCONTACTS);
-    ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Bounce Threshold", bounceThreshold_, DEFAULT_BOUNCETHRESHOLD);
-    ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Time Accumulator", timeAcc_, 0.0f);
-    ATTRIBUTE(PhysicsWorld, VAR_INT, "Random Seed", randomSeed_, 0);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_VECTOR3, "Gravity", GetGravity, SetGravity, Vector3, Vector3::ZERO);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Linear Damping Threshold", GetLinearDampingThreshold, SetLinearDampingThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Linear Damping Scale", GetLinearDampingScale, SetLinearDampingScale, float, 0.0f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Angular Damping Threshold", GetAngularDampingThreshold, SetAngularDampingThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Angular Damping Scale", GetAngularDampingScale, SetAngularDampingScale, float, 0.0f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "ERP", GetERP, SetERP, float, 0.2f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "CFM", GetCFM, SetCFM, float, 0.00001f);
-    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Contact Surface Layer", GetContactSurfaceLayer, SetContactSurfaceLayer, float, 0.0f);
+    ATTRIBUTE(PhysicsWorld, VAR_INT, "Physics FPS", fps_, DEFAULT_FPS, AM_DEFAULT);
+    ATTRIBUTE(PhysicsWorld, VAR_INT, "Max Contacts", maxContacts_, DEFAULT_MAXCONTACTS, AM_DEFAULT);
+    ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Bounce Threshold", bounceThreshold_, DEFAULT_BOUNCETHRESHOLD, AM_DEFAULT);
+    ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Time Accumulator", timeAcc_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE(PhysicsWorld, VAR_INT, "Random Seed", randomSeed_, 0, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_VECTOR3, "Gravity", GetGravity, SetGravity, Vector3, Vector3::ZERO, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Linear Damping Threshold", GetLinearDampingThreshold, SetLinearDampingThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Linear Damping Scale", GetLinearDampingScale, SetLinearDampingScale, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Angular Damping Threshold", GetAngularDampingThreshold, SetAngularDampingThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Angular Damping Scale", GetAngularDampingScale, SetAngularDampingScale, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "ERP", GetERP, SetERP, float, 0.2f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "CFM", GetCFM, SetCFM, float, 0.00001f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Contact Surface Layer", GetContactSurfaceLayer, SetContactSurfaceLayer, float, 0.0f, AM_DEFAULT);
 }
 }
 
 
 void PhysicsWorld::Update(float timeStep)
 void PhysicsWorld::Update(float timeStep)

+ 13 - 13
Engine/Physics/RigidBody.cpp

@@ -67,19 +67,19 @@ void RigidBody::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<RigidBody>();
     context->RegisterFactory<RigidBody>();
     
     
-    ATTRIBUTE(RigidBody, VAR_FLOAT, "Mass", mass_, DEFAULT_MASS);
-    ACCESSOR_ATTRIBUTE_MODE(RigidBody, VAR_VECTOR3, "Physics Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_SERIALIZATION);
-    ACCESSOR_ATTRIBUTE_MODE(RigidBody, VAR_QUATERNION, "Physics Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_SERIALIZATION);
-    ACCESSOR_ATTRIBUTE_MODE(RigidBody, VAR_VECTOR3, "Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector3, Vector3::ZERO, AM_BOTH | AM_LATESTDATA);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping Threshold", GetLinearDampingThreshold, SetLinearDampingThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping Scale", GetLinearDampingScale, SetLinearDampingScale, float, 0.0f);
-    ACCESSOR_ATTRIBUTE_MODE(RigidBody, VAR_VECTOR3, "Angular Velocity", GetAngularVelocity, SetAngularVelocity, Vector3, Vector3::ZERO, AM_BOTH | AM_LATESTDATA);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping Threshold", GetAngularDampingThreshold, SetAngularDampingThreshold, float, 0.01f);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping Scale", GetAngularDampingScale, SetAngularDampingScale, float, 0.0f);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Max Velocity", GetAngularMaxVelocity, SetAngularMaxVelocity, float, M_INFINITY);
-    ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Is Active", IsActive, SetActive, bool, true);
+    ATTRIBUTE(RigidBody, VAR_FLOAT, "Mass", mass_, DEFAULT_MASS, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Physics Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_FILE);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_QUATERNION, "Physics Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_FILE);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector3, Vector3::ZERO, AM_DEFAULT | AM_LATESTDATA);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping Threshold", GetLinearDampingThreshold, SetLinearDampingThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Linear Damping Scale", GetLinearDampingScale, SetLinearDampingScale, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_VECTOR3, "Angular Velocity", GetAngularVelocity, SetAngularVelocity, Vector3, Vector3::ZERO, AM_DEFAULT | AM_LATESTDATA);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping Threshold", GetAngularDampingThreshold, SetAngularDampingThreshold, float, 0.01f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Damping Scale", GetAngularDampingScale, SetAngularDampingScale, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_FLOAT, "Angular Max Velocity", GetAngularMaxVelocity, SetAngularMaxVelocity, float, M_INFINITY, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(RigidBody, VAR_BOOL, "Is Active", IsActive, SetActive, bool, true, AM_FILE);
 }
 }
 
 
 void RigidBody::SetMass(float mass)
 void RigidBody::SetMass(float mass)

+ 62 - 35
Engine/Scene/Node.cpp

@@ -25,6 +25,7 @@
 #include "Component.h"
 #include "Component.h"
 #include "Context.h"
 #include "Context.h"
 #include "Log.h"
 #include "Log.h"
+#include "MemoryBuffer.h"
 #include "Scene.h"
 #include "Scene.h"
 #include "VectorBuffer.h"
 #include "VectorBuffer.h"
 #include "XMLElement.h"
 #include "XMLElement.h"
@@ -65,11 +66,12 @@ void Node::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<Node>();
     context->RegisterFactory<Node>();
     
     
-    ATTRIBUTE(Node, VAR_STRING, "Name", name_, String());
-    ATTRIBUTE_MODE(Node, VAR_VECTOR3, "Position", position_, Vector3::ZERO, AM_BOTH | AM_LATESTDATA);
-    ATTRIBUTE_MODE(Node, VAR_QUATERNION, "Rotation", rotation_, Quaternion::IDENTITY, AM_BOTH | AM_LATESTDATA);
-    ATTRIBUTE(Node, VAR_VECTOR3, "Scale", scale_, Vector3::UNITY);
-    ATTRIBUTE_MODE(Node, VAR_VARIANTMAP, "Variables", vars_, VariantMap(), AM_SERIALIZATION);
+    REF_ACCESSOR_ATTRIBUTE(Node, VAR_STRING, "Name", GetName, SetName, String, String(), AM_DEFAULT);
+    REF_ACCESSOR_ATTRIBUTE(Node, VAR_VECTOR3, "Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_DEFAULT | AM_LATESTDATA);
+    REF_ACCESSOR_ATTRIBUTE(Node, VAR_QUATERNION, "Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_DEFAULT | AM_LATESTDATA);
+    REF_ACCESSOR_ATTRIBUTE(Node, VAR_VECTOR3, "Scale", GetScale, SetScale, Vector3, Vector3::UNITY, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(Node, VAR_BUFFER, "Parent Node", GetParentAttr, SetParentAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_NET | AM_NOEDIT);
+    ATTRIBUTE(Node, VAR_VARIANTMAP, "Variables", vars_, VariantMap(), AM_FILE);
 }
 }
 
 
 void Node::OnEvent(Object* sender, bool broadcast, StringHash eventType, VariantMap& eventData)
 void Node::OnEvent(Object* sender, bool broadcast, StringHash eventType, VariantMap& eventData)
@@ -91,27 +93,13 @@ void Node::OnEvent(Object* sender, bool broadcast, StringHash eventType, Variant
         Object::OnEvent(sender, broadcast, eventType, eventData);
         Object::OnEvent(sender, broadcast, eventType, eventData);
 }
 }
 
 
-void Node::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
+void Node::OnFinishUpdate()
 {
 {
-    switch (attr.offset_)
-    {
-    case offsetof(Node, name_):
-        SetName(value.GetString());
-        break;
-        
-    case offsetof(Node, position_):
-    case offsetof(Node, rotation_):
-    case offsetof(Node, scale_):
-        Serializable::OnSetAttribute(attr, value);
-        // If transform changes, dirty the node as applicable
-        if (!dirty_)
-            MarkDirty();
-        break;
-    
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
+    for (unsigned i = 0; i < components_.Size(); ++i)
+        components_[i]->OnFinishUpdate();
+    
+    for (unsigned i = 0; i < children_.Size(); ++i)
+        children_[i]->OnFinishUpdate();
 }
 }
 
 
 bool Node::Load(Deserializer& source)
 bool Node::Load(Deserializer& source)
@@ -184,15 +172,6 @@ bool Node::SaveXML(XMLElement& dest)
     return true;
     return true;
 }
 }
 
 
-void Node::PostLoad()
-{
-    for (unsigned i = 0; i < components_.Size(); ++i)
-        components_[i]->PostLoad();
-    
-    for (unsigned i = 0; i < children_.Size(); ++i)
-        children_[i]->PostLoad();
-}
-
 void Node::SetName(const String& name)
 void Node::SetName(const String& name)
 {
 {
     name_ = name;
     name_ = name;
@@ -626,6 +605,54 @@ void Node::SetOwner(Connection* owner)
     owner_ = owner;
     owner_ = owner;
 }
 }
 
 
+void Node::SetParentAttr(PODVector<unsigned char> value)
+{
+    Scene* scene = GetScene();
+    if (!scene)
+        return;
+    
+    MemoryBuffer buf(value);
+    unsigned baseNodeID = buf.ReadVLE();
+    if (!baseNodeID)
+        return;
+    
+    if (baseNodeID < FIRST_LOCAL_ID)
+        SetParent(scene->GetNodeByID(baseNodeID));
+    else
+    {
+        Node* baseNode = scene->GetNodeByID(baseNodeID);
+        if (baseNode)
+            SetParent(baseNode->GetChild(buf.ReadStringHash(), true));
+    }
+}
+
+PODVector<unsigned char> Node::GetParentAttr() const
+{
+    VectorBuffer buf;
+    Scene* scene = GetScene();
+    if (scene && parent_)
+    {
+        unsigned parentID = parent_->GetID();
+        if (parentID < FIRST_LOCAL_ID)
+            buf.WriteVLE(parentID);
+        else
+        {
+            // Parent is local: traverse hierarchy to find a non-local base node
+            // This iteration always stops due to the scene (root) being non-local
+            Node* current = parent_;
+            while (current->GetID() >= FIRST_LOCAL_ID)
+                current = current->GetParent();
+            
+            buf.WriteVLE(current->GetID());
+            buf.WriteStringHash(parent_->GetNameHash());
+        }
+    }
+    else
+        buf.WriteVLE(0);
+    
+    return buf.GetBuffer();
+}
+
 bool Node::Load(Deserializer& source, bool readChildren)
 bool Node::Load(Deserializer& source, bool readChildren)
 {
 {
     // Remove all children and components first in case this is not a fresh load
     // Remove all children and components first in case this is not a fresh load
@@ -734,7 +761,7 @@ Node* Node::CreateChild(unsigned id, bool local)
     
     
     // If zero ID specified, let the scene assign
     // If zero ID specified, let the scene assign
     if (scene_)
     if (scene_)
-        newNode->SetID(id ? id : scene_->GetFreeunsigned(local));
+        newNode->SetID(id ? id : scene_->GetFreeNodeID(local));
     else
     else
         newNode->SetID(id);
         newNode->SetID(id);
     
     

+ 7 - 4
Engine/Scene/Node.h

@@ -45,8 +45,8 @@ public:
     
     
     /// Handle event
     /// Handle event
     virtual void OnEvent(Object* sender, bool broadcast, StringHash eventType, VariantMap& eventData);
     virtual void OnEvent(Object* sender, bool broadcast, StringHash eventType, VariantMap& eventData);
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
+    /// Perform finalization for components and child nodes. Only called after scene load
+    virtual void OnFinishUpdate();
     /// Load from binary data. Return true if successful
     /// Load from binary data. Return true if successful
     virtual bool Load(Deserializer& source);
     virtual bool Load(Deserializer& source);
     /// Load from XML data. Return true if successful
     /// Load from XML data. Return true if successful
@@ -55,8 +55,6 @@ public:
     virtual bool Save(Serializer& dest);
     virtual bool Save(Serializer& dest);
     /// Save as XML data. Return true if successful
     /// Save as XML data. Return true if successful
     virtual bool SaveXML(XMLElement& dest);
     virtual bool SaveXML(XMLElement& dest);
-    /// Perform post-load for components and child nodes
-    virtual void PostLoad();
     
     
     /// Set name
     /// Set name
     void SetName(const String& name);
     void SetName(const String& name);
@@ -241,6 +239,11 @@ public:
     /// Set owner connection for multiplayer
     /// Set owner connection for multiplayer
     void SetOwner(Connection* owner);
     void SetOwner(Connection* owner);
     
     
+    /// Set parent attribute (network only)
+    void SetParentAttr(PODVector<unsigned char> value);
+    /// Return parent attribute (network only)
+    PODVector<unsigned char> GetParentAttr() const;
+    
     /// User variables
     /// User variables
     VariantMap vars_;
     VariantMap vars_;
     
     

+ 7 - 15
Engine/Scene/Scene.cpp

@@ -49,7 +49,7 @@ Scene::Scene(Context* context) :
     asyncLoading_(false)
     asyncLoading_(false)
 {
 {
     // Assign an ID to self so that nodes can refer to this node as a parent
     // Assign an ID to self so that nodes can refer to this node as a parent
-    SetID(GetFreeunsigned(false));
+    SetID(GetFreeNodeID(false));
     NodeAdded(this);
     NodeAdded(this);
     
     
     SubscribeToEvent(E_UPDATE, HANDLER(Scene, HandleUpdate));
     SubscribeToEvent(E_UPDATE, HANDLER(Scene, HandleUpdate));
@@ -70,10 +70,10 @@ void Scene::RegisterObject(Context* context)
     context->RegisterFactory<Scene>();
     context->RegisterFactory<Scene>();
     context->CopyBaseAttributes<Node, Scene>();
     context->CopyBaseAttributes<Node, Scene>();
     
     
-    ATTRIBUTE(Scene, VAR_INT, "Next Non-Local Node ID", nonLocalNodeID_, FIRST_NONLOCAL_ID);
-    ATTRIBUTE(Scene, VAR_INT, "Next Non-Local Component ID", nonLocalComponentID_, FIRST_NONLOCAL_ID);
-    ATTRIBUTE(Scene, VAR_INT, "Next Local Node ID", localNodeID_, FIRST_LOCAL_ID);
-    ATTRIBUTE(Scene, VAR_INT, "Next Local Component ID", localComponentID_, FIRST_LOCAL_ID);
+    ATTRIBUTE(Scene, VAR_INT, "Next Non-Local Node ID", nonLocalNodeID_, FIRST_NONLOCAL_ID, AM_DEFAULT);
+    ATTRIBUTE(Scene, VAR_INT, "Next Non-Local Component ID", nonLocalComponentID_, FIRST_NONLOCAL_ID, AM_DEFAULT);
+    ATTRIBUTE(Scene, VAR_INT, "Next Local Node ID", localNodeID_, FIRST_LOCAL_ID, AM_DEFAULT);
+    ATTRIBUTE(Scene, VAR_INT, "Next Local Component ID", localComponentID_, FIRST_LOCAL_ID, AM_DEFAULT);
 }
 }
 
 
 bool Scene::Load(Deserializer& source)
 bool Scene::Load(Deserializer& source)
@@ -271,14 +271,6 @@ void Scene::Clear()
     checksum_ = 0;
     checksum_ = 0;
 }
 }
 
 
-void Scene::ClearNonLocal()
-{
-    // Because node removal can remove arbitrary other nodes, can not iterate. Instead loop until the first node is local,
-    // or the map is empty
-    while (allNodes_.Size() && allNodes_.Begin()->first_ < FIRST_LOCAL_ID)
-        allNodes_.Begin()->second_->Remove();
-}
-
 void Scene::AddRequiredPackageFile(PackageFile* file)
 void Scene::AddRequiredPackageFile(PackageFile* file)
 {
 {
     if (file)
     if (file)
@@ -325,7 +317,7 @@ float Scene::GetAsyncProgress() const
         return (float)asyncProgress_.loadedNodes_ / (float)asyncProgress_.totalNodes_;
         return (float)asyncProgress_.loadedNodes_ / (float)asyncProgress_.totalNodes_;
 }
 }
 
 
-unsigned Scene::GetFreeunsigned(bool local)
+unsigned Scene::GetFreeNodeID(bool local)
 {
 {
     if (!local)
     if (!local)
     {
     {
@@ -499,7 +491,7 @@ void Scene::FinishAsyncLoading()
 
 
 void Scene::FinishLoading(Deserializer* source)
 void Scene::FinishLoading(Deserializer* source)
 {
 {
-    PostLoad();
+    OnFinishUpdate();
     
     
     if (source)
     if (source)
     {
     {

+ 1 - 3
Engine/Scene/Scene.h

@@ -91,8 +91,6 @@ public:
     void SetActive(bool enable);
     void SetActive(bool enable);
     /// Clear scene completely of nodes and components
     /// Clear scene completely of nodes and components
     void Clear();
     void Clear();
-    /// Clear scene of all non-local child nodes. Note: if they have local children, they will be removed as well
-    void ClearNonLocal();
     /// Add a required package file for multiplayer. To be called on the server
     /// Add a required package file for multiplayer. To be called on the server
     void AddRequiredPackageFile(PackageFile* file);
     void AddRequiredPackageFile(PackageFile* file);
     /// Clear required package files
     /// Clear required package files
@@ -122,7 +120,7 @@ public:
     const Map<unsigned, Component*>& GetAllComponents() const { return allComponents_; }
     const Map<unsigned, Component*>& GetAllComponents() const { return allComponents_; }
     
     
     /// Get free node ID, either non-local or local
     /// Get free node ID, either non-local or local
-    unsigned GetFreeunsigned(bool local);
+    unsigned GetFreeNodeID(bool local);
     /// Get free component ID, either non-local or local
     /// Get free component ID, either non-local or local
     unsigned GetFreeComponentID(bool local);
     unsigned GetFreeComponentID(bool local);
     /// Node added. Assign scene pointer and add to ID map
     /// Node added. Assign scene pointer and add to ID map

+ 6 - 7
Engine/Scene/Serializable.cpp

@@ -35,8 +35,7 @@ OBJECTTYPESTATIC(Serializable);
 
 
 Serializable::Serializable(Context* context) :
 Serializable::Serializable(Context* context) :
     Object(context),
     Object(context),
-    inSerialization_(false),
-    inNetwork_(false)
+    inSerialization_(false)
 {
 {
 }
 }
 
 
@@ -193,7 +192,7 @@ bool Serializable::Load(Deserializer& source)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (!(attr.mode_ & AM_SERIALIZATION))
+        if (!(attr.mode_ & AM_FILE))
             continue;
             continue;
         
         
         if (source.IsEof())
         if (source.IsEof())
@@ -221,7 +220,7 @@ bool Serializable::Save(Serializer& dest)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (!(attr.mode_ & AM_SERIALIZATION))
+        if (!(attr.mode_ & AM_FILE))
             continue;
             continue;
         
         
         if (!dest.WriteVariantData(GetAttribute(i)))
         if (!dest.WriteVariantData(GetAttribute(i)))
@@ -253,7 +252,7 @@ bool Serializable::LoadXML(const XMLElement& source)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (!(attr.mode_ & AM_SERIALIZATION))
+        if (!(attr.mode_ & AM_FILE))
             continue;
             continue;
         
         
         // We could assume fixed order. However, do name-based lookup instead for more robustness
         // We could assume fixed order. However, do name-based lookup instead for more robustness
@@ -320,7 +319,7 @@ bool Serializable::SaveXML(XMLElement& dest)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (!(attr.mode_ & AM_SERIALIZATION))
+        if (!(attr.mode_ & AM_FILE))
             continue;
             continue;
         
         
         XMLElement attrElem = dest.CreateChild("attribute");
         XMLElement attrElem = dest.CreateChild("attribute");
@@ -446,7 +445,7 @@ unsigned Serializable::GetNumNetworkAttributes() const
     for (unsigned i = 0; i < attributes->Size(); ++i)
     for (unsigned i = 0; i < attributes->Size(); ++i)
     {
     {
         const AttributeInfo& attr = attributes->At(i);
         const AttributeInfo& attr = attributes->At(i);
-        if (attr.mode_ & AM_NETWORK)
+        if (attr.mode_ & AM_NET)
             ++num;
             ++num;
     }
     }
     
     

+ 41 - 12
Engine/Scene/Serializable.h

@@ -45,6 +45,8 @@ public:
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
     /// Handle attribute read access. Default implementation reads the variable at offset, or invokes the get accessor
     /// Handle attribute read access. Default implementation reads the variable at offset, or invokes the get accessor
     virtual Variant OnGetAttribute(const AttributeInfo& attr);
     virtual Variant OnGetAttribute(const AttributeInfo& attr);
+    /// Perform finalization after a scene load or network update
+    virtual void OnFinishUpdate() {}
     /// Load from binary data. Return true if successful
     /// Load from binary data. Return true if successful
     virtual bool Load(Deserializer& source);
     virtual bool Load(Deserializer& source);
     /// Save as binary data. Return true if successful
     /// Save as binary data. Return true if successful
@@ -53,8 +55,6 @@ public:
     virtual bool LoadXML(const XMLElement& source);
     virtual bool LoadXML(const XMLElement& source);
     /// Save as XML data. Return true if successful
     /// Save as XML data. Return true if successful
     virtual bool SaveXML(XMLElement& dest);
     virtual bool SaveXML(XMLElement& dest);
-    /// Perform post-load after the whole scene has been loaded
-    virtual void PostLoad() {}
     
     
     /// Set attribute by index. Return true if successfully set
     /// Set attribute by index. Return true if successfully set
     bool SetAttribute(unsigned index, const Variant& value);
     bool SetAttribute(unsigned index, const Variant& value);
@@ -75,12 +75,9 @@ public:
 protected:
 protected:
     /// In serialization -flag
     /// In serialization -flag
     bool inSerialization_;
     bool inSerialization_;
-    /// In network replication -flag
-    bool inNetwork_;
 };
 };
 
 
-
-/// Template implementation of the attribute accessor invoke helper class (stores function pointers of specific class)
+/// Template implementation of the attribute accessor invoke helper class
 template <class T, class U> class AttributeAccessorImpl : public AttributeAccessor
 template <class T, class U> class AttributeAccessorImpl : public AttributeAccessor
 {
 {
 public:
 public:
@@ -114,9 +111,41 @@ public:
     SetFunctionPtr setFunction_;
     SetFunctionPtr setFunction_;
 };
 };
 
 
-#define ATTRIBUTE(className, type, name, variable, defaultValue) context->RegisterAttribute<className>(AttributeInfo(type, name, offsetof(className, variable), defaultValue, AM_BOTH))
-#define ATTRIBUTE_MODE(className, type, name, variable, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(type, name, offsetof(className, variable), defaultValue, mode))
-#define ENUM_ATTRIBUTE(className, name, variable, enumNames, defaultValue) context->RegisterAttribute<className>(AttributeInfo(VAR_INT, name, offsetof(className, variable), enumNames, defaultValue, AM_BOTH))
-#define ENUM_ATTRIBUTE_MODE(className, name, variable, enumNames, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(VAR_INT, name, offsetof(className, variable), enumNames, defaultValue, mode))
-#define ACCESSOR_ATTRIBUTE(className, type, name, getFunction, setFunction, typeName, defaultValue) context->RegisterAttribute<className>(AttributeInfo(type, name, new AttributeAccessorImpl<className, typeName>(&className::getFunction, &className::setFunction), defaultValue, AM_BOTH))
-#define ACCESSOR_ATTRIBUTE_MODE(className, type, name, getFunction, setFunction, typeName, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(type, name, new AttributeAccessorImpl<className, typeName>(&className::getFunction, &className::setFunction), defaultValue, mode))
+/// Template implementation of the attribute accessor invoke helper class using const references
+template <class T, class U> class RefAttributeAccessorImpl : public AttributeAccessor
+{
+public:
+    typedef const U& (T::*GetFunctionPtr)() const;
+    typedef void (T::*SetFunctionPtr)(const U&);
+    
+    /// Construct with function pointers
+    RefAttributeAccessorImpl(GetFunctionPtr getFunction, SetFunctionPtr setFunction) :
+        getFunction_(getFunction),
+        setFunction_(setFunction)
+    {
+    }
+    
+    /// Invoke getter function
+    virtual Variant Get(Serializable* ptr)
+    {
+        T* classPtr = static_cast<T*>(ptr);
+        return Variant((classPtr->*getFunction_)());
+    }
+    
+    /// Invoke setter function
+    virtual void Set(Serializable* ptr, const Variant& value)
+    {
+        T* classPtr = static_cast<T*>(ptr);
+        (classPtr->*setFunction_)(value.Get<U>());
+    }
+    
+    /// Class-specific pointer to getter function
+    GetFunctionPtr getFunction_;
+    /// Class-specific pointer to setter function
+    SetFunctionPtr setFunction_;
+};
+
+#define ATTRIBUTE(className, type, name, variable, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(type, name, offsetof(className, variable), defaultValue, mode))
+#define ENUM_ATTRIBUTE(className, name, variable, enumNames, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(VAR_INT, name, offsetof(className, variable), enumNames, defaultValue, mode))
+#define ACCESSOR_ATTRIBUTE(className, type, name, getFunction, setFunction, typeName, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(type, name, new AttributeAccessorImpl<className, typeName>(&className::getFunction, &className::setFunction), defaultValue, mode))
+#define REF_ACCESSOR_ATTRIBUTE(className, type, name, getFunction, setFunction, typeName, defaultValue, mode) context->RegisterAttribute<className>(AttributeInfo(type, name, new RefAttributeAccessorImpl<className, typeName>(&className::getFunction, &className::setFunction), defaultValue, mode))

+ 56 - 76
Engine/Script/ScriptInstance.cpp

@@ -49,7 +49,7 @@ static const String methodDeclarations[] = {
     "void FixedPostUpdate(float)",
     "void FixedPostUpdate(float)",
     "void Load(Deserializer&)",
     "void Load(Deserializer&)",
     "void Save(Serializer&)",
     "void Save(Serializer&)",
-    "void PostLoad()"
+    "void OnFinishUpdate()"
 };
 };
 
 
 OBJECTTYPESTATIC(ScriptInstance);
 OBJECTTYPESTATIC(ScriptInstance);
@@ -76,83 +76,16 @@ void ScriptInstance::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<ScriptInstance>();
     context->RegisterFactory<ScriptInstance>();
     
     
-    ATTRIBUTE(ScriptInstance, VAR_RESOURCEREF, "Script File", scriptFile_, ResourceRef(ScriptFile::GetTypeStatic()));
-    ATTRIBUTE(ScriptInstance, VAR_STRING, "Class Name", className_, String());
-    ATTRIBUTE(ScriptInstance, VAR_BOOL, "Active", active_, true);
-    ATTRIBUTE(ScriptInstance, VAR_INT, "Fixed Update FPS", fixedUpdateFps_, 0);
-    ATTRIBUTE_MODE(ScriptInstance, VAR_FLOAT, "Time Accumulator", fixedUpdateAcc_, 0.0f, AM_SERIALIZATION);
-    ATTRIBUTE_MODE(ScriptInstance, VAR_BUFFER, "Delayed Method Calls", delayedMethodCalls_, PODVector<unsigned char>(), AM_SERIALIZATION);
-    ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_BUFFER, "Script Data", GetScriptData, SetScriptData, PODVector<unsigned char>, PODVector<unsigned char>());
+    ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_RESOURCEREF, "Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef, ResourceRef(ScriptFile::GetTypeStatic()), AM_DEFAULT);
+    REF_ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_STRING, "Class Name", GetClassName, SetClassName, String, String(), AM_DEFAULT);
+    ATTRIBUTE(ScriptInstance, VAR_BOOL, "Active", active_, true, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_INT, "Fixed Update FPS", GetFixedUpdateFps, SetFixedUpdateFps, int, 0, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_FLOAT, "Time Accumulator", GetFixedUpdateAccAttr, SetFixedUpdateAccAttr, float, 0.0f, AM_FILE);
+    ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_BUFFER, "Delayed Method Calls", GetDelayedMethodCallsAttr, SetDelayedMethodCallsAttr, PODVector<unsigned char>, PODVector<unsigned char>(), AM_FILE);
+    ACCESSOR_ATTRIBUTE(ScriptInstance, VAR_BUFFER, "Script Data", GetScriptData, SetScriptData, PODVector<unsigned char>, PODVector<unsigned char>(), AM_DEFAULT);
 }
 }
 
 
-void ScriptInstance::OnSetAttribute(const AttributeInfo& attr, const Variant& value)
-{
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
-    
-    switch (attr.offset_)
-    {
-    case offsetof(ScriptInstance, scriptFile_):
-        SetScriptFile(cache->GetResource<ScriptFile>(value.GetResourceRef().id_));
-        break;
-        
-    case offsetof(ScriptInstance, className_):
-        SetClassName(value.GetString());
-        break;
-        
-    case offsetof(ScriptInstance, fixedUpdateFps_):
-        SetFixedUpdateFps(value.GetInt());
-        break;
-        
-    case offsetof(ScriptInstance, fixedUpdateAcc_):
-        Serializable::OnSetAttribute(attr, value);
-        fixedPostUpdateAcc_ = fixedUpdateAcc_;
-        break;
-        
-    case offsetof(ScriptInstance, delayedMethodCalls_):
-        {
-            MemoryBuffer buf(value.GetBuffer());
-            delayedMethodCalls_.Resize(buf.ReadVLE());
-            for (Vector<DelayedMethodCall>::Iterator i = delayedMethodCalls_.Begin(); i != delayedMethodCalls_.End(); ++i)
-            {
-                i->delay_ = buf.ReadFloat();
-                i->declaration_ = buf.ReadString();
-                i->parameters_ = buf.ReadVariantVector();
-            }
-        }
-        break;
-        
-    default:
-        Serializable::OnSetAttribute(attr, value);
-        break;
-    }
-}
-
-Variant ScriptInstance::OnGetAttribute(const AttributeInfo& attr)
-{
-    switch (attr.offset_)
-    {
-    case offsetof(ScriptInstance, scriptFile_):
-        return GetResourceRef(scriptFile_, ScriptFile::GetTypeStatic());
-        
-    case offsetof(ScriptInstance, delayedMethodCalls_):
-        {
-            VectorBuffer buf;
-            buf.WriteVLE(delayedMethodCalls_.Size());
-            for (Vector<DelayedMethodCall>::ConstIterator i = delayedMethodCalls_.Begin(); i != delayedMethodCalls_.End(); ++i)
-            {
-                buf.WriteFloat(i->delay_);
-                buf.WriteString(i->declaration_);
-                buf.WriteVariantVector(i->parameters_);
-            }
-            return buf.GetBuffer();
-        }
-        
-    default:
-        return Serializable::OnGetAttribute(attr);
-    }
-}
-
-void ScriptInstance::PostLoad()
+void ScriptInstance::OnFinishUpdate()
 {
 {
     if (scriptObject_ && methods_[METHOD_POSTLOAD])
     if (scriptObject_ && methods_[METHOD_POSTLOAD])
         scriptFile_->Execute(scriptObject_, methods_[METHOD_POSTLOAD]);
         scriptFile_->Execute(scriptObject_, methods_[METHOD_POSTLOAD]);
@@ -303,6 +236,53 @@ void ScriptInstance::AddEventHandler(Object* sender, StringHash eventType, const
     SubscribeToEvent(sender, eventType, HANDLER_USERDATA(ScriptInstance, HandleScriptEvent, (void*)method));
     SubscribeToEvent(sender, eventType, HANDLER_USERDATA(ScriptInstance, HandleScriptEvent, (void*)method));
 }
 }
 
 
+void ScriptInstance::SetScriptFileAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetScriptFile(cache->GetResource<ScriptFile>(value.id_));
+}
+
+void ScriptInstance::SetDelayedMethodCallsAttr(PODVector<unsigned char> value)
+{
+    MemoryBuffer buf(value);
+    delayedMethodCalls_.Resize(buf.ReadVLE());
+    for (Vector<DelayedMethodCall>::Iterator i = delayedMethodCalls_.Begin(); i != delayedMethodCalls_.End(); ++i)
+    {
+        i->delay_ = buf.ReadFloat();
+        i->declaration_ = buf.ReadString();
+        i->parameters_ = buf.ReadVariantVector();
+    }
+}
+
+void ScriptInstance::SetFixedUpdateAccAttr(float value)
+{
+    fixedUpdateAcc_ = value;
+    fixedPostUpdateAcc_ = value;
+}
+
+ResourceRef ScriptInstance::GetScriptFileAttr() const
+{
+    return GetResourceRef(scriptFile_, ScriptFile::GetTypeStatic());
+}
+
+PODVector<unsigned char> ScriptInstance::GetDelayedMethodCallsAttr() const
+{
+    VectorBuffer buf;
+    buf.WriteVLE(delayedMethodCalls_.Size());
+    for (Vector<DelayedMethodCall>::ConstIterator i = delayedMethodCalls_.Begin(); i != delayedMethodCalls_.End(); ++i)
+    {
+        buf.WriteFloat(i->delay_);
+        buf.WriteString(i->declaration_);
+        buf.WriteVariantVector(i->parameters_);
+    }
+    return buf.GetBuffer();
+}
+
+float ScriptInstance::GetFixedUpdateAccAttr() const
+{
+    return fixedUpdateAcc_;
+}
+
 void ScriptInstance::CreateObject()
 void ScriptInstance::CreateObject()
 {
 {
     if (!scriptFile_ || className_.Empty())
     if (!scriptFile_ || className_.Empty())

+ 14 - 5
Engine/Script/ScriptInstance.h

@@ -70,12 +70,8 @@ public:
     /// Register object factory
     /// Register object factory
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
     
     
-    /// Handle attribute write access
-    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& value);
-    /// Handle attribute read access
-    virtual Variant OnGetAttribute(const AttributeInfo& attr);
     /// Perform post-load after the whole scene has been loaded
     /// Perform post-load after the whole scene has been loaded
-    virtual void PostLoad();
+    virtual void OnFinishUpdate();
     /// Add an event handler. Called by script exposed version of SubscribeToEvent()
     /// Add an event handler. Called by script exposed version of SubscribeToEvent()
     virtual void AddEventHandler(StringHash eventType, const String& handlerName);
     virtual void AddEventHandler(StringHash eventType, const String& handlerName);
     /// Add an event handler for a specific sender. Called by script exposed version of SubscribeToEvent()
     /// Add an event handler for a specific sender. Called by script exposed version of SubscribeToEvent()
@@ -111,6 +107,19 @@ public:
     /// Return fixed updates per second
     /// Return fixed updates per second
     int GetFixedUpdateFps() const { return fixedUpdateFps_; }
     int GetFixedUpdateFps() const { return fixedUpdateFps_; }
     
     
+    /// Set script file attribute
+    void SetScriptFileAttr(ResourceRef value);
+    /// Set delayed method calls attribute
+    void SetDelayedMethodCallsAttr(PODVector<unsigned char> value);
+    /// Set fixed update time accumulator attribute
+    void SetFixedUpdateAccAttr(float value);
+    /// Return script file attribute
+    ResourceRef GetScriptFileAttr() const;
+    /// Return delayed method calls attribute
+    PODVector<unsigned char> GetDelayedMethodCallsAttr() const;
+    /// Return fixed update time accumulator attribute
+    float GetFixedUpdateAccAttr() const;
+    
 private:
 private:
     /// (Re)create the script object and check for supported methods if successfully created
     /// (Re)create the script object and check for supported methods if successfully created
     void CreateObject();
     void CreateObject();

+ 1 - 1
Urho3D/Urho3D.cpp

@@ -44,7 +44,7 @@ void Run(const char* cmdLine);
 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
 {
 {
     #if defined(_MSC_VER) && defined(_DEBUG)
     #if defined(_MSC_VER) && defined(_DEBUG)
-    //_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
     #endif
     #endif
     
     
     #if defined(ENABLE_MINIDUMPS) && !defined(_DEBUG)
     #if defined(ENABLE_MINIDUMPS) && !defined(_DEBUG)