Răsfoiți Sursa

Expose Geometry, IndexBuffer & VertexBuffer to script, with VB & IB content access using VectorBuffer. AngelScript 34_DynamicGeometry example. Lua version to follow.

Lasse Öörni 10 ani în urmă
părinte
comite
0235c02653

+ 1 - 1
Docs/Reference.dox

@@ -507,7 +507,7 @@ When a scene is saved/loaded, any pending delayed calls are also saved and resto
 
 \section Script_ScriptAPI The script API
 
-Much of the Urho3D classes are exposed to scripts, however things that require low-level access or high performance (like direct vertex buffer access) are not. Also for scripting convenience some things have been changed from the C++ API:
+Much of the Urho3D classes are exposed to scripts, however things that require low-level access or high performance (like direct low level rendering) are not. Also for scripting convenience some things have been changed from the C++ API:
 
 - The template array and string classes are exposed as Array<type> and String.
 

+ 2 - 0
Source/Urho3D/Graphics/Model.h

@@ -165,6 +165,8 @@ public:
     const PODVector<Vector3>& GetGeometryCenters() const { return geometryCenters_; }
     /// Return geometry by index and LOD level. The LOD level is clamped if out of range.
     Geometry* GetGeometry(unsigned index, unsigned lodLevel) const;
+    /// Return geometry center by index.
+    const Vector3& GetGeometryCenter(unsigned index) const { return index < geometryCenters_.Size() ? geometryCenters_[index] : Vector3::ZERO; }
     /// Return geometery bone mappings.
     const Vector<PODVector<unsigned> >& GetGeometryBoneMappings() const { return geometryBoneMappings_; }
     /// Return vertex morphs.

+ 50 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/Geometry.pkg

@@ -0,0 +1,50 @@
+$#include "Graphics/Geometry.h"
+
+class Geometry : public Object
+{
+    Geometry();
+    ~Geometry();
+    
+    bool SetNumVertexBuffers(unsigned num);
+    bool SetVertexBuffer(unsigned index, VertexBuffer* buffer, unsigned elementMask = MASK_DEFAULT);
+    void SetIndexBuffer(IndexBuffer* buffer);
+    bool SetDrawRange(PrimitiveType type, unsigned indexStart, unsigned indexCount, bool getUsedVertexRange = true);
+    bool SetDrawRange(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned vertexStart, unsigned vertexCount, bool checkIllegal = true);
+    void SetLodDistance(float distance);
+
+    unsigned GetNumVertexBuffers() const;
+    VertexBuffer* GetVertexBuffer(unsigned index) const;
+    unsigned GetVertexElementMask(unsigned index) const;
+    IndexBuffer* GetIndexBuffer() const;
+    PrimitiveType GetPrimitiveType() const;
+    unsigned GetIndexStart() const;
+    unsigned GetIndexCount() const;
+    unsigned GetVertexStart() const;
+    unsigned GetVertexCount() const;
+    float GetLodDistance();
+    bool IsEmpty() const;
+    
+    tolua_property__get_set unsigned numVertexBuffers;
+    tolua_property__get_set IndexBuffer* indexBuffer;
+    tolua_readonly tolua_property__get_set PrimitiveType primitiveType;
+    tolua_readonly tolua_property__get_set unsigned indexStart;
+    tolua_readonly tolua_property__get_set unsigned indexCount;
+    tolua_readonly tolua_property__get_set unsigned vertexStart;
+    tolua_readonly tolua_property__get_set unsigned vertexCount;
+    tolua_property__get_set float lodDistance;
+    tolua_readonly tolua_property__is_set bool empty;
+};
+
+${
+#define TOLUA_DISABLE_tolua_GraphicsLuaAPI_Geometry_new00
+static int tolua_GraphicsLuaAPI_Geometry_new00(lua_State* tolua_S)
+{
+    return ToluaNewObject<Geometry>(tolua_S);
+}
+
+#define TOLUA_DISABLE_tolua_GraphicsLuaAPI_Geometry_new00_local
+static int tolua_GraphicsLuaAPI_Geometry_new00_local(lua_State* tolua_S)
+{
+    return ToluaNewObjectGC<Geometry>(tolua_S);
+}
+$}

+ 71 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/IndexBuffer.pkg

@@ -0,0 +1,71 @@
+$#include "Graphics/IndexBuffer.h"
+
+class IndexBuffer : public Object
+{
+    IndexBuffer();
+    ~IndexBuffer();
+
+    void SetShadowed(bool enable);
+    bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
+    tolua_outside bool IndexBufferSetData @ SetData(VectorBuffer& data);
+    tolua_outside bool IndexBufferSetDataRange @ SetDataRange(VectorBuffer& data, unsigned start, unsigned count, bool discard = false);
+    tolua_outside VectorBuffer IndexBufferGetData @ GetData();
+
+    bool IsShadowed() const;
+    bool IsDynamic() const;
+    unsigned GetIndexCount() const;
+    unsigned GetIndexSize() const;
+    
+    tolua_property__is_set bool shadowed;
+    tolua_readonly tolua_property__is_set bool dynamic;
+    tolua_readonly tolua_property__get_set unsigned indexCount;
+    tolua_readonly tolua_property__get_set unsigned indexSize;
+};
+
+${
+#define TOLUA_DISABLE_tolua_GraphicsLuaAPI_IndexBuffer_new00
+static int tolua_GraphicsLuaAPI_IndexBuffer_new00(lua_State* tolua_S)
+{
+    return ToluaNewObject<IndexBuffer>(tolua_S);
+}
+
+#define TOLUA_DISABLE_tolua_GraphicsLuaAPI_IndexBuffer_new00_local
+static int tolua_GraphicsLuaAPI_IndexBuffer_new00_local(lua_State* tolua_S)
+{
+    return ToluaNewObjectGC<IndexBuffer>(tolua_S);
+}
+
+static bool IndexBufferSetData(IndexBuffer* dest, VectorBuffer& src)
+{
+    // Make sure there is enough data
+    if (dest->GetIndexCount() && src.GetSize() >= dest->GetIndexCount() * dest->GetIndexSize())
+        return dest->SetData(&src.GetBuffer()[0]);
+    else
+        return false;
+}
+
+static bool IndexBufferSetDataRange(IndexBuffer* dest, VectorBuffer& src, unsigned start, unsigned count, bool discard)
+{
+    // Make sure there is enough data
+    if (dest->GetIndexCount() && src.GetSize() >= count * dest->GetIndexSize())
+        return dest->SetDataRange(&src.GetBuffer()[0], start, count, discard);
+    else
+        return false;
+}
+
+static VectorBuffer IndexBufferGetData(IndexBuffer* src)
+{
+    VectorBuffer ret;
+    void* data = src->Lock(0, src->GetIndexCount(), false);
+
+    if (data)
+    {
+        ret.Write(data, src->GetIndexCount() * src->GetIndexSize());
+        ret.Seek(0);
+        src->Unlock();
+    }
+
+    return ret;
+}
+
+$}

+ 7 - 1
Source/Urho3D/LuaScript/pkgs/Graphics/Model.pkg

@@ -5,11 +5,17 @@ class Model : public Resource
     // SharedPtr<Model> Clone(const String cloneName = String::EMPTY) const;
     tolua_outside Model* ModelClone @ Clone(const String cloneName = String::EMPTY) const;
 
+    void SetBoundingBox(const BoundingBox& box);
+    void SetNumGeometries(unsigned num);
+    bool SetNumGeometryLodLevels(unsigned index, unsigned num);
+    bool SetGeometry(unsigned index, unsigned lodLevel, Geometry* geometry);
+    bool SetGeometryCenter(unsigned index, const Vector3& center);
     const BoundingBox& GetBoundingBox() const;
     Skeleton& GetSkeleton();
     unsigned GetNumGeometries() const;
     unsigned GetNumGeometryLodLevels(unsigned index) const;
     Geometry* GetGeometry(unsigned index, unsigned lodLevel) const;
+    const Vector3& GetGeometryCenter(unsigned index) const;
     unsigned GetNumMorphs() const;
     const ModelMorph* GetMorph(const String name) const;
     const ModelMorph* GetMorph(StringHash nameHash) const;
@@ -17,7 +23,7 @@ class Model : public Resource
     unsigned GetMorphRangeStart(unsigned bufferIndex) const;
     unsigned GetMorphRangeCount(unsigned bufferIndex) const;
 
-    tolua_readonly tolua_property__get_set BoundingBox& boundingBox;
+    tolua_property__get_set BoundingBox& boundingBox;
     tolua_readonly tolua_property__get_set Skeleton skeleton;
     tolua_readonly tolua_property__get_set unsigned numGeometries;
     tolua_readonly tolua_property__get_set unsigned numMorphs;

+ 72 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/VertexBuffer.pkg

@@ -0,0 +1,72 @@
+$#include "Graphics/VertexBuffer.h"
+
+class VertexBuffer : public Object
+{
+    VertexBuffer();
+    ~VertexBuffer();
+
+    void SetShadowed(bool enable);
+    bool SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic = false);
+    tolua_outside bool VertexBufferSetData @ SetData(VectorBuffer& data);
+    tolua_outside bool VertexBufferSetDataRange @ SetDataRange(VectorBuffer& data, unsigned start, unsigned count, bool discard = false);
+    tolua_outside VectorBuffer VertexBufferGetData @ GetData();
+
+    bool IsShadowed() const;
+    bool IsDynamic() const;
+    unsigned GetVertexCount() const;
+    unsigned GetVertexSize() const;
+    unsigned GetElementMask() const;
+
+    tolua_property__is_set bool shadowed;
+    tolua_readonly tolua_property__is_set bool dynamic;
+    tolua_readonly tolua_property__get_set unsigned vertexCount;
+    tolua_readonly tolua_property__get_set unsigned vertexSize;
+    tolua_readonly tolua_property__get_set unsigned elementMask;
+};
+
+${
+#define TOLUA_DISABLE_tolua_GraphicsLuaAPI_VertexBuffer_new00
+static int tolua_GraphicsLuaAPI_VertexBuffer_new00(lua_State* tolua_S)
+{
+    return ToluaNewObject<VertexBuffer>(tolua_S);
+}
+
+#define TOLUA_DISABLE_tolua_GraphicsLuaAPI_VertexBuffer_new00_local
+static int tolua_GraphicsLuaAPI_VertexBuffer_new00_local(lua_State* tolua_S)
+{
+    return ToluaNewObjectGC<VertexBuffer>(tolua_S);
+}
+
+static bool VertexBufferSetData(VertexBuffer* dest, VectorBuffer& src)
+{
+    // Make sure there is enough data
+    if (dest->GetVertexCount() && src.GetSize() >= dest->GetVertexCount() * dest->GetVertexSize())
+        return dest->SetData(&src.GetBuffer()[0]);
+    else
+        return false;
+}
+
+static bool VertexBufferSetDataRange(VertexBuffer* dest, VectorBuffer& src, unsigned start, unsigned count, bool discard)
+{
+    // Make sure there is enough data
+    if (dest->GetVertexCount() && src.GetSize() >= count * dest->GetVertexSize())
+        return dest->SetDataRange(&src.GetBuffer()[0], start, count, discard);
+    else
+        return false;
+}
+
+static VectorBuffer VertexBufferGetData(VertexBuffer* src)
+{
+    VectorBuffer ret;
+    void* data = src->Lock(0, src->GetVertexCount(), false);
+
+    if (data)
+    {
+        ret.Write(data, src->GetVertexCount() * src->GetVertexSize());
+        ret.Seek(0);
+        src->Unlock();
+    }
+
+    return ret;
+}
+$}

+ 3 - 0
Source/Urho3D/LuaScript/pkgs/GraphicsLuaAPI.pkg

@@ -12,6 +12,9 @@ $pfile "Graphics/DecalSet.pkg"
 $pfile "Graphics/Graphics.pkg"
 $pfile "Graphics/Light.pkg"
 $pfile "Graphics/Material.pkg"
+$pfile "Graphics/VertexBuffer.pkg"
+$pfile "Graphics/IndexBuffer.pkg"
+$pfile "Graphics/Geometry.pkg"
 $pfile "Graphics/Model.pkg"
 $pfile "Graphics/Octree.pkg"
 $pfile "Graphics/OctreeQuery.pkg"

+ 150 - 8
Source/Urho3D/Script/GraphicsAPI.cpp

@@ -29,7 +29,9 @@
 #include "../Graphics/CustomGeometry.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DecalSet.h"
+#include "../Graphics/Geometry.h"
 #include "../Graphics/Graphics.h"
+#include "../Graphics/IndexBuffer.h"
 #include "../Graphics/Light.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Octree.h"
@@ -47,6 +49,7 @@
 #include "../Graphics/Texture3D.h"
 #include "../Graphics/TextureCube.h"
 #include "../Graphics/Skybox.h"
+#include "../Graphics/VertexBuffer.h"
 #include "../Graphics/Zone.h"
 
 #ifdef _MSC_VER
@@ -574,6 +577,145 @@ static const TechniqueEntry& MaterialGetTechniqueEntry(unsigned index, Material*
     return ptr->GetTechniqueEntry(index);
 }
 
+static bool VertexBufferSetData(VectorBuffer& src, VertexBuffer* dest)
+{
+    // Make sure there is enough data
+    if (dest->GetVertexCount() && src.GetSize() >= dest->GetVertexCount() * dest->GetVertexSize())
+        return dest->SetData(&src.GetBuffer()[0]);
+    else
+        return false;
+}
+
+static bool VertexBufferSetDataRange(VectorBuffer& src, unsigned start, unsigned count, bool discard, VertexBuffer* dest)
+{
+    // Make sure there is enough data
+    if (dest->GetVertexCount() && src.GetSize() >= count * dest->GetVertexSize())
+        return dest->SetDataRange(&src.GetBuffer()[0], start, count, discard);
+    else
+        return false;
+}
+
+static VectorBuffer VertexBufferGetData(VertexBuffer* src)
+{
+    VectorBuffer ret;
+    void* data = src->Lock(0, src->GetVertexCount(), false);
+
+    if (data)
+    {
+        ret.Write(data, src->GetVertexCount() * src->GetVertexSize());
+        ret.Seek(0);
+        src->Unlock();
+    }
+
+    return ret;
+}
+
+static bool IndexBufferSetData(VectorBuffer& src, IndexBuffer* dest)
+{
+    // Make sure there is enough data
+    if (dest->GetIndexCount() && src.GetSize() >= dest->GetIndexCount() * dest->GetIndexSize())
+        return dest->SetData(&src.GetBuffer()[0]);
+    else
+        return false;
+}
+
+static bool IndexBufferSetDataRange(VectorBuffer& src, unsigned start, unsigned count, bool discard, IndexBuffer* dest)
+{
+    // Make sure there is enough data
+    if (dest->GetIndexCount() && src.GetSize() >= count * dest->GetIndexSize())
+        return dest->SetDataRange(&src.GetBuffer()[0], start, count, discard);
+    else
+        return false;
+}
+
+static VectorBuffer IndexBufferGetData(IndexBuffer* src)
+{
+    VectorBuffer ret;
+    void* data = src->Lock(0, src->GetIndexCount(), false);
+
+    if (data)
+    {
+        ret.Write(data, src->GetIndexCount() * src->GetIndexSize());
+        ret.Seek(0);
+        src->Unlock();
+    }
+
+    return ret;
+}
+
+static void RegisterBuffers(asIScriptEngine* engine)
+{
+    engine->RegisterGlobalProperty("const uint MASK_NONE", (void*)&MASK_NONE);
+    engine->RegisterGlobalProperty("const uint MASK_POSITION", (void*)&MASK_POSITION);
+    engine->RegisterGlobalProperty("const uint MASK_NORMAL", (void*)&MASK_NORMAL);
+    engine->RegisterGlobalProperty("const uint MASK_COLOR", (void*)&MASK_COLOR);
+    engine->RegisterGlobalProperty("const uint MASK_TEXCOORD1", (void*)&MASK_TEXCOORD1);
+    engine->RegisterGlobalProperty("const uint MASK_TEXCOORD2", (void*)&MASK_TEXCOORD2);
+    engine->RegisterGlobalProperty("const uint MASK_CUBETEXCOORD1", (void*)&MASK_CUBETEXCOORD1);
+    engine->RegisterGlobalProperty("const uint MASK_CUBETEXCOORD2", (void*)&MASK_CUBETEXCOORD2);
+    engine->RegisterGlobalProperty("const uint MASK_TANGENT", (void*)&MASK_TANGENT);
+    engine->RegisterGlobalProperty("const uint MASK_BLENDWEIGHTS", (void*)&MASK_BLENDWEIGHTS);
+    engine->RegisterGlobalProperty("const uint MASK_BLENDINDICES", (void*)&MASK_BLENDINDICES);
+    engine->RegisterGlobalProperty("const uint MASK_INSTANCEMATRIX1", (void*)&MASK_INSTANCEMATRIX1);
+    engine->RegisterGlobalProperty("const uint MASK_INSTANCEMATRIX2", (void*)&MASK_INSTANCEMATRIX2);
+    engine->RegisterGlobalProperty("const uint MASK_INSTANCEMATRIX3", (void*)&MASK_INSTANCEMATRIX3);
+    engine->RegisterGlobalProperty("const uint MASK_DEFAULT", (void*)&MASK_DEFAULT);
+
+    engine->RegisterEnum("PrimitiveType");
+    engine->RegisterEnumValue("PrimitiveType", "TRIANGLE_LIST", TRIANGLE_LIST);
+    engine->RegisterEnumValue("PrimitiveType", "LINE_LIST", LINE_LIST);
+    engine->RegisterEnumValue("PrimitiveType", "POINT_LIST", POINT_LIST);
+    engine->RegisterEnumValue("PrimitiveType", "TRIANGLE_STRIP", TRIANGLE_STRIP);
+    engine->RegisterEnumValue("PrimitiveType", "LINE_STRIP", LINE_STRIP);
+    engine->RegisterEnumValue("PrimitiveType", "TRIANGLE_FAN", TRIANGLE_FAN);
+
+    RegisterObject<VertexBuffer>(engine, "VertexBuffer");
+    RegisterObjectConstructor<VertexBuffer>(engine, "VertexBuffer");
+    engine->RegisterObjectMethod("VertexBuffer", "void SetSize(uint, uint, bool dynamic = false)", asMETHOD(VertexBuffer, SetSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("VertexBuffer", "bool SetData(VectorBuffer&)", asFUNCTION(VertexBufferSetData), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("VertexBuffer", "bool SetDataRange(VectorBuffer&, uint, uint, bool discard = false)", asFUNCTION(VertexBufferSetDataRange), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("VertexBuffer", "VectorBuffer GetData()", asFUNCTION(VertexBufferGetData), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("VertexBuffer", "void set_shadowed(bool)", asMETHOD(VertexBuffer, SetShadowed), asCALL_THISCALL);
+    engine->RegisterObjectMethod("VertexBuffer", "bool get_shadowed() const", asMETHOD(VertexBuffer, IsShadowed), asCALL_THISCALL);
+    engine->RegisterObjectMethod("VertexBuffer", "bool get_dynamic() const", asMETHOD(VertexBuffer, IsDynamic), asCALL_THISCALL);
+    engine->RegisterObjectMethod("VertexBuffer", "uint get_vertexCount() const", asMETHOD(VertexBuffer, GetVertexCount), asCALL_THISCALL);
+    engine->RegisterObjectMethod("VertexBuffer", "uint get_vertexSize() const", asMETHODPR(VertexBuffer, GetVertexSize, () const, unsigned), asCALL_THISCALL);
+    engine->RegisterObjectMethod("VertexBuffer", "uint get_elementMask() const", asMETHOD(VertexBuffer, GetElementMask), asCALL_THISCALL);
+
+    RegisterObject<IndexBuffer>(engine, "IndexBuffer");
+    RegisterObjectConstructor<IndexBuffer>(engine, "IndexBuffer");
+    engine->RegisterObjectMethod("IndexBuffer", "void SetSize(uint, bool, bool dynamic = false)", asMETHOD(IndexBuffer, SetSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IndexBuffer", "bool SetData(VectorBuffer&)", asFUNCTION(IndexBufferSetData), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("IndexBuffer", "bool SetDataRange(VectorBuffer&, uint, uint, bool discard = false)", asFUNCTION(IndexBufferSetDataRange), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("IndexBuffer", "VectorBuffer GetData()", asFUNCTION(IndexBufferGetData), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("IndexBuffer", "void set_shadowed(bool)", asMETHOD(IndexBuffer, SetShadowed), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IndexBuffer", "bool get_shadowed() const", asMETHOD(IndexBuffer, IsShadowed), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IndexBuffer", "bool get_dynamic() const", asMETHOD(IndexBuffer, IsDynamic), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IndexBuffer", "uint get_indexCount() const", asMETHOD(IndexBuffer, GetIndexCount), asCALL_THISCALL);
+    engine->RegisterObjectMethod("IndexBuffer", "uint get_indexSize() const", asMETHOD(IndexBuffer, GetIndexSize), asCALL_THISCALL);
+
+    RegisterObject<Geometry>(engine, "Geometry");
+    RegisterObjectConstructor<Geometry>(engine, "Geometry");
+    engine->RegisterObjectMethod("Geometry", "void set_numVertexBuffers(uint)", asMETHOD(Geometry, SetNumVertexBuffers), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "uint get_numVertexBuffers() const", asMETHOD(Geometry, GetNumVertexBuffers), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "bool SetVertexBuffer(uint, VertexBuffer@+, uint elementMask = MASK_DEFAULT)", asMETHOD(Geometry, SetVertexBuffer), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "void SetIndexBuffer(IndexBuffer@+)", asMETHOD(Geometry, SetIndexBuffer), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "bool SetDrawRange(PrimitiveType, uint, uint, bool getUsedVertexRange = true)", asMETHODPR(Geometry, SetDrawRange, (PrimitiveType, unsigned, unsigned, bool), bool),asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "bool SetDrawRange(PrimitiveType, uint, uint, uint, uint, bool checkIllegal = true)", asMETHODPR(Geometry, SetDrawRange, (PrimitiveType, unsigned, unsigned, unsigned, unsigned, bool), bool), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "VertexBuffer@+ get_vertexBuffers(uint) const", asMETHOD(Geometry, GetVertexBuffer), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "uint get_vertexElementMasks(uint) const", asMETHOD(Geometry, GetVertexElementMask), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "void set_indexBuffer(IndexBuffer@+)", asMETHOD(Geometry, SetIndexBuffer), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "IndexBuffer@+ get_indexBuffer() const", asMETHOD(Geometry, GetIndexBuffer), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "PrimitiveType get_primitiveType() const", asMETHOD(Geometry, GetPrimitiveType), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "uint get_indexStart() const", asMETHOD(Geometry, GetIndexStart), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "uint get_indexCount() const", asMETHOD(Geometry, GetIndexCount), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "uint get_vertexStart() const", asMETHOD(Geometry, GetVertexStart), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "uint get_vertexCount() const", asMETHOD(Geometry, GetVertexCount), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "void set_lodDistance(float)", asMETHOD(Geometry, SetLodDistance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "float get_lodDistance() const", asMETHOD(Geometry, GetLodDistance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Geometry", "bool get_empty() const", asMETHOD(Geometry, IsEmpty), asCALL_THISCALL);
+}
+
 static void RegisterMaterial(asIScriptEngine* engine)
 {
     engine->RegisterObjectType("BiasParameters", sizeof(BiasParameters), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
@@ -711,10 +853,17 @@ static void RegisterModel(asIScriptEngine* engine)
 {
     RegisterResource<Model>(engine, "Model");
     engine->RegisterObjectMethod("Model", "Model@ Clone(const String&in cloneName = String()) const", asFUNCTION(ModelClone), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Model", "bool SetGeometry(uint, uint, Geometry@+)", asMETHOD(Model, SetGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Model", "Geometry@+ GetGeometry(uint, uint) const", asMETHOD(Model, GetGeometry), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Model", "void set_boundingBox(const BoundingBox&in)", asMETHOD(Model, SetBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "const BoundingBox& get_boundingBox() const", asMETHOD(Model, GetBoundingBox), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "Skeleton@+ get_skeleton()", asMETHOD(Model, GetSkeleton), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Model", "void set_numGeometries(uint)", asMETHOD(Model, SetNumGeometries), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "uint get_numGeometries() const", asMETHOD(Model, GetNumGeometries), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Model", "bool set_numGeometryLodLevels(uint, uint)", asMETHOD(Model, SetNumGeometryLodLevels), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "uint get_numGeometryLodLevels(uint) const", asMETHOD(Model, GetNumGeometryLodLevels), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Model", "bool set_geometryCenters(uint, const Vector3&in)", asMETHOD(Model, SetGeometryCenter), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Model", "const Vector3& get_geometryCenters(uint) const", asMETHOD(Model, GetGeometryCenter), asCALL_THISCALL);
     engine->RegisterObjectMethod("Model", "uint get_numMorphs() const", asMETHOD(Model, GetNumMorphs), asCALL_THISCALL);
 }
 
@@ -1211,14 +1360,6 @@ static void RegisterParticleEmitter(asIScriptEngine* engine)
 
 static void RegisterCustomGeometry(asIScriptEngine* engine)
 {
-    engine->RegisterEnum("PrimitiveType");
-    engine->RegisterEnumValue("PrimitiveType", "TRIANGLE_LIST", TRIANGLE_LIST);
-    engine->RegisterEnumValue("PrimitiveType", "LINE_LIST", LINE_LIST);
-    engine->RegisterEnumValue("PrimitiveType", "POINT_LIST", POINT_LIST);
-    engine->RegisterEnumValue("PrimitiveType", "TRIANGLE_STRIP", TRIANGLE_STRIP);
-    engine->RegisterEnumValue("PrimitiveType", "LINE_STRIP", LINE_STRIP);
-    engine->RegisterEnumValue("PrimitiveType", "TRIANGLE_FAN", TRIANGLE_FAN);
-    
     engine->RegisterObjectType("CustomGeometryVertex", 0, asOBJ_REF);
     engine->RegisterObjectBehaviour("CustomGeometryVertex", asBEHAVE_ADDREF, "void f()", asFUNCTION(FakeAddRef), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("CustomGeometryVertex", asBEHAVE_RELEASE, "void f()", asFUNCTION(FakeReleaseRef), asCALL_CDECL_OBJLAST);
@@ -1629,6 +1770,7 @@ void RegisterGraphicsAPI(asIScriptEngine* engine)
     RegisterRenderPath(engine);
     RegisterTextures(engine);
     RegisterMaterial(engine);
+    RegisterBuffers(engine);
     RegisterModel(engine);
     RegisterAnimation(engine);
     RegisterDrawable(engine);

+ 329 - 0
bin/Data/Scripts/34_DynamicGeometry.as

@@ -0,0 +1,329 @@
+// Dynamic geometry example.
+// This sample demonstrates:
+//     - Cloning a Model resource
+//     - Modifying the vertex buffer data of the cloned models at runtime to efficiently animate them
+//     - Creating a Model resource and its buffer data from scratch
+
+#include "Scripts/Utilities/Sample.as"
+
+bool animate = true;
+float animTime = 0.0;
+VectorBuffer originalVertexData;
+Array<VertexBuffer@> animatingBuffers;
+Array<Vector3> originalVertices;
+Array<uint> vertexDuplicates;
+
+void Start()
+{
+    // Execute the common startup for samples
+    SampleStart();
+
+    // Create the scene content
+    CreateScene();
+
+    // Create the UI content
+    CreateInstructions();
+
+    // Setup the viewport for displaying the scene
+    SetupViewport();
+
+    // Hook up to the frame update events
+    SubscribeToEvents();
+}
+
+void CreateScene()
+{
+    scene_ = Scene();
+    
+    // Create the Octree component to the scene so that drawable objects can be rendered. Use default volume
+    // (-1000, -1000, -1000) to (1000, 1000, 1000)
+    scene_.CreateComponent("Octree");
+
+    // Create a Zone for ambient light & fog control
+    Node@ zoneNode = scene_.CreateChild("Zone");
+    Zone@ zone = zoneNode.CreateComponent("Zone");
+    zone.boundingBox = BoundingBox(-1000.0, 1000.0);
+    zone.fogColor = Color(0.2, 0.2, 0.2);
+    zone.fogStart = 200.0;
+    zone.fogEnd = 300.0;
+    
+    // Create a directional light
+    Node@ lightNode = scene_.CreateChild("DirectionalLight");
+    lightNode.direction = Vector3(-0.6, -1.0, -0.8); // The direction vector does not need to be normalized
+    Light@ light = lightNode.CreateComponent("Light");
+    light.lightType = LIGHT_DIRECTIONAL;
+    light.color = Color(0.4, 1.0, 0.4);
+    light.specularIntensity = 1.5;
+    
+    // Get the original model and its unmodified vertices, which are used as source data for the animation
+    Model@ originalModel = cache.GetResource("Model", "Models/Box.mdl");
+    if (originalModel is null)
+    {
+        Print("Model not found, cannot initialize example scene");
+        return;
+    }
+    // Get the vertex buffer from the first geometry's first LOD level
+    VertexBuffer@ buffer = originalModel.GetGeometry(0, 0).vertexBuffers[0];
+    originalVertexData = buffer.GetData();
+    uint numVertices = buffer.vertexCount;
+    uint vertexSize = buffer.vertexSize;
+    // Copy the original vertex positions
+    for (uint i = 0; i < numVertices; ++i)
+    {
+        originalVertexData.Seek(i * vertexSize);
+        originalVertices.Push(originalVertexData.ReadVector3());
+    }
+
+    // Detect duplicate vertices to allow seamless animation
+    vertexDuplicates.Resize(originalVertices.length);
+    for (uint i = 0; i < originalVertices.length; ++i)
+    {
+        vertexDuplicates[i] = i; // Assume not a duplicate
+        for (uint j = 0; j < i; ++j)
+        {
+            if (originalVertices[i].Equals(originalVertices[j]))
+            {
+                vertexDuplicates[i] = j;
+                break;
+            }
+        }
+    }
+
+    // Create StaticModels in the scene. Clone the model for each so that we can modify the vertex data individually
+    for (int y = -1; y <= 1; ++y)
+    {
+        for (int x = -1; x <= 1; ++x)
+        {
+            Node@ node = scene_.CreateChild("Object");
+            node.position = Vector3(x * 2.0, 0.0, y * 2.0);
+            StaticModel@ object = node.CreateComponent("StaticModel");
+            Model@ cloneModel = originalModel.Clone();
+            object.model = cloneModel;
+            // Store the cloned vertex buffer that we will modify when animating
+            animatingBuffers.Push(cloneModel.GetGeometry(0, 0).vertexBuffers[0]);
+        }
+    }
+    
+    // Finally create one model (pyramid shape) and a StaticModel to display it from scratch
+    // Note: there are duplicated vertices to enable face normals. We will calculate normals programmatically
+    {
+        const uint numVertices = 18;
+        
+        float[] vertexData = {
+            // Position          Normal
+            0.0, 0.5, 0.0,       0.0, 0.0, 0.0,
+            0.5, -0.5, 0.5,      0.0, 0.0, 0.0,
+            0.5, -0.5, -0.5,     0.0, 0.0, 0.0,
+
+            0.0, 0.5, 0.0,       0.0, 0.0, 0.0,
+            -0.5, -0.5, 0.5,     0.0, 0.0, 0.0,
+            0.5, -0.5, 0.5,      0.0, 0.0, 0.0,
+
+            0.0, 0.5, 0.0,       0.0, 0.0, 0.0,
+            -0.5, -0.5, -0.5,    0.0, 0.0, 0.0,
+            -0.5, -0.5, 0.5,     0.0, 0.0, 0.0,
+
+            0.0, 0.5, 0.0,       0.0, 0.0, 0.0,
+            0.5, -0.5, -0.5,     0.0, 0.0, 0.0,
+            -0.5, -0.5, -0.5,    0.0, 0.0, 0.0,
+
+            0.5, -0.5, -0.5,     0.0, 0.0, 0.0,
+            0.5, -0.5, 0.5,      0.0, 0.0, 0.0,
+            -0.5, -0.5, 0.5,     0.0, 0.0, 0.0,
+
+            0.5, -0.5, -0.5,     0.0, 0.0, 0.0,
+            -0.5, -0.5, 0.5,     0.0, 0.0, 0.0,
+            -0.5, -0.5, -0.5,    0.0, 0.0, 0.0
+        };
+        
+        const uint16[] indexData = {
+            0, 1, 2,
+            3, 4, 5,
+            6, 7, 8,
+            9, 10, 11,
+            12, 13, 14,
+            15, 16, 17
+        };
+        
+        // Calculate face normals now
+        for (uint i = 0; i < numVertices; i += 3)
+        {
+            Vector3 v1(vertexData[6 * i], vertexData[6 * i + 1], vertexData[6 * i + 2]);
+            Vector3 v2(vertexData[6 * i + 6], vertexData[6 * i + 7], vertexData[6 * i + 8]);
+            Vector3 v3(vertexData[6 * i + 12], vertexData[6 * i + 13], vertexData[6 * i + 14]);
+
+            Vector3 edge1 = v1 - v2;
+            Vector3 edge2 = v1 - v3;
+            Vector3 normal = edge1.CrossProduct(edge2).Normalized();
+            vertexData[6 * i + 3] = vertexData[6 * i + 9] = vertexData[6 * i + 15] = normal.x;
+            vertexData[6 * i + 4] = vertexData[6 * i + 10] = vertexData[6 * i + 16] = normal.y;
+            vertexData[6 * i + 5] = vertexData[6 * i + 11] = vertexData[6 * i + 17] = normal.z;
+        }
+
+        Model@ fromScratchModel = Model();
+        VertexBuffer@ vb = VertexBuffer();
+        IndexBuffer@ ib = IndexBuffer();
+        Geometry@ geom = Geometry();
+
+        // Shadowed buffer needed for raycasts to work, and so that data can be automatically restored on device loss
+        vb.shadowed = true;
+        vb.SetSize(numVertices, MASK_POSITION|MASK_NORMAL);
+        VectorBuffer temp;
+        for (uint i = 0; i < numVertices * 6; ++i)
+            temp.WriteFloat(vertexData[i]);
+        vb.SetData(temp);
+
+        ib.shadowed = true;
+        ib.SetSize(numVertices, false);
+        temp.Clear();
+        for (uint i = 0; i < numVertices; ++i)
+            temp.WriteUShort(indexData[i]);
+        ib.SetData(temp);
+
+        geom.SetVertexBuffer(0, vb);
+        geom.SetIndexBuffer(ib);
+        geom.SetDrawRange(TRIANGLE_LIST, 0, numVertices);
+
+        fromScratchModel.numGeometries = 1;
+        fromScratchModel.SetGeometry(0, 0, geom);
+        fromScratchModel.boundingBox = BoundingBox(Vector3(-0.5, -0.5, -0.5), Vector3(0.5, 0.5, 0.5));
+
+        Node@ node = scene_.CreateChild("FromScratchObject");
+        node.position = Vector3(0.0, 3.0, 0.0);
+        StaticModel@ object = node.CreateComponent("StaticModel");
+        object.model = fromScratchModel;
+    }
+
+    // Create the camera
+    cameraNode = Node("Camera");
+    cameraNode.position = Vector3(0.0, 2.0, -20.0);
+    Camera@ camera = cameraNode.CreateComponent("Camera");
+    camera.farClip = 300.0f;
+}
+
+void CreateInstructions()
+{
+    // Construct new Text object, set string to display and font to use
+    Text@ instructionText = ui.root.CreateChild("Text");
+    instructionText.text =
+        "Use WASD keys and mouse/touch to move\n"
+        "Space to toggle animation";
+    instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
+    // The text has multiple rows. Center them in relation to each other
+    instructionText.textAlignment = HA_CENTER;
+
+    // Position the text relative to the screen center
+    instructionText.horizontalAlignment = HA_CENTER;
+    instructionText.verticalAlignment = VA_CENTER;
+    instructionText.SetPosition(0, ui.root.height / 4);
+}
+
+void SetupViewport()
+{
+    // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen
+    Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
+    renderer.viewports[0] = viewport;
+}
+
+void SubscribeToEvents()
+{
+    // Subscribe HandleUpdate() function for processing update events
+    SubscribeToEvent("Update", "HandleUpdate");
+}
+
+void MoveCamera(float timeStep)
+{
+    // Do not move if the UI has a focused element (the console)
+    if (ui.focusElement !is null)
+        return;
+
+    // Movement speed as world units per second
+    const float MOVE_SPEED = 20.0f;
+    // Mouse sensitivity as degrees per pixel
+    const float MOUSE_SENSITIVITY = 0.1f;
+
+    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
+    IntVector2 mouseMove = input.mouseMove;
+    yaw += MOUSE_SENSITIVITY * mouseMove.x;
+    pitch += MOUSE_SENSITIVITY * mouseMove.y;
+    pitch = Clamp(pitch, -90.0, 90.0f);
+
+    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
+    cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
+
+    // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
+    if (input.keyDown['W'])
+        cameraNode.Translate(Vector3(0.0, 0.0, 1.0f) * MOVE_SPEED * timeStep);
+    if (input.keyDown['S'])
+        cameraNode.Translate(Vector3(0.0, 0.0, -1.0f) * MOVE_SPEED * timeStep);
+    if (input.keyDown['A'])
+        cameraNode.Translate(Vector3(-1.0, 0.0, 0.0f) * MOVE_SPEED * timeStep);
+    if (input.keyDown['D'])
+        cameraNode.Translate(Vector3(1.0, 0.0, 0.0f) * MOVE_SPEED * timeStep);
+}
+
+void AnimateObjects(float timeStep)
+{
+    animTime += timeStep * 100.0;
+    
+    // Repeat for each of the cloned vertex buffers
+    for (uint i = 0; i < animatingBuffers.length; ++i)
+    {
+        float startPhase = animTime + i * 30.0;
+        VertexBuffer@ buffer = animatingBuffers[i];
+        
+        // Need to prepare a VectorBuffer with all data (positions, normals, uvs...)
+        VectorBuffer newData;
+        uint numVertices = buffer.vertexCount;
+        uint vertexSize = buffer.vertexSize;
+        for (uint j = 0; j < numVertices; ++j)
+        {
+            // If there are duplicate vertices, animate them in phase of the original
+            float phase = startPhase + vertexDuplicates[j] * 10.0f;
+            Vector3 src = originalVertices[j];
+            Vector3 dest;
+            dest.x = src.x * (1.0 + 0.1 * Sin(phase));
+            dest.y = src.y * (1.0 + 0.1 * Sin(phase + 60.0));
+            dest.z = src.z * (1.0 + 0.1 * Sin(phase + 120.0));
+            
+            // Write position
+            newData.WriteVector3(dest);
+            // Copy other vertex elements
+            originalVertexData.Seek(j * vertexSize + 12); // Seek past the vertex position
+            for (uint k = 12; k < vertexSize; k += 4)
+                newData.WriteFloat(originalVertexData.ReadFloat());
+        }
+        
+        buffer.SetData(newData);
+    }
+}
+
+void HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+    // Take the frame time step, which is stored as a float
+    float timeStep = eventData["TimeStep"].GetFloat();
+
+    // Toggle animation with space
+    if (input.keyPress[KEY_SPACE])
+        animate = !animate;
+
+    // Move the camera, scale movement with time step
+    MoveCamera(timeStep);
+
+    // Animate objects' vertex data if enabled
+    if (animate)
+        AnimateObjects(timeStep);
+}
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>"
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />"
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>"
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">"
+        "        <element type=\"Text\">"
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />"
+        "            <attribute name=\"Text\" value=\"SPACE\" />"
+        "        </element>"
+        "    </add>"
+        "</patch>";