From a7b0820028846ebfe06ef1546c2f4caf3fb88ec3 Mon Sep 17 00:00:00 2001 From: Petr Ohlidal Date: Mon, 26 Feb 2024 07:31:48 +0100 Subject: [PATCH] :angel:Script: :sparkles: OGRE Mesh/Material bindings + example This introduces 'example_ogre_terrnBatcher.as' which shows how Mesh/Material API works and will evolve into a production tool. this will conclude #3030. THE INTENDED USE of the future completed TERRN BATCHER: 1. You start with a list of OGRE SceneNodes - see green box. 2. you pick those you want to batch together - see orange box. 3. [WIP] The tool generates one mesh which contains all those picked (keeping position, scale and rotation) and cover them in single material containing all the textures. 4. [TBD] The tool remembers what batches were done as a 'schedule', and allows saving/loading the schedule as file. Modders can share and improve the schedules. 5. [TBD] Modders can launch the tool directly from .terrn2 and auto-execute a schedule on map load. 6. Profit --- .../scripts/example_ogre_terrnBatcher.as | 504 ++++++++++++++++++ .../scripting/bindings/OgreAngelscript.cpp | 197 ++++++- 2 files changed, 698 insertions(+), 3 deletions(-) create mode 100644 resources/scripts/example_ogre_terrnBatcher.as diff --git a/resources/scripts/example_ogre_terrnBatcher.as b/resources/scripts/example_ogre_terrnBatcher.as new file mode 100644 index 0000000000..20030ef51e --- /dev/null +++ b/resources/scripts/example_ogre_terrnBatcher.as @@ -0,0 +1,504 @@ +/// \title TerrnBatcher - tool to merge static meshes & materials for FPS boost +/// \brief Prototype, displays UI, implements selection but not the batching yet. +/// +/// DEV NOTES: +/// -Started as copypaste of 'example_ogre_inspector.as' because I needed the scene traversal logic+UI +/// -Added crude material info display --> to be polished and merged back to Inspector. +/// -Implemented a 'Basket' (selection) mechanism containing 'Picks' (scene nodes) +/// ------------------------------------------------------------------------------------- + +// #===================================================================== +// # #### WARNING - DANGEROUS API #### +// # OGRE objects don't use reference counting +// # - __ N E V E R __ keep pointers across `frameStep()` invocations! +// # Prefer always looking up the resource from OGRE - slower but safer. +// #===================================================================== + +#include "imgui_utils.as" + +TerrnBatcherUI tbUI; + +void main() +{ + game.log("TERRN BATCHER [ALPHA] - shows how you can traverse scene and merge meshes/textures together for better FPS"); +} + +void frameStep(float dt) +{ + ImGui::Begin("TERRN BATCHER [ALPHA]", /*open:*/true, ImGuiWindowFlags_AlwaysAutoResize); + tbUI.draw(); + ImGui::End(); +} + +class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare Schedule for the batching. +{ + // dictionary> tbuiSelection; ~ Used by the Pick buttons. + // * Keeping scenenode pointers is dangerous - they aren't refcounted. + // * We keep a dictionary "scenenode name" -> child node indices + dictionary tbuiSelection; + + //#region Draw UI - main + void draw() + { + ImGui::Text("shows how you can traverse scene and merge meshes/textures together for better FPS"); + ImGui::Text("Note that all element names are just simple strings, even if they contain '/'."); + + TerrainClass@ terrain = game.getTerrain(); + if (@terrain == null) + { + ImGui::TextDisabled("ERROR No terrain loaded"); + return; + } + + Ogre::Root@ root = Ogre::Root::getSingleton(); + if (@root == null) + { + ImGui::TextDisabled("ERROR Cannot retrieve OGRE `Root` object"); + return; + } + + + Ogre::SceneManager@ sceneMgr = this.findSceneManager(root, "main_scene_manager"); + if (@sceneMgr == null) + { + ImGui::TextDisabled("ERROR Cannot retrieve scene manager"); + return; + } + + Ogre::SceneNode@ rootNode = sceneMgr.getRootSceneNode(); + if (@rootNode == null) + { + ImGui::TextDisabled("ERROR Cannot retrieve root scene node"); + return; + } + + Ogre::SceneNode@ terrnNode = this.findChildSceneNode(rootNode, "Terrain: " + terrain.getTerrainName()); + if (@terrnNode==null) + { + ImGui::TextDisabled("ERROR Cannot retrieve terrain's grouping scene node"); + return; + } + + ImGui::Separator(); + this.drawSelectionBasket(terrnNode); + ImGui::Separator(); + + this.drawTreeNodeOgreSceneNodeRecursive(terrnNode); + } + //#endregion Draw UI - main + + //#region Draw UI - schedule + void drawSelectionBasket(Ogre::SceneNode@ terrnNode) + { + array tobjNodes = tbuiSelection.getKeys(); + ImGui::TextDisabled("Selection:"); + + if (tobjNodes.length() == 0) + { + ImGui::Text("nothing selected. Use 'pick' buttons in tree below."); + return; + } + + // totals + dictionary materialSet; // material name => int numHits (-1 means not trivial - not supported) + dictionary uniqueSubmeshSet; // dictionary { mesh name + ':' + submesh index => uint numVerts } + dictionary uniqueSubmeshSetHits; // dictionary { mesh name + ':' + submesh index => int numHits } + + + for (uint i=0; i "); + + Ogre::SceneNode@ tobjNode = this.findChildSceneNode(terrnNode,tobjNodes[i]); + if (@tobjNode == null) + { + ImGui::SameLine(); + ImGui::Text("ERROR - not found in scene graph!"); + } + else + { + ImGui::SameLine(); + array@ nodeIndices = cast>(tbuiSelection[tobjNodes[i]]); + if (@nodeIndices == null) + { + ImGui::Text("ERROR cast failed"); + } + else + { + ImGui::Text(nodeIndices.length() + " nodes selected"); + + for (uint j=0; j materialKeys = materialSet.getKeys(); + TerrainClass@ terrain = game.getTerrain(); + for (uint i = 0; i " + tex.getName() + " ("+tex.getWidth()+" x " + tex.getHeight() + ") ["+matNumHits+" hits]"); + } + else + { + ImGui::SameLine(); + ImGui::Text("TBD: non trivial materials are not supported yet"); + } + } + + // now traverse set of unique submeshes + ImGui::TextDisabled("(by submesh):"); + array submeshKeys = uniqueSubmeshSet.getKeys(); + for (uint i = 0; i "+numVerts+" verts ["+uint(uniqueSubmeshSetHits[submeshKeys[i]])+" hits]"); + } + } + + // helper for `drawSelectionBasket()` + void basketProcessSingleInput(dictionary&inout materialSet, dictionary&inout subMeshSet, dictionary&inout subMeshSetHits, Ogre::SceneNode@ tobjNode, int childIndex) + { + Ogre::ChildNodeArray@ children = tobjNode.getChildren(); + array@ nodeIndices = cast>(tbuiSelection[tobjNode.getName()]); + if (nodeIndices[childIndex] >= children.length()) + { + ImGui::Text("ERROR: '"+tobjNode.getName()+"' has only "+children.length()+"children, requested index"+childIndex); + return; + } + + // traverse Entities/subentities and collect materials. + Ogre::SceneNode@ pickedNode = cast(children[nodeIndices[childIndex]]); + Ogre::MovableObjectArray@ movables = pickedNode.getAttachedObjects(); + // just assume trivial case - single attached entity + Ogre::Entity@ ent = cast(movables[0]); + Ogre::SubEntityArray@ subEntities = ent.getSubEntities(); + for (uint k=0; k "+index); + return; + } + if (!tbuiSelection.exists(key)) + { + tbuiSelection[key] = array(); + } + array@ arr = cast>(tbuiSelection[key]); + arr.insertLast(index); + game.log("DBG picked "+key+" --> "+index); + } + + void unPickSceneNode(string key, uint index) + { + if (!this.isSceneNodePicked(key, index)) + { + game.log("DBG unPickSceneNode(): this is not picked: "+key+" --> "+index); + return; + } + array@ arr = cast>(tbuiSelection[key]); + for (uint i=0; i "+index); + return; + } + } + } + + bool isSceneNodePicked(string key, uint index) + { + if (!tbuiSelection.exists(key)) { return false; } + array@ arr = cast>(tbuiSelection[key]); + for (uint i=0; i sceneMgrNames = sceneManagers.getKeys(); + for (uint i = 0; i < sceneManagers.getSize(); i++) + { + if (sceneMgrNames[i] == subject) + { + return sceneManagers[sceneMgrNames[i]]; + } + } + return null; + } + + Ogre::SceneNode@ findChildSceneNode(Ogre::SceneNode@ snode, string subject) + { + Ogre::ChildNodeArray@ children = snode.getChildren(); + for (uint i = 0; i < children.length(); i++) + { + Ogre::SceneNode@ child = cast(children[i]); + if (@child != null && child.getName() == subject) + { + return child; + } + } + return null; + } + + //#endregion Searching helpers + + //#region Inspector tree nodes + void drawTreeNodeOgreSceneNodeRecursive(Ogre::SceneNode@ snode, uint indexUnderParent=0) + { + // Start with all nodes folded (root node can have hundreds...) + ImGui::SetNextItemOpen(false, ImGuiCond_Once); + + Ogre::ChildNodeArray@ children = snode.getChildren(); + Ogre::MovableObjectArray@ movables = snode.getAttachedObjects(); + + // The `__getUniqueName()` is a Rigs of Rods extension (that's why double leading underscores), + // because names are optional and usually not set, and imgui tree nodes require unique IDs. + if (ImGui::TreeNode(snode.__getUniqueName())) + { + // Tree node open, draw children recursively + ImGui::TextDisabled("Ogre::Node ["+children.length()+"]"); + for (uint i = 0; i < children.length(); i++) + { + Ogre::SceneNode@ child = cast(children[i]); + if (@child != null) + { + drawTreeNodeOgreSceneNodeRecursive(child, i); + } + } + + // Draw attached movable objects + ImGui::TextDisabled("Ogre::MovableObject [" + movables.length() + "]"); + for (uint i = 0; i < movables.length(); i++) + { + drawTreeNodeOgreMovableObject(movables[i]); + } + + ImGui::TreePop(); + } + else + { + ImGui::PushID(snode.__getUniqueName()); + + // Tree node closed, draw info (context-sensitive) + ImGui::SameLine(); + if (children.length() == 0 && movables.length() == 1) + { + //the most usual case - a node with an entity. display the mesh name. + ImGui::TextDisabled("-->"); + ImGui::SameLine(); + ImGui::Text(movables[0].__getUniqueName()); + ImGui::SameLine(); + if (ImGui::SmallButton("go to")) + { + game.setPersonPosition(snode.getPosition()); + } + ImGui::SameLine(); + // +terrnBatcher - selection + + if (this.isSceneNodePicked(snode.getParentSceneNode().getName(), indexUnderParent)) + { + if(ImGui::SmallButton("-UnPick")) + { + this.unPickSceneNode(snode.getParentSceneNode().getName(), indexUnderParent); + } + } + else + { + if (ImGui::SmallButton("+Pick!")) + { + + this.pickSceneNode(snode.getParentSceneNode().getName(), indexUnderParent); + } + } + // END terrnBatcher + } + else + { + ImGui::Text("("+children.length()+" children, "+movables.length()+" movables)"); + } + + ImGui::PopID(); //snode.__getUniqueName() + } + } + + void drawTreeNodeOgreMovableObject(Ogre::MovableObject@ movable) + { + if (ImGui::TreeNode(movable.__getUniqueName())) + { + bool visible = movable.isVisible(); + if (ImGui::Checkbox("Visible", visible)) + { + movable.setVisible(visible); + } + + bool castShadows = movable.getCastShadows(); + if (ImGui::Checkbox("Cast shadows", castShadows)) + { + movable.setCastShadows(castShadows); + } + + if (movable.getMovableType() == "Entity") + { + Ogre::Entity@ entity = cast(movable); + Ogre::AnimationStateSet@ stateSet = entity.getAllAnimationStates(); + if (@stateSet != null) + { + Ogre::AnimationStateDict@ stateDict = stateSet.getAnimationStates(); + ImGui::TextDisabled("Ogre::AnimationState [" + stateDict.getSize() + "]"); + array stateNames = stateDict.getKeys(); + for (uint i = 0; i < stateDict.getSize(); i++) + { + this.drawTreeNodeOgreAnimationState(stateDict[stateNames[i]]); + } + } + + // +TerrnBatcher - subEntities + Ogre::SubEntityArray@ subEntities = entity.getSubEntities(); + ImGui::TextDisabled("Ogre::SubEntity [" + subEntities.length() + "]"); + for (uint i = 0; i < subEntities.length(); i++) + { + this.drawTreeNodeOgreSubEntity(subEntities, i); + } + // END +TerrnBatcher + } + + ImGui::TreePop(); + } + } + + void drawTreeNodeOgreAnimationState(Ogre::AnimationState@ anim) + { + ImGui::BulletText('"' + anim.getAnimationName() + '"'); + ImGui::SameLine(); + ImGui::Text("(" + formatFloat(anim.getLength(), "", 0, 2) + " sec)"); + if (anim.getEnabled()) + { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, vector2(0.f, 0.f)); + ImGui::ProgressBar(anim.getTimePosition() / anim.getLength()); + ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding + } + } + //#endregion Inspector tree nodes + + // #region Material helpers + + Ogre::TexturePtr getTrivialCaseMaterialTexture(Ogre::MaterialPtr mat) + { + if (mat.isNull()) return Ogre::TexturePtr(); // null + Ogre::TechniqueArray@ techs = mat.getTechniques(); + if (techs.length() != 1) return Ogre::TexturePtr(); // null + Ogre::PassArray@ passes = techs[0].getPasses(); + if (passes.length() != 1) return Ogre::TexturePtr(); // null + Ogre::TextureUnitStateArray@ tuss = passes[0].getTextureUnitStates(); + if (tuss.length() != 1) return Ogre::TexturePtr(); // null + return tuss[0]._getTexturePtr(); + } + + bool isMaterialTrivialCase(Ogre::MaterialPtr mat) + { + return !this.getTrivialCaseMaterialTexture(mat).isNull(); + } + + // #endregion Material helpers + + void drawTreeNodeOgreSubEntity(Ogre::SubEntityArray@ subEntities, uint idx) + { + if (subEntities.length() <= idx) return; // sanity check + + Ogre::SubEntity@ subEntity = subEntities[idx]; + Ogre::MaterialPtr mat = subEntity.getMaterial(); + if (!mat.isNull()) + { + Ogre::TechniqueArray@ techs = mat.getTechniques(); + ImGui::Text("|> Material: \"" + mat.getName() + "\" ("+techs.length()+" techniques)"); + for (uint i = 0; i < techs.length(); i++) + { + Ogre::PassArray@ passes = techs[i].getPasses(); + ImGui::Text("|> > Technique: \"" + techs[i].getName()+"\" ("+passes.length()+" passes)"); + for (uint j=0; j < passes.length(); j++) + { + Ogre::TextureUnitStateArray@ tuss = passes[j].getTextureUnitStates(); + ImGui::Text("|> > > Pass: \"" + passes[j].getName() + "\" (" + tuss.length() + " texture units)"); + for (uint k=0; k > > > Texture unit: \"" + tuss[k].getName() + "\""); + Ogre::TexturePtr tex = tuss[k]._getTexturePtr(); + if (!tex.isNull()) + { + ImGui::SameLine(); + ImGui::Text("Texture=\"" + tex.getName()+"\""); + } + } + } + } + } + else + { + ImGui::Text("no material"); + } + } +} diff --git a/source/main/scripting/bindings/OgreAngelscript.cpp b/source/main/scripting/bindings/OgreAngelscript.cpp index 60299e17cd..3b7bbb2e9b 100644 --- a/source/main/scripting/bindings/OgreAngelscript.cpp +++ b/source/main/scripting/bindings/OgreAngelscript.cpp @@ -2,7 +2,7 @@ This source file is part of Rigs of Rods Copyright 2005-2012 Pierre-Michel Ricordel Copyright 2007-2012 Thomas Fischer - Copyright 2013-2023 Petr Ohlidal + Copyright 2013-2024 Petr Ohlidal For more information, see http://www.rigsofrods.org/ @@ -515,6 +515,63 @@ static AnimationStateDict* AnimationStateSetGetAnimationStates(AnimationStateSet return new AnimationStateDict(self->getAnimationStates()); } +/***MATERIAL***/ +typedef CReadonlyScriptArrayView TechniqueArray; + +static TechniqueArray* MaterialPtrGetTechniques(const MaterialPtr& self) +{ + return new TechniqueArray(self->getTechniques()); +} + +static void MaterialPtrDefaultConstructor(MaterialPtr* self) +{ + new (self) MaterialPtr(); +} + +static void MaterialPtrCopyConstructor(const MaterialPtr& other, MaterialPtr* self) +{ + new (self) MaterialPtr(other); +} + +static void MaterialPtrDestructor(MaterialPtr* self) +{ + (self)->~MaterialPtr(); +} + +static void MaterialPtrAssignOperator(const MaterialPtr& other, MaterialPtr* self) +{ + (self)->operator=(other); +} + +static bool MaterialPtrIsNull(MaterialPtr* self) +{ + return !(self)->operator bool(); +} + +/***TECHNIQUE***/ +typedef CReadonlyScriptArrayView PassArray; + +static PassArray* TechniqueGetPasses(Technique* self) +{ + return new PassArray(self->getPasses()); +} + +/***PASS***/ +typedef CReadonlyScriptArrayView TextureUnitStateArray; + +static TextureUnitStateArray* PassGetTextureUnitStates(Pass* self) +{ + return new TextureUnitStateArray(self->getTextureUnitStates()); +} + +/***SUBENTITY***/ +typedef CReadonlyScriptArrayView SubEntityArray; + +static SubEntityArray* EntityGetSubEntities(Entity* self) +{ + return new SubEntityArray(self->getSubEntities()); +} + // forward declarations, defined below void registerOgreVector3(AngelScript::asIScriptEngine* engine); void registerOgreVector2(AngelScript::asIScriptEngine* engine); @@ -527,6 +584,7 @@ void registerOgreBox(AngelScript::asIScriptEngine* engine); void registerOgreMovableObject(AngelScript::asIScriptEngine* engine); void registerOgreEntity(AngelScript::asIScriptEngine* engine); +void registerOgreSubEntity(AngelScript::asIScriptEngine* engine); void registerOgreNode(AngelScript::asIScriptEngine* engine); void registerOgreSceneNode(AngelScript::asIScriptEngine* engine); void registerOgreSceneManager(AngelScript::asIScriptEngine* engine); @@ -542,6 +600,11 @@ void registerOgreImage(AngelScript::asIScriptEngine* engine); void registerOgreMeshManager(AngelScript::asIScriptEngine* engine); void registerOgreMesh(AngelScript::asIScriptEngine* engine); void registerOgreSubMesh(AngelScript::asIScriptEngine* engine); +void registerOgreMaterialManager(AngelScript::asIScriptEngine* engine); +void registerOgreMaterial(AngelScript::asIScriptEngine* engine); +void registerOgreTechnique(AngelScript::asIScriptEngine* engine); +void registerOgrePass(AngelScript::asIScriptEngine* engine); +void registerOgreTextureUnitState(AngelScript::asIScriptEngine* engine); // main registration method void RoR::RegisterOgreObjects(AngelScript::asIScriptEngine* engine) @@ -588,6 +651,9 @@ void RoR::RegisterOgreObjects(AngelScript::asIScriptEngine* engine) r = engine->RegisterObjectType("Entity", sizeof(Entity), asOBJ_REF | asOBJ_NOCOUNT); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectType("SubEntity", sizeof(Entity), asOBJ_REF | asOBJ_NOCOUNT); + ROR_ASSERT(r >= 0); + r = engine->RegisterObjectType("Node", sizeof(Node), asOBJ_REF | asOBJ_NOCOUNT); ROR_ASSERT(r >= 0); @@ -634,6 +700,21 @@ void RoR::RegisterOgreObjects(AngelScript::asIScriptEngine* engine) r = engine->RegisterObjectType("MeshManager", sizeof(TextureManager), asOBJ_REF | asOBJ_NOCOUNT); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectType("MaterialManager", sizeof(MaterialManager), asOBJ_REF | asOBJ_NOCOUNT); + ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectType("MaterialPtr", sizeof(MeshPtr), asOBJ_VALUE | asGetTypeTraits()); + ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectType("Technique", sizeof(SubMesh), asOBJ_REF | asOBJ_NOCOUNT); + ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectType("Pass", sizeof(SubMesh), asOBJ_REF | asOBJ_NOCOUNT); + ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectType("TextureUnitState", sizeof(SubMesh), asOBJ_REF | asOBJ_NOCOUNT); + ROR_ASSERT(r >= 0); + // dictionary/array view types, also under namespace `Ogre` SceneManagerInstanceDict::RegisterReadonlyScriptDictView(engine, "SceneManagerInstanceDict", "SceneManager"); @@ -641,6 +722,10 @@ void RoR::RegisterOgreObjects(AngelScript::asIScriptEngine* engine) ChildNodeArray::RegisterReadonlyScriptArrayView(engine, "ChildNodeArray", "Node"); AnimationStateDict::RegisterReadonlyScriptDictView(engine, "AnimationStateDict", "AnimationState"); SubMeshArray::RegisterReadonlyScriptArrayView(engine, "SubMeshArray", "SubMesh"); + TechniqueArray::RegisterReadonlyScriptArrayView(engine, "TechniqueArray", "Technique"); + PassArray::RegisterReadonlyScriptArrayView(engine, "PassArray", "Pass"); + TextureUnitStateArray::RegisterReadonlyScriptArrayView(engine, "TextureUnitStateArray", "TextureUnitState"); + SubEntityArray::RegisterReadonlyScriptArrayView(engine, "SubEntityArray", "SubEntity"); // enums, also under namespace `Ogre` @@ -684,6 +769,7 @@ void RoR::RegisterOgreObjects(AngelScript::asIScriptEngine* engine) registerOgreNode(engine); registerOgreMovableObject(engine); registerOgreEntity(engine); + registerOgreSubEntity(engine); registerOgreSceneNode(engine); registerOgreSceneManager(engine); registerOgreRoot(engine); @@ -699,6 +785,11 @@ void RoR::RegisterOgreObjects(AngelScript::asIScriptEngine* engine) registerOgreMesh(engine); registerOgreSubMesh(engine); registerOgreMeshManager(engine); + registerOgreMaterial(engine); + registerOgreTechnique(engine); + registerOgrePass(engine); + registerOgreTextureUnitState(engine); + registerOgreMaterialManager(engine); // To estabilish class hierarchy in AngelScript you need to register the reference cast operators opCast and opImplCast. @@ -1289,6 +1380,9 @@ void registerOgreTexture(AngelScript::asIScriptEngine* engine) r = engine->RegisterObjectMethod("TexturePtr", "bool isNull()", asFUNCTION(TexturePtrIsNull), asCALL_CDECL_OBJLAST); ROR_ASSERT(r >= 0); // Wrappers are inevitable, see https://www.gamedev.net/forums/topic/540419-custom-smartpointers-and-angelscript-/ + r = engine->RegisterObjectMethod("TexturePtr", "string getName() const", asFUNCTIONPR([](TexturePtr const& self) { + return self->getName(); + }, (TexturePtr const&), Ogre::String), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); r = engine->RegisterObjectMethod("TexturePtr", "uint getWidth()", asFUNCTIONPR([](TexturePtr const& self) { return (asUINT)self->getWidth(); }, (TexturePtr const&), Ogre::uint32), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); @@ -1369,7 +1463,6 @@ void registerOgreEntity(AngelScript::asIScriptEngine* engine) int r; r = engine->SetDefaultNamespace("Ogre"); ROR_ASSERT(r >= 0); - r = engine->RegisterObjectMethod("Entity", "void setMaterialName(const string &in name, const string &in rg = \"OgreAutodetect\")", asMETHOD(Entity, setMaterialName), asCALL_THISCALL); ROR_ASSERT(r >= 0); r = engine->RegisterObjectMethod("Entity", "AnimationState @getAnimationState(const string &in) const", asMETHOD(Entity, getAnimationState), asCALL_THISCALL); ROR_ASSERT(r >= 0); r = engine->RegisterObjectMethod("Entity", "AnimationStateSet @getAllAnimationStates()", asMETHOD(Entity, getAllAnimationStates), asCALL_THISCALL); ROR_ASSERT(r >= 0); r = engine->RegisterObjectMethod("Entity", "void setDisplaySkeleton(bool)", asMETHOD(Entity, setDisplaySkeleton), asCALL_THISCALL); ROR_ASSERT(r >= 0); @@ -1379,12 +1472,25 @@ void registerOgreEntity(AngelScript::asIScriptEngine* engine) r = engine->RegisterObjectMethod("Entity", "Entity @getManualLodLevel(uint64) const", asMETHOD(Entity, getManualLodLevel), asCALL_THISCALL); ROR_ASSERT(r >= 0); r = engine->RegisterObjectMethod("Entity", "void setMeshLodBias(float, uint16, uint16)", asMETHOD(Entity, setMeshLodBias), asCALL_THISCALL); ROR_ASSERT(r >= 0); r = engine->RegisterObjectMethod("Entity", "void setMaterialLodBias(float, uint16, uint16)", asMETHOD(Entity, setMaterialLodBias), asCALL_THISCALL); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectMethod("Entity", "SubEntityArray @getSubEntities() const", asFUNCTION(EntityGetSubEntities), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectMethod("Entity", "const MeshPtr& getMesh() const", asMETHOD(Entity, getMesh), asCALL_THISCALL); ROR_ASSERT(r >= 0); registerOgreMovableObjectBase(engine, "Entity"); r = engine->SetDefaultNamespace(""); ROR_ASSERT(r >= 0); } +void registerOgreSubEntity(AngelScript::asIScriptEngine* engine) +{ + int r; + r = engine->SetDefaultNamespace("Ogre"); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectMethod("SubEntity", "const MaterialPtr& getMaterial() const", asMETHOD(SubEntity, getMaterial), asCALL_THISCALL); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectMethod("SubEntity", "void setMaterial(const MaterialPtr&in)", asMETHOD(SubEntity, setMaterial), asCALL_THISCALL); ROR_ASSERT(r >= 0); + + r = engine->SetDefaultNamespace(""); ROR_ASSERT(r >= 0); +} + template void registerOgreNodeBase(AngelScript::asIScriptEngine* engine, const char* obj) { @@ -1855,7 +1961,6 @@ void registerOgreSubMesh(AngelScript::asIScriptEngine* engine) engine->SetDefaultNamespace("Ogre"); // Register the SubMesh class - engine->RegisterObjectType("SubMesh", 0, asOBJ_REF | asOBJ_NOCOUNT); engine->RegisterObjectMethod("SubMesh", "const string& getMaterialName()", asMETHOD(Ogre::SubMesh, getMaterialName), asCALL_THISCALL); engine->RegisterObjectMethod("SubMesh", "void setMaterialName(const string&in, const string&in)", asMETHOD(Ogre::SubMesh, setMaterialName), asCALL_THISCALL); @@ -1911,3 +2016,89 @@ void registerOgreMeshManager(AngelScript::asIScriptEngine * engine) r = engine->SetDefaultNamespace(""); ROR_ASSERT(r >= 0); } + +void registerOgreMaterialManager(AngelScript::asIScriptEngine * engine) +{ + int r; + r = engine->SetDefaultNamespace("Ogre"); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectMethod("MaterialManager", "MaterialPtr getByName(const string&in file, const string&in rg)", asFUNCTIONPR([](MaterialManager& mgr, std::string const& file, std::string const& rg){ + try { return mgr.getByName(file, rg); } + catch (...) { App::GetScriptEngine()->forwardExceptionAsScriptEvent("Ogre::MaterialManager::getByName()"); return Ogre::MaterialPtr();} + }, (MaterialManager& mgr, std::string const& file, std::string const& rg), MaterialPtr), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectMethod("MaterialManager", "void create(const string&in file, const string&in rg)", asFUNCTIONPR([](MaterialManager& mgr, std::string const& file, std::string const& rg){ + try { return mgr.create(file, rg); } + catch (...) { App::GetScriptEngine()->forwardExceptionAsScriptEvent("Ogre::MaterialManager::create()"); return Ogre::MaterialPtr(); } + }, (MaterialManager& mgr, std::string const& file, std::string const& rg), MaterialPtr), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + + r = engine->SetDefaultNamespace("Ogre::MaterialManager"); ROR_ASSERT(r >= 0); + r = engine->RegisterGlobalFunction("MaterialManager& getSingleton()", asFUNCTION(MaterialManager::getSingleton), asCALL_CDECL); ROR_ASSERT(r >= 0); + + r = engine->SetDefaultNamespace(""); ROR_ASSERT(r >= 0); +} + +void registerOgreMaterial(AngelScript::asIScriptEngine* engine) +{ + int r; + r = engine->SetDefaultNamespace("Ogre"); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectBehaviour("MaterialPtr", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(MaterialPtrDefaultConstructor), asCALL_CDECL_OBJLAST); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectBehaviour("MaterialPtr", asBEHAVE_CONSTRUCT, "void f(const MaterialPtr&in)", asFUNCTION(MaterialPtrCopyConstructor), asCALL_CDECL_OBJLAST); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectBehaviour("MaterialPtr", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(MaterialPtrDestructor), asCALL_CDECL_OBJLAST); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectMethod("MaterialPtr", "MaterialPtr& opAssign(const MaterialPtr&in)", asFUNCTION(MaterialPtrAssignOperator), asCALL_CDECL_OBJLAST); ROR_ASSERT(r >= 0); + r = engine->RegisterObjectMethod("MaterialPtr", "bool isNull()", asFUNCTION(MaterialPtrIsNull), asCALL_CDECL_OBJLAST); ROR_ASSERT(r >= 0); + + // Wrappers are inevitable, see https://www.gamedev.net/forums/topic/540419-custom-smartpointers-and-angelscript-/ + r = engine->RegisterObjectMethod("MaterialPtr", "TechniqueArray@ getTechniques()", asFUNCTION(MaterialPtrGetTechniques), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectMethod("MaterialPtr", "string getName()", asFUNCTIONPR([](MaterialPtr const& self) { + return self->getName(); + }, (MaterialPtr const&), Ogre::String), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectMethod("MaterialPtr", "Technique@ createTechnique()", asFUNCTIONPR([](MaterialPtr const& self) { + try { return self->createTechnique(); } + catch (...) { App::GetScriptEngine()->forwardExceptionAsScriptEvent("Ogre::Material::createTechnique()"); return (Ogre::Technique*)nullptr;} + }, (MaterialPtr const&), Ogre::Technique*), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + + r = engine->RegisterObjectMethod("MaterialPtr", "void removeTechnique()", asFUNCTIONPR([](MaterialPtr const& self, uint16_t index) { + try { self->removeTechnique(index); } + catch (...) { App::GetScriptEngine()->forwardExceptionAsScriptEvent("Ogre::Material::removeTechnique()"); } + }, (MaterialPtr const&, uint16_t), void), asCALL_CDECL_OBJFIRST); ROR_ASSERT(r >= 0); + + r = engine->SetDefaultNamespace(""); ROR_ASSERT(r >= 0); +} + +void registerOgreTechnique(AngelScript::asIScriptEngine* engine) +{ + engine->SetDefaultNamespace("Ogre"); + + engine->RegisterObjectMethod("Technique", "PassArray @getPasses()", asFUNCTION(TechniqueGetPasses), asCALL_CDECL_OBJFIRST); + engine->RegisterObjectMethod("Technique", "Pass @createPass()", asMETHOD(Ogre::Technique, createPass), asCALL_THISCALL); + engine->RegisterObjectMethod("Technique", "void removePass(uint16 index)", asMETHOD(Ogre::Technique, removePass), asCALL_THISCALL); + engine->RegisterObjectMethod("Technique", "const string& getName() const", asMETHOD(Ogre::Technique, getName), asCALL_THISCALL); + + engine->SetDefaultNamespace(""); +} + +void registerOgrePass(AngelScript::asIScriptEngine* engine) +{ + engine->SetDefaultNamespace("Ogre"); + + engine->RegisterObjectMethod("Pass", "const string& getName() const", asMETHOD(Ogre::Pass, getName), asCALL_THISCALL); + engine->RegisterObjectMethod("Pass", "TextureUnitStateArray @getTextureUnitStates()", asFUNCTION(PassGetTextureUnitStates), asCALL_CDECL_OBJFIRST); + engine->RegisterObjectMethod("Pass", "void removeTextureUnitState(uint16 index)", asMETHOD(Ogre::Pass, removeTextureUnitState), asCALL_THISCALL); + + engine->SetDefaultNamespace(""); +} + +void registerOgreTextureUnitState(AngelScript::asIScriptEngine* engine) +{ + engine->SetDefaultNamespace("Ogre"); + + engine->RegisterObjectMethod("TextureUnitState", "const string& getName() const", asMETHOD(Ogre::TextureUnitState, getName), asCALL_THISCALL); + engine->RegisterObjectMethod("TextureUnitState", "void setTexture(const TexturePtr&in)", asMETHODPR(Ogre::TextureUnitState, setTexture, (const TexturePtr&), void), asCALL_THISCALL); + engine->RegisterObjectMethod("TextureUnitState", "const TexturePtr& _getTexturePtr() const", asMETHODPR(Ogre::TextureUnitState, _getTexturePtr, (void) const, const TexturePtr&), asCALL_THISCALL); + + engine->SetDefaultNamespace(""); +} \ No newline at end of file