Browse Source

Merge pull request #2105 from eugeneko/attribute-metadata

Add attribute metadata.
Eugene Kozlov 8 years ago
parent
commit
79f01057d3

+ 1 - 0
Docs/Urho3D.dox

@@ -1366,6 +1366,7 @@ From 1.6 to 1.7:
 
 
 From 1.7 to master:
 From 1.7 to master:
 - URHO3D_CXX11 define was removed. C++11 mode is unconditionally enabled.
 - URHO3D_CXX11 define was removed. C++11 mode is unconditionally enabled.
+- URHO3D_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE and URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE macros were removed. Use attribute metadata instead. Element names shall be stored in StringVector (without trailing zero) instead of const char*[].
 
 
 */
 */
 
 

+ 1 - 10
Source/Urho3D/AngelScript/CoreAPI.cpp

@@ -859,15 +859,6 @@ static CScriptArray* AttributeInfoGetEnumNames(AttributeInfo* ptr)
     return VectorToArray<String>(enumNames, "Array<String>");
     return VectorToArray<String>(enumNames, "Array<String>");
 }
 }
 
 
-static CScriptArray* AttributeInfoGetVariantStructureElementNames(AttributeInfo* ptr)
-{
-    Vector<String> variantStructureElementNames;
-    const char** variantStructureElementNamesPtrs = ptr->variantStructureElementNames_;
-    while (variantStructureElementNamesPtrs && *variantStructureElementNamesPtrs)
-        variantStructureElementNames.Push(*variantStructureElementNamesPtrs++);
-    return VectorToArray<String>(variantStructureElementNames, "Array<String>");
-}
-
 static Object* CreateObject(const String& objectType)
 static Object* CreateObject(const String& objectType)
 {
 {
     if (Context* context = GetScriptContext())
     if (Context* context = GetScriptContext())
@@ -1014,11 +1005,11 @@ void RegisterObject(asIScriptEngine* engine)
     engine->RegisterObjectBehaviour("AttributeInfo", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructAttributeInfo), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("AttributeInfo", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructAttributeInfo), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("AttributeInfo", "AttributeInfo& opAssign(const AttributeInfo&in)", asMETHODPR(AttributeInfo, operator =, (const AttributeInfo&), AttributeInfo&), asCALL_THISCALL);
     engine->RegisterObjectMethod("AttributeInfo", "AttributeInfo& opAssign(const AttributeInfo&in)", asMETHODPR(AttributeInfo, operator =, (const AttributeInfo&), AttributeInfo&), asCALL_THISCALL);
     engine->RegisterObjectMethod("AttributeInfo", "Array<String>@ get_enumNames() const", asFUNCTION(AttributeInfoGetEnumNames), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("AttributeInfo", "Array<String>@ get_enumNames() const", asFUNCTION(AttributeInfoGetEnumNames), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("AttributeInfo", "Array<String>@ get_variantStructureElementNames() const", asFUNCTION(AttributeInfoGetVariantStructureElementNames), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectProperty("AttributeInfo", "VariantType type", offsetof(AttributeInfo, type_));
     engine->RegisterObjectProperty("AttributeInfo", "VariantType type", offsetof(AttributeInfo, type_));
     engine->RegisterObjectProperty("AttributeInfo", "String name", offsetof(AttributeInfo, name_));
     engine->RegisterObjectProperty("AttributeInfo", "String name", offsetof(AttributeInfo, name_));
     engine->RegisterObjectProperty("AttributeInfo", "Variant defaultValue", offsetof(AttributeInfo, defaultValue_));
     engine->RegisterObjectProperty("AttributeInfo", "Variant defaultValue", offsetof(AttributeInfo, defaultValue_));
     engine->RegisterObjectProperty("AttributeInfo", "uint mode", offsetof(AttributeInfo, mode_));
     engine->RegisterObjectProperty("AttributeInfo", "uint mode", offsetof(AttributeInfo, mode_));
+    engine->RegisterObjectProperty("AttributeInfo", "VariantMap metadata", offsetof(AttributeInfo, metadata_));
 
 
     RegisterObject<Object>(engine, "Object");
     RegisterObject<Object>(engine, "Object");
 
 

+ 4 - 19
Source/Urho3D/AngelScript/SceneAPI.cpp

@@ -51,7 +51,7 @@ static void RegisterSerializable(asIScriptEngine* engine)
     engine->RegisterEnumValue("AutoRemoveMode", "REMOVE_NODE", REMOVE_NODE);
     engine->RegisterEnumValue("AutoRemoveMode", "REMOVE_NODE", REMOVE_NODE);
 
 
     engine->RegisterGlobalProperty("const uint AM_FILEREADONLY", (void*)&AM_FILEREADONLY);
     engine->RegisterGlobalProperty("const uint AM_FILEREADONLY", (void*)&AM_FILEREADONLY);
-    
+
     RegisterSerializable<Serializable>(engine, "Serializable");
     RegisterSerializable<Serializable>(engine, "Serializable");
 }
 }
 
 
@@ -289,23 +289,8 @@ static CScriptArray* GetObjectsByCategory(const String& category)
 static CScriptArray* GetObjectAttributeInfos(const String& objectType)
 static CScriptArray* GetObjectAttributeInfos(const String& objectType)
 {
 {
     const Vector<AttributeInfo>* attributes = GetScriptContext()->GetAttributes(Urho3D::StringHash(objectType));
     const Vector<AttributeInfo>* attributes = GetScriptContext()->GetAttributes(Urho3D::StringHash(objectType));
-    Vector<AttributeInfo> copiedAttributes;
-    if (attributes)
-        for (Vector<AttributeInfo>::ConstIterator i = attributes->Begin(); i != attributes->End(); ++i)
-        {
-            AttributeInfo copy;
-            copy.type_ = i->type_;
-            copy.name_ = i->name_;
-            copy.offset_ = i->offset_;
-            copy.enumNames_ = i->enumNames_;
-            copy.variantStructureElementNames_ = i->variantStructureElementNames_;
-            copy.accessor_ = i->accessor_;
-            copy.defaultValue_ = i->defaultValue_;
-            copy.mode_ = i->mode_;
-            copy.ptr_ = i->ptr_;
-            copiedAttributes.Push(copy);
-        }
-    return VectorToArray<AttributeInfo>(copiedAttributes, "Array<AttributeInfo>");
+    static Vector<AttributeInfo> emptyAttributes;
+    return VectorToArray<AttributeInfo>(attributes ? *attributes : emptyAttributes, "Array<AttributeInfo>");
 }
 }
 
 
 static void RegisterSmoothedTransform(asIScriptEngine* engine)
 static void RegisterSmoothedTransform(asIScriptEngine* engine)
@@ -386,7 +371,7 @@ static void RegisterScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "void Clear(bool clearReplicated = true, bool clearLocal = true)", asMETHOD(Scene, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void Clear(bool clearReplicated = true, bool clearLocal = true)", asMETHOD(Scene, Clear), 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", "void RegisterVar(const String&in)", asMETHOD(Scene, RegisterVar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void RegisterVar(const String&in)", asMETHOD(Scene, RegisterVar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void UnregisterVar(const String&in)", asMETHOD(Scene, UnregisterVar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void UnregisterVar(const String&in)", asMETHOD(Scene, UnregisterVar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void UnregisterAllVars(const String&in)", asMETHOD(Scene, UnregisterAllVars), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void UnregisterAllVars(const String&in)", asMETHOD(Scene, UnregisterAllVars), asCALL_THISCALL);

+ 37 - 18
Source/Urho3D/Core/Attribute.h

@@ -69,7 +69,6 @@ struct AttributeInfo
         type_(VAR_NONE),
         type_(VAR_NONE),
         offset_(0),
         offset_(0),
         enumNames_(0),
         enumNames_(0),
-        variantStructureElementNames_(0),
         mode_(AM_DEFAULT),
         mode_(AM_DEFAULT),
         ptr_(0)
         ptr_(0)
     {
     {
@@ -81,7 +80,6 @@ struct AttributeInfo
         name_(name),
         name_(name),
         offset_((unsigned)offset),
         offset_((unsigned)offset),
         enumNames_(0),
         enumNames_(0),
-        variantStructureElementNames_(0),
         defaultValue_(defaultValue),
         defaultValue_(defaultValue),
         mode_(mode),
         mode_(mode),
         ptr_(0)
         ptr_(0)
@@ -94,7 +92,6 @@ struct AttributeInfo
         name_(name),
         name_(name),
         offset_((unsigned)offset),
         offset_((unsigned)offset),
         enumNames_(enumNames),
         enumNames_(enumNames),
-        variantStructureElementNames_(0),
         defaultValue_(defaultValue),
         defaultValue_(defaultValue),
         mode_(mode),
         mode_(mode),
         ptr_(0)
         ptr_(0)
@@ -107,7 +104,6 @@ struct AttributeInfo
         name_(name),
         name_(name),
         offset_(0),
         offset_(0),
         enumNames_(0),
         enumNames_(0),
-        variantStructureElementNames_(0),
         accessor_(accessor),
         accessor_(accessor),
         defaultValue_(defaultValue),
         defaultValue_(defaultValue),
         mode_(mode),
         mode_(mode),
@@ -122,7 +118,6 @@ struct AttributeInfo
         name_(name),
         name_(name),
         offset_(0),
         offset_(0),
         enumNames_(enumNames),
         enumNames_(enumNames),
-        variantStructureElementNames_(0),
         accessor_(accessor),
         accessor_(accessor),
         defaultValue_(defaultValue),
         defaultValue_(defaultValue),
         mode_(mode),
         mode_(mode),
@@ -130,18 +125,17 @@ struct AttributeInfo
     {
     {
     }
     }
 
 
-    /// Construct variant structure (structure, which packed to VariantVector) attribute.
-    AttributeInfo(VariantType type, const char* name, AttributeAccessor* accessor, const Variant& defaultValue, const char** variantStructureElementNames, unsigned mode) :
-        type_(type),
-        name_(name),
-        offset_(0),
-        enumNames_(0),
-        variantStructureElementNames_(variantStructureElementNames),
-        accessor_(accessor),
-        defaultValue_(defaultValue),
-        mode_(mode),
-        ptr_(0)
+    /// Get attribute metadata.
+    const Variant& GetMetadata(const StringHash& key) const
     {
     {
+        auto elem = metadata_.Find(key);
+        return elem != metadata_.End() ? elem->second_ : Variant::EMPTY;
+    }
+
+    /// Get attribute metadata of specified type.
+    template <class T> T GetMetadata(const StringHash& key) const
+    {
+        return GetMetadata(key).Get<T>();
     }
     }
 
 
     /// Attribute type.
     /// Attribute type.
@@ -152,16 +146,41 @@ struct AttributeInfo
     unsigned offset_;
     unsigned offset_;
     /// Enum names.
     /// Enum names.
     const char** enumNames_;
     const char** enumNames_;
-    /// Variant structure elements names.
-    const char** variantStructureElementNames_;
     /// Helper object for accessor mode.
     /// Helper object for accessor mode.
     SharedPtr<AttributeAccessor> accessor_;
     SharedPtr<AttributeAccessor> accessor_;
     /// Default value for network replication.
     /// Default value for network replication.
     Variant defaultValue_;
     Variant defaultValue_;
     /// Attribute mode: whether to use for serialization, network replication, or both.
     /// Attribute mode: whether to use for serialization, network replication, or both.
     unsigned mode_;
     unsigned mode_;
+    /// Attribute metadata.
+    VariantMap metadata_;
     /// Attribute data pointer if elsewhere than in the Serializable.
     /// Attribute data pointer if elsewhere than in the Serializable.
     void* ptr_;
     void* ptr_;
 };
 };
 
 
+/// Attribute handle returned by Context::RegisterAttribute and used to chain attribute setup calls.
+struct AttributeHandle
+{
+    friend class Context;
+private:
+    /// Construct default.
+    AttributeHandle() = default;
+    /// Construct from another handle.
+    AttributeHandle(const AttributeHandle& another) = default;
+    /// Attribute info.
+    AttributeInfo* attributeInfo_ = nullptr;
+    /// Network attribute info.
+    AttributeInfo* networkAttributeInfo_ = nullptr;
+public:
+    /// Set metadata.
+    AttributeHandle& SetMetadata(StringHash key, const Variant& value)
+    {
+        if (attributeInfo_)
+            attributeInfo_->metadata_[key] = value;
+        if (networkAttributeInfo_)
+            networkAttributeInfo_->metadata_[key] = value;
+        return *this;
+    }
+};
+
 }
 }

+ 13 - 4
Source/Urho3D/Core/Context.cpp

@@ -193,7 +193,7 @@ void Context::RemoveSubsystem(StringHash objectType)
         subsystems_.Erase(i);
         subsystems_.Erase(i);
 }
 }
 
 
-void Context::RegisterAttribute(StringHash objectType, const AttributeInfo& attr)
+AttributeHandle Context::RegisterAttribute(StringHash objectType, const AttributeInfo& attr)
 {
 {
     // None or pointer types can not be supported
     // None or pointer types can not be supported
     if (attr.type_ == VAR_NONE || attr.type_ == VAR_VOIDPTR || attr.type_ == VAR_PTR
     if (attr.type_ == VAR_NONE || attr.type_ == VAR_VOIDPTR || attr.type_ == VAR_PTR
@@ -201,13 +201,22 @@ void Context::RegisterAttribute(StringHash objectType, const AttributeInfo& attr
     {
     {
         URHO3D_LOGWARNING("Attempt to register unsupported attribute type " + Variant::GetTypeName(attr.type_) + " to class " +
         URHO3D_LOGWARNING("Attempt to register unsupported attribute type " + Variant::GetTypeName(attr.type_) + " to class " +
             GetTypeName(objectType));
             GetTypeName(objectType));
-        return;
+        return AttributeHandle();
     }
     }
 
 
-    attributes_[objectType].Push(attr);
+    AttributeHandle handle;
+
+    Vector<AttributeInfo>& objectAttributes = attributes_[objectType];
+    objectAttributes.Push(attr);
+    handle.attributeInfo_ = &objectAttributes.Back();
 
 
     if (attr.mode_ & AM_NET)
     if (attr.mode_ & AM_NET)
-        networkAttributes_[objectType].Push(attr);
+    {
+        Vector<AttributeInfo>& objectNetworkAttributes = networkAttributes_[objectType];
+        objectNetworkAttributes.Push(attr);
+        handle.networkAttributeInfo_ = &objectNetworkAttributes.Back();
+    }
+    return handle;
 }
 }
 
 
 void Context::RemoveAttribute(StringHash objectType, const char* name)
 void Context::RemoveAttribute(StringHash objectType, const char* name)

+ 3 - 3
Source/Urho3D/Core/Context.h

@@ -89,7 +89,7 @@ public:
     /// Remove a subsystem.
     /// Remove a subsystem.
     void RemoveSubsystem(StringHash objectType);
     void RemoveSubsystem(StringHash objectType);
     /// Register object attribute.
     /// Register object attribute.
-    void RegisterAttribute(StringHash objectType, const AttributeInfo& attr);
+    AttributeHandle RegisterAttribute(StringHash objectType, const AttributeInfo& attr);
     /// Remove object attribute.
     /// Remove object attribute.
     void RemoveAttribute(StringHash objectType, const char* name);
     void RemoveAttribute(StringHash objectType, const char* name);
     /// Update object attribute's default value.
     /// Update object attribute's default value.
@@ -116,7 +116,7 @@ public:
     /// Template version of removing a subsystem.
     /// Template version of removing a subsystem.
     template <class T> void RemoveSubsystem();
     template <class T> void RemoveSubsystem();
     /// Template version of registering an object attribute.
     /// Template version of registering an object attribute.
-    template <class T> void RegisterAttribute(const AttributeInfo& attr);
+    template <class T> AttributeHandle RegisterAttribute(const AttributeInfo& attr);
     /// Template version of removing an object attribute.
     /// Template version of removing an object attribute.
     template <class T> void RemoveAttribute(const char* name);
     template <class T> void RemoveAttribute(const char* name);
     /// Template version of copying base class attributes to derived class.
     /// Template version of copying base class attributes to derived class.
@@ -249,7 +249,7 @@ template <class T> void Context::RegisterFactory(const char* category)
 
 
 template <class T> void Context::RemoveSubsystem() { RemoveSubsystem(T::GetTypeStatic()); }
 template <class T> void Context::RemoveSubsystem() { RemoveSubsystem(T::GetTypeStatic()); }
 
 
-template <class T> void Context::RegisterAttribute(const AttributeInfo& attr) { RegisterAttribute(T::GetTypeStatic(), attr); }
+template <class T> AttributeHandle Context::RegisterAttribute(const AttributeInfo& attr) { return RegisterAttribute(T::GetTypeStatic(), attr); }
 
 
 template <class T> void Context::RemoveAttribute(const char* name) { RemoveAttribute(T::GetTypeStatic(), name); }
 template <class T> void Context::RemoveAttribute(const char* name) { RemoveAttribute(T::GetTypeStatic(), name); }
 
 

+ 5 - 6
Source/Urho3D/Graphics/AnimatedModel.cpp

@@ -49,7 +49,7 @@ namespace Urho3D
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
 
 
-const char* animationStatesStructureElementNames[] =
+static const StringVector animationStatesStructureElementNames =
 {
 {
     "Anim State Count",
     "Anim State Count",
     "   Animation",
     "   Animation",
@@ -57,8 +57,7 @@ const char* animationStatesStructureElementNames[] =
     "   Is Looped",
     "   Is Looped",
     "   Weight",
     "   Weight",
     "   Time",
     "   Time",
-    "   Layer",
-    0
+    "   Layer"
 };
 };
 
 
 static bool CompareAnimationOrder(const SharedPtr<AnimationState>& lhs, const SharedPtr<AnimationState>& rhs)
 static bool CompareAnimationOrder(const SharedPtr<AnimationState>& lhs, const SharedPtr<AnimationState>& rhs)
@@ -119,9 +118,9 @@ void AnimatedModel::RegisterObject(Context* context)
     URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
     URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
     URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Bone Animation Enabled", GetBonesEnabledAttr, SetBonesEnabledAttr, VariantVector,
     URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Bone Animation Enabled", GetBonesEnabledAttr, SetBonesEnabledAttr, VariantVector,
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-    URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Animation States", GetAnimationStatesAttr, SetAnimationStatesAttr,
-                                                            VariantVector, Variant::emptyVariantVector,
-                                                            animationStatesStructureElementNames, AM_FILE);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Animation States", GetAnimationStatesAttr, SetAnimationStatesAttr,
+        VariantVector, Variant::emptyVariantVector, AM_FILE)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, animationStatesStructureElementNames);
     URHO3D_ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
     URHO3D_ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
         AM_DEFAULT | AM_NOEDIT);
         AM_DEFAULT | AM_NOEDIT);
 }
 }

+ 6 - 8
Source/Urho3D/Graphics/BillboardSet.cpp

@@ -57,7 +57,7 @@ const char* faceCameraModeNames[] =
     0
     0
 };
 };
 
 
-const char* billboardsStructureElementNames[] =
+static const StringVector billboardsStructureElementNames =
 {
 {
     "Billboard Count",
     "Billboard Count",
     "   Position",
     "   Position",
@@ -66,8 +66,7 @@ const char* billboardsStructureElementNames[] =
     "   Color",
     "   Color",
     "   Rotation",
     "   Rotation",
     "   Direction",
     "   Direction",
-    "   Is Enabled",
-    0
+    "   Is Enabled"
 };
 };
 
 
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
@@ -129,9 +128,8 @@ void BillboardSet::RegisterObject(Context* context)
     URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
     URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
     URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
-    URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Billboards", GetBillboardsAttr, SetBillboardsAttr,
-                                                            VariantVector, Variant::emptyVariantVector,
-                                                            billboardsStructureElementNames, AM_FILE);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector, AM_FILE)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, billboardsStructureElementNames);
     URHO3D_ACCESSOR_ATTRIBUTE("Network Billboards", GetNetBillboardsAttr, SetNetBillboardsAttr, PODVector<unsigned char>,
     URHO3D_ACCESSOR_ATTRIBUTE("Network Billboards", GetNetBillboardsAttr, SetNetBillboardsAttr, PODVector<unsigned char>,
         Variant::emptyBuffer, AM_NET | AM_NOEDIT);
         Variant::emptyBuffer, AM_NET | AM_NOEDIT);
 }
 }
@@ -521,7 +519,7 @@ void BillboardSet::UpdateBufferSize()
         }
         }
         geometryTypeUpdate_ = false;
         geometryTypeUpdate_ = false;
     }
     }
-    
+
     bool largeIndices = (numBillboards * 4) >= 65536;
     bool largeIndices = (numBillboards * 4) >= 65536;
 
 
     if (indexBuffer_->GetIndexCount() != numBillboards * 6)
     if (indexBuffer_->GetIndexCount() != numBillboards * 6)
@@ -551,7 +549,7 @@ void BillboardSet::UpdateBufferSize()
             dest[3] = vertexIndex + 2;
             dest[3] = vertexIndex + 2;
             dest[4] = vertexIndex + 3;
             dest[4] = vertexIndex + 3;
             dest[5] = vertexIndex;
             dest[5] = vertexIndex;
-            
+
             dest += 6;
             dest += 6;
             vertexIndex += 4;
             vertexIndex += 4;
         }
         }

+ 6 - 7
Source/Urho3D/Graphics/StaticModelGroup.cpp

@@ -40,11 +40,10 @@ namespace Urho3D
 
 
 extern const char* GEOMETRY_CATEGORY;
 extern const char* GEOMETRY_CATEGORY;
 
 
-const char* instanceNodesStructureElementNames[] =
+static const StringVector instanceNodesStructureElementNames =
 {
 {
     "Instance Count",
     "Instance Count",
-    "   NodeID",
-    0
+    "   NodeID"
 };
 };
 
 
 StaticModelGroup::StaticModelGroup(Context* context) :
 StaticModelGroup::StaticModelGroup(Context* context) :
@@ -65,9 +64,9 @@ void StaticModelGroup::RegisterObject(Context* context)
     context->RegisterFactory<StaticModelGroup>(GEOMETRY_CATEGORY);
     context->RegisterFactory<StaticModelGroup>(GEOMETRY_CATEGORY);
 
 
     URHO3D_COPY_BASE_ATTRIBUTES(StaticModel);
     URHO3D_COPY_BASE_ATTRIBUTES(StaticModel);
-    URHO3D_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Instance Nodes", GetNodeIDsAttr, SetNodeIDsAttr,
-                                                       VariantVector, Variant::emptyVariantVector, instanceNodesStructureElementNames,
-                                                       AM_DEFAULT | AM_NODEIDVECTOR);
+    URHO3D_ACCESSOR_ATTRIBUTE("Instance Nodes", GetNodeIDsAttr, SetNodeIDsAttr,
+        VariantVector, Variant::emptyVariantVector, AM_DEFAULT | AM_NODEIDVECTOR)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, instanceNodesStructureElementNames);
 }
 }
 
 
 void StaticModelGroup::ApplyAttributes()
 void StaticModelGroup::ApplyAttributes()
@@ -390,7 +389,7 @@ void StaticModelGroup::OnWorldBoundingBoxUpdate()
 
 
     worldBoundingBox_ = worldBox;
     worldBoundingBox_ = worldBox;
 
 
-    // Store the amount of valid instances we found instead of resizing worldTransforms_. This is because this function may be 
+    // Store the amount of valid instances we found instead of resizing worldTransforms_. This is because this function may be
     // called from multiple worker threads simultaneously
     // called from multiple worker threads simultaneously
     numWorldTransforms_ = index;
     numWorldTransforms_ = index;
 }
 }

+ 10 - 12
Source/Urho3D/Navigation/CrowdManager.cpp

@@ -46,16 +46,15 @@ extern const char* NAVIGATION_CATEGORY;
 static const unsigned DEFAULT_MAX_AGENTS = 512;
 static const unsigned DEFAULT_MAX_AGENTS = 512;
 static const float DEFAULT_MAX_AGENT_RADIUS = 0.f;
 static const float DEFAULT_MAX_AGENT_RADIUS = 0.f;
 
 
-const char* filterTypesStructureElementNames[] =
+static const StringVector filterTypesStructureElementNames =
 {
 {
     "Query Filter Type Count",
     "Query Filter Type Count",
     "   Include Flags",
     "   Include Flags",
     "   Exclude Flags",
     "   Exclude Flags",
-    "   >AreaCost",
-    0
+    "   >AreaCost"
 };
 };
 
 
-const char* obstacleAvoidanceTypesStructureElementNames[] =
+static const StringVector obstacleAvoidanceTypesStructureElementNames =
 {
 {
     "Obstacle Avoid. Type Count",
     "Obstacle Avoid. Type Count",
     "   Velocity Bias",
     "   Velocity Bias",
@@ -67,8 +66,7 @@ const char* obstacleAvoidanceTypesStructureElementNames[] =
     "   Grid Size",
     "   Grid Size",
     "   Adaptive Divs",
     "   Adaptive Divs",
     "   Adaptive Rings",
     "   Adaptive Rings",
-    "   Adaptive Depth",
-    0
+    "   Adaptive Depth"
 };
 };
 
 
 void CrowdAgentUpdateCallback(dtCrowdAgent* ag, float dt)
 void CrowdAgentUpdateCallback(dtCrowdAgent* ag, float dt)
@@ -104,12 +102,12 @@ void CrowdManager::RegisterObject(Context* context)
     URHO3D_ATTRIBUTE("Max Agents", unsigned, maxAgents_, DEFAULT_MAX_AGENTS, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Max Agents", unsigned, maxAgents_, DEFAULT_MAX_AGENTS, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Max Agent Radius", float, maxAgentRadius_, DEFAULT_MAX_AGENT_RADIUS, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Max Agent Radius", float, maxAgentRadius_, DEFAULT_MAX_AGENT_RADIUS, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Navigation Mesh", unsigned, navigationMeshId_, 0, AM_DEFAULT | AM_COMPONENTID);
     URHO3D_ATTRIBUTE("Navigation Mesh", unsigned, navigationMeshId_, 0, AM_DEFAULT | AM_COMPONENTID);
-    URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Filter Types", GetQueryFilterTypesAttr, SetQueryFilterTypesAttr,
-                                                             VariantVector, Variant::emptyVariantVector,
-                                                             filterTypesStructureElementNames, AM_DEFAULT);
-    URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Obstacle Avoidance Types", GetObstacleAvoidanceTypesAttr, SetObstacleAvoidanceTypesAttr,
-                                                             VariantVector, Variant::emptyVariantVector,
-                                                             obstacleAvoidanceTypesStructureElementNames, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Filter Types", GetQueryFilterTypesAttr, SetQueryFilterTypesAttr,
+        VariantVector, Variant::emptyVariantVector, AM_DEFAULT)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, filterTypesStructureElementNames);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Obstacle Avoidance Types", GetObstacleAvoidanceTypesAttr, SetObstacleAvoidanceTypesAttr,
+        VariantVector, Variant::emptyVariantVector, AM_DEFAULT)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, obstacleAvoidanceTypesStructureElementNames);
 }
 }
 
 
 void CrowdManager::ApplyAttributes()
 void CrowdManager::ApplyAttributes()

+ 5 - 7
Source/Urho3D/Physics/RaycastVehicle.cpp

@@ -129,7 +129,7 @@ struct RaycastVehicleData
     bool added_;
     bool added_;
 };
 };
 
 
-RaycastVehicle::RaycastVehicle(Context* context) : 
+RaycastVehicle::RaycastVehicle(Context* context) :
     LogicComponent(context)
     LogicComponent(context)
 {
 {
     // fixed update() for inputs and post update() to sync wheels for rendering
     // fixed update() for inputs and post update() to sync wheels for rendering
@@ -147,7 +147,7 @@ RaycastVehicle::~RaycastVehicle()
     wheelNodes_.Clear();
     wheelNodes_.Clear();
 }
 }
 
 
-const char* wheelElementNames[] =
+static const StringVector wheelElementNames =
 {
 {
     "Number of wheels",
     "Number of wheels",
     "   Wheel node id",
     "   Wheel node id",
@@ -170,16 +170,14 @@ const char* wheelElementNames[] =
     "   Friction slip",
     "   Friction slip",
     "   Roll influence",
     "   Roll influence",
     "   Engine force",
     "   Engine force",
-    "   Brake",
-    0
+    "   Brake"
 };
 };
 
 
 void RaycastVehicle::RegisterObject(Context* context)
 void RaycastVehicle::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<RaycastVehicle>();
     context->RegisterFactory<RaycastVehicle>();
-    URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Wheel data", GetWheelDataAttr, SetWheelDataAttr,
-            VariantVector, Variant::emptyVariantVector,
-            wheelElementNames, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Wheel data", GetWheelDataAttr, SetWheelDataAttr, VariantVector, Variant::emptyVariantVector, AM_DEFAULT)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, wheelElementNames);
     URHO3D_ATTRIBUTE("Maximum side slip threshold", float, maxSideSlipSpeed_, 4.0f, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Maximum side slip threshold", float, maxSideSlipSpeed_, 4.0f, AM_DEFAULT);
     URHO3D_ATTRIBUTE("RPM for wheel motors in air (0=calculate)", float, inAirRPM_, 0.0f, AM_DEFAULT);
     URHO3D_ATTRIBUTE("RPM for wheel motors in air (0=calculate)", float, inAirRPM_, 0.0f, AM_DEFAULT);
 }
 }

+ 7 - 4
Source/Urho3D/Scene/Serializable.h

@@ -340,6 +340,13 @@ public:
     SetFunctionPtr setFunction_;
     SetFunctionPtr setFunction_;
 };
 };
 
 
+/// Attribute metadata.
+namespace AttributeMetadata
+{
+    /// Names of vector struct elements. StringVector.
+    static const StringHash P_VECTOR_STRUCT_ELEMENTS("VectorStructElements");
+}
+
 // The following macros need to be used within a class member function such as ClassName::RegisterObject().
 // The following macros need to be used within a class member function such as ClassName::RegisterObject().
 // A variable called "context" needs to exist in the current scope and point to a valid Context object.
 // A variable called "context" needs to exist in the current scope and point to a valid Context object.
 
 
@@ -365,9 +372,5 @@ public:
 #define URHO3D_MIXED_ACCESSOR_ATTRIBUTE_FREE(name, getFunction, setFunction, typeName, defaultValue, mode) context->RegisterAttribute<ClassName>(Urho3D::AttributeInfo(Urho3D::GetVariantType<typeName >(), name, new Urho3D::AttributeAccessorFreeImpl<ClassName, typeName, Urho3D::MixedAttributeTrait<typeName > >(getFunction, setFunction), defaultValue, mode))
 #define URHO3D_MIXED_ACCESSOR_ATTRIBUTE_FREE(name, getFunction, setFunction, typeName, defaultValue, mode) context->RegisterAttribute<ClassName>(Urho3D::AttributeInfo(Urho3D::GetVariantType<typeName >(), name, new Urho3D::AttributeAccessorFreeImpl<ClassName, typeName, Urho3D::MixedAttributeTrait<typeName > >(getFunction, setFunction), defaultValue, mode))
 /// Update the default value of an already registered attribute.
 /// Update the default value of an already registered attribute.
 #define URHO3D_UPDATE_ATTRIBUTE_DEFAULT_VALUE(name, defaultValue) context->UpdateAttributeDefaultValue<ClassName>(name, defaultValue)
 #define URHO3D_UPDATE_ATTRIBUTE_DEFAULT_VALUE(name, defaultValue) context->UpdateAttributeDefaultValue<ClassName>(name, defaultValue)
-/// Define a variant structure attribute that uses get and set functions.
-#define URHO3D_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE(name, getFunction, setFunction, typeName, defaultValue, variantStructureElementNames, mode) context->RegisterAttribute<ClassName>(Urho3D::AttributeInfo(Urho3D::GetVariantType<typeName >(), name, new Urho3D::AttributeAccessorImpl<ClassName, typeName, Urho3D::AttributeTrait<typeName > >(&ClassName::getFunction, &ClassName::setFunction), defaultValue, variantStructureElementNames, mode))
-/// Define a variant structure attribute that uses get and set functions, where the get function returns by value, but the set function uses a reference.
-#define URHO3D_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE(name, getFunction, setFunction, typeName, defaultValue, variantStructureElementNames, mode) context->RegisterAttribute<ClassName>(Urho3D::AttributeInfo(Urho3D::GetVariantType<typeName >(), name, new Urho3D::AttributeAccessorImpl<ClassName, typeName, Urho3D::MixedAttributeTrait<typeName > >(&ClassName::getFunction, &ClassName::setFunction), defaultValue, variantStructureElementNames, mode))
 
 
 }
 }

+ 5 - 6
Source/Urho3D/Scene/SplinePath.cpp

@@ -33,11 +33,10 @@ namespace Urho3D
 extern const char* interpolationModeNames[];
 extern const char* interpolationModeNames[];
 extern const char* LOGIC_CATEGORY;
 extern const char* LOGIC_CATEGORY;
 
 
-const char* controlPointsStructureElementNames[] =
+static const StringVector controlPointsStructureElementNames =
 {
 {
     "Control Point Count",
     "Control Point Count",
-    "   NodeID",
-    0
+    "   NodeID"
 };
 };
 
 
 SplinePath::SplinePath(Context* context) :
 SplinePath::SplinePath(Context* context) :
@@ -63,9 +62,9 @@ void SplinePath::RegisterObject(Context* context)
     URHO3D_ATTRIBUTE("Traveled", float, traveled_, 0.f, AM_FILE | AM_NOEDIT);
     URHO3D_ATTRIBUTE("Traveled", float, traveled_, 0.f, AM_FILE | AM_NOEDIT);
     URHO3D_ATTRIBUTE("Elapsed Time", float, elapsedTime_, 0.f, AM_FILE | AM_NOEDIT);
     URHO3D_ATTRIBUTE("Elapsed Time", float, elapsedTime_, 0.f, AM_FILE | AM_NOEDIT);
     URHO3D_ACCESSOR_ATTRIBUTE("Controlled", GetControlledIdAttr, SetControlledIdAttr, unsigned, 0, AM_FILE | AM_NODEID);
     URHO3D_ACCESSOR_ATTRIBUTE("Controlled", GetControlledIdAttr, SetControlledIdAttr, unsigned, 0, AM_FILE | AM_NODEID);
-    URHO3D_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Control Points", GetControlPointIdsAttr, SetControlPointIdsAttr,
-                                                            VariantVector, Variant::emptyVariantVector, controlPointsStructureElementNames,
-                                                            AM_FILE | AM_NODEIDVECTOR);
+    URHO3D_ACCESSOR_ATTRIBUTE("Control Points", GetControlPointIdsAttr, SetControlPointIdsAttr,
+        VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NODEIDVECTOR)
+        .SetMetadata(AttributeMetadata::P_VECTOR_STRUCT_ELEMENTS, controlPointsStructureElementNames);
 }
 }
 
 
 void SplinePath::ApplyAttributes()
 void SplinePath::ApplyAttributes()

+ 2 - 2
bin/Data/Scripts/Editor/AttributeEditor.as

@@ -1516,9 +1516,9 @@ void InitVectorStructs()
             for (uint attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++)
             for (uint attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++)
             {
             {
                 AttributeInfo attribute = attributes[attributeIndex];
                 AttributeInfo attribute = attributes[attributeIndex];
-                if (attribute.type == VAR_VARIANTVECTOR and attribute.variantStructureElementNames.length > 0)
+                if (attribute.type == VAR_VARIANTVECTOR and attribute.metadata.Contains("VectorStructElements"))
                 {
                 {
-                    Array<String> elementsNames = attribute.variantStructureElementNames;
+                    Array<String>@ elementsNames = attribute.metadata["VectorStructElements"].GetStringVector();
                     vectorStructs.Push(VectorStruct(objectName, attribute.name, elementsNames, 1));
                     vectorStructs.Push(VectorStruct(objectName, attribute.name, elementsNames, 1));
                 }
                 }
             }
             }