Skip to content

Commit

Permalink
👼Script: Added game.serializeMeshResource() - dumps .mesh files
Browse files Browse the repository at this point in the history
Script 'example_ogre_terrnBatcher.as' updates:
- Added [dump] button to inspector scenegraph - tested to work also for ManualObjects
- Fixed mesh generation (I forgot to offset indices for every new appended mesh)
- Reduced debug outputs

Code changes:
- added RGN_LOGS, auto-created at startup (along with other resource groups)
- the actor-dump code now uses RGN_LOGS
- moved RGN_ defs from ResourceManager.h to Application.h
  • Loading branch information
ohlidalp committed Mar 14, 2024
1 parent e82d58a commit a35a9d8
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 33 deletions.
77 changes: 60 additions & 17 deletions resources/scripts/example_ogre_terrnBatcher.as
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,18 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
// * We keep a dictionary "scenenode name" -> child node indices
dictionary tbuiSelection;

// Checkboxes in 'batch controls'
bool tbuiHideOriginal = true;
bool tbuiShowNew = true;

// Checkboxes in 'inspector'
bool tbuiShowDumpButton = false;

// Counters
uint tbuiNumBatchesCreated = 0;
uint tbuiNumMeshesDumped = 0;

// Constants
string tbuiOutputsNodeName = "TerrnBatcher outputs";

//#region Draw UI - main
Expand Down Expand Up @@ -100,6 +107,7 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S

ImGui::Separator();
ImGui::TextDisabled(" S C E N E G R A P H :");
ImGui::Checkbox("Enable mesh dumping (write .mesh file to logs directory)", tbuiShowDumpButton);
this.drawTreeNodeOgreSceneNodeRecursive(terrnNode);
}
//#endregion Draw UI - main
Expand Down Expand Up @@ -507,7 +515,46 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
this.pickSceneNode(snode.getParentSceneNode().getName(), indexUnderParent);
}
}
// END terrnBatcher selection

// +TerrnBatcher - mesh serialization
if (tbuiShowDumpButton)
{
ImGui::SameLine();
if (ImGui::SmallButton("Dump"))
{
if (this.dumpMesh(movables[0]))
{
game.log("TerrnBatcher: Mesh dumped successfully");
}
}
}
}

bool dumpMesh(Ogre::MovableObject@ movable)
{
// Save to 'logs' directory - Make sure filenames dont clash
// ----------------------------------------------------------
string fileName = "TerrnBatcher(NID" + thisScript + ")_Dump" + (tbuiNumMeshesDumped++) + "_";
Ogre::MeshPtr mesh;
if (movable.getMovableType() == "Entity")
{
Ogre::Entity@ ent = cast<Ogre::Entity>(movable);
mesh = ent.getMesh();
fileName += mesh.getName();
}
else if (movable.getMovableType() == "ManualObject")
{
Ogre::ManualObject@ mo = cast<Ogre::ManualObject>(movable);
mesh = mo.convertToMesh(fileName, "Logs");
fileName += "batch.mesh";
}
else
{
game.log("ERROR dumpMesh(): unrecognized MovableObject type '" + movable.getMovableType() + "'");
return false;
}

return game.serializeMeshResource(fileName, "Logs", mesh);
}

// #endregion TerrnBatcher controls for inspector
Expand Down Expand Up @@ -576,41 +623,37 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
// Proof of concept: make it a world-space mesh. Assume tri-list type mesh with 1 UV (=texcoords) layer
// ------------------------------------------------------------------------

uint dbgVertsAdded = 0;
uint dbgIndsAdded = 0;

uint indexBase = mo.getCurrentVertexCount();

array<vector3> vertPos = subMesh.__getVertexPositions();
array<vector2> vertUVs = subMesh.__getVertexTexcoords(0);
for (uint iVert = 0; iVert < vertPos.length(); iVert++)
{
mo.position((rot * vertPos[iVert]) * scale + pos);
mo.textureCoord(vertUVs[iVert]);
dbgVertsAdded++;
}

if (subMesh.__getIndexType() == Ogre::IndexType::IT_16BIT)
{
array<uint16> indexBuf = subMesh.__getIndexBuffer16bit();
for (uint iIndex = 0; iIndex < indexBuf.length(); iIndex++)
{
mo.index(indexBuf[iIndex]);
dbgIndsAdded++;
mo.index(indexBase + indexBuf[iIndex]);
}
}
else
{
game.log("ERROR appendMeshInstanceToManualObject(): mesh is not supported - not 16-bit indexed");
}

game.log("DBG addSingleSubMeshToBatch() dbgVertsAdded="+dbgVertsAdded+", dbgIndsAdded="+dbgIndsAdded);
}

void addSceneNodeAttachedMeshesToBatch(Ogre::ManualObject@ mo, Ogre::SceneNode@ pickedNode, string&inout foundMatName)
{
Ogre::MovableObjectArray@ movables = pickedNode.getAttachedObjects();
for (uint iMovas = 0; iMovas < movables.length(); iMovas++)
{
game.log("DBG addSceneNodeAttachedMeshesToBatch(): iMovas="+iMovas+"/"+movables.length());
//game.log("DBG addSceneNodeAttachedMeshesToBatch(): iMovas="+iMovas+"/"+movables.length());
if (movables[iMovas].getMovableType() != "Entity")
{
game.log("DBG batchSelectedMeshes(): skipping movable of type '"+movables[iMovas].getMovableType()+"' - not suported!");
Expand All @@ -621,7 +664,7 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
Ogre::SubEntityArray@ subEntities = ent.getSubEntities();
for (uint iSubent=0; iSubent<subEntities.length(); iSubent++)
{
game.log("DBG addSceneNodeAttachedMeshesToBatch(): iSubent="+iSubent+"/"+subEntities.length());
//game.log("DBG addSceneNodeAttachedMeshesToBatch(): iSubent="+iSubent+"/"+subEntities.length());
Ogre::MaterialPtr mat = subEntities[iSubent].getMaterial();
if (mat.isNull())
{
Expand Down Expand Up @@ -658,7 +701,7 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
// ----------------------------------------------------------------------------------------------------------------------

string moName = this.composeUniqueId("batch #" + tbuiNumBatchesCreated++);
game.log("DBG batchSelectedMeshes(): moName='"+moName+"'");
//game.log("DBG batchSelectedMeshes(): moName='"+moName+"'");
Ogre::ManualObject@ mo = game.getSceneManager().createManualObject(moName);
string foundMatName = "";
if (@mo == null)
Expand Down Expand Up @@ -689,7 +732,7 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
// Loop through list of scene nodes selected under this grouping node
array<uint>@ nodeIndices = cast<array<uint>>(tbuiSelection[tobjNodes[iNode]]);

game.log("DBG batchSelectedMeshes(): processing node "+uint(iNode+1)+"/"+tobjNodes.length()+" ("+tobjNodes[iNode]+") - "+nodeIndices.length()+" nodes picked");
//game.log("DBG batchSelectedMeshes(): processing node "+uint(iNode+1)+"/"+tobjNodes.length()+" ("+tobjNodes[iNode]+") - "+nodeIndices.length()+" nodes picked");

for (uint iChild=0; iChild<nodeIndices.length(); iChild++)
{
Expand All @@ -704,7 +747,7 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
// Process the entities attached to this scene node
Ogre::SceneNode@ childNode = cast<Ogre::SceneNode>(children[nodeIndices[iChild]]);

game.log("DBG batchSelectedMeshes(): iChild="+iChild+"/"+nodeIndices.length()+", uniqueName()="+childNode.__getUniqueName());
//game.log("DBG batchSelectedMeshes(): iChild="+iChild+"/"+nodeIndices.length()+", uniqueName()="+childNode.__getUniqueName());
this.addSceneNodeAttachedMeshesToBatch(mo, childNode, /*[inout]*/foundMatName);
if (tbuiHideOriginal)
{
Expand All @@ -729,14 +772,14 @@ class TerrnBatcherUI // Based on Inspector UI, can pick scenenodes and prepare S
}
}

// Helpers to make magic happen
// #endregion The actual magic - merging meshes

// #region Generic helpers
string composeUniqueId(string name)
{
// to avoid clash with leftover scene nodes created before, we include the NID in the name - using automatic global var `thisScript`.
return "TerrnBatcher(NID:"+thisScript+"): "+name;
}

// #endregion The actual magic - merging meshes

// #endregion Generic helpers
}

11 changes: 11 additions & 0 deletions source/main/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@

#define CHARACTER_ANIM_NAME_LEN 10 // Restricted for networking

// OGRE resource group names
#define RGN_TEMP "Temp"
#define RGN_CACHE "Cache"
#define RGN_REPO "Repo"
#define RGN_CONFIG "Config"
#define RGN_CONTENT "Content"
#define RGN_SAVEGAMES "Savegames"
#define RGN_MANAGED_MATS "ManagedMaterials"
#define RGN_SCRIPTS "Scripts"
#define RGN_LOGS "Logs"

// Legacy macros
#define TOSTRING(x) Ogre::StringConverter::toString(x)
#define PARSEINT(x) Ogre::StringConverter::parseInt(x)
Expand Down
9 changes: 2 additions & 7 deletions source/main/physics/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4562,15 +4562,10 @@ void Actor::WriteDiagnosticDump(std::string const& fileName)
<< std::endl;
}

// Write out to 'logs' using OGRE resource system - complicated, but works with Unicode paths on Windows
Ogre::String rgName = "dumpRG";
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
App::sys_logs_dir->getStr(), "FileSystem", rgName, /*recursive=*/false, /*readOnly=*/false);
Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup(rgName);
Ogre::DataStreamPtr outStream = Ogre::ResourceGroupManager::getSingleton().createResource(fileName, rgName, /*overwrite=*/true);
// Write out to 'logs' using OGRE resource system - works with Unicode paths on Windows
Ogre::DataStreamPtr outStream = Ogre::ResourceGroupManager::getSingleton().createResource(fileName, RGN_LOGS, /*overwrite=*/true);
std::string text = buf.str();
outStream->write(text.c_str(), text.length());
Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(rgName);
}

void Actor::UpdatePropAnimInputEvents()
Expand Down
2 changes: 2 additions & 0 deletions source/main/resources/ContentManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ void ContentManager::InitContentManager()
App::sys_savegames_dir->getStr(), "FileSystem", RGN_SAVEGAMES, /*recursive=*/false, /*readOnly=*/false);
ResourceGroupManager::getSingleton().addResourceLocation(
App::sys_scripts_dir->getStr(), "FileSystem", RGN_SCRIPTS, /*recursive:*/false, /*readonly:*/false);
ResourceGroupManager::getSingleton().addResourceLocation(
App::sys_logs_dir->getStr(), "FileSystem", RGN_LOGS, /*recursive:*/false, /*readonly:*/false);

Ogre::ScriptCompilerManager::getSingleton().setListener(this);

Expand Down
9 changes: 0 additions & 9 deletions source/main/resources/ContentManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,6 @@
#include <OgreScriptCompiler.h>
#include <rapidjson/document.h>

#define RGN_TEMP "Temp"
#define RGN_CACHE "Cache"
#define RGN_REPO "Repo"
#define RGN_CONFIG "Config"
#define RGN_CONTENT "Content"
#define RGN_SAVEGAMES "Savegames"
#define RGN_MANAGED_MATS "ManagedMaterials"
#define RGN_SCRIPTS "Scripts"

namespace RoR {

class ContentManager:
Expand Down
22 changes: 22 additions & 0 deletions source/main/scripting/GameScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,26 @@ Ogre::Image GameScript::loadImageResource(const std::string& filename, const std
}
}

bool GameScript::serializeMeshResource(const std::string& filename, const std::string& resource_group, const Ogre::MeshPtr& mesh)
{
try
{
std::string resource_name = this->CheckFileAccess("serializeMeshResource()", filename, resource_group);
if (resource_name == "")
return false; // Access denied - error already logged

Ogre::MeshSerializer ser;
Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().createResource(resource_name, resource_group);
ser.exportMesh(mesh.get(), stream);
return true;
}
catch (...)
{
App::GetScriptEngine()->forwardExceptionAsScriptEvent("GameScript::serializeMeshResource()");
return false;
}
}

// ------------------------
// Helpers:

Expand Down Expand Up @@ -1856,6 +1876,8 @@ bool GameScript::HaveMainCamera(const char* func_name)
std::string GameScript::CheckFileAccess(const char* func_name, const std::string& filename, const std::string& resource_group)
{
// Extract filename and extension from the input, because OGRE allows absolute paths in resource system.
// -----------------------------------------------------------------------------------------------------

std::string basename, extension, path;
Ogre::StringUtil::splitFullFilename(filename, basename, extension, path);
if (path != "")
Expand Down
5 changes: 5 additions & 0 deletions source/main/scripting/GameScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ class GameScript
*/
Ogre::Image loadImageResource(const std::string& filename, const std::string& resource_group);

/**
* Uses `Ogre::MeshSerializer` to save binary .mesh file (latest format, native endianness).
*/
bool serializeMeshResource(const std::string& filename, const std::string& resource_group, const Ogre::MeshPtr& mesh);

/// @}

/// @name GUI
Expand Down
1 change: 1 addition & 0 deletions source/main/scripting/bindings/GameScriptAngelscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ void RoR::RegisterGameScript(asIScriptEngine *engine)
result = engine->RegisterObjectMethod("GameScriptClass", "array<dictionary>@ findResourceFileInfo(const string &in, const string &in, bool=false)", asMETHOD(GameScript, findResourceFileInfo), asCALL_THISCALL); ROR_ASSERT(result >= 0);
result = engine->RegisterObjectMethod("GameScriptClass", "void fetchUrlAsStringAsync(const string &in, const string &in)", asMETHOD(GameScript, fetchUrlAsStringAsync), asCALL_THISCALL); ROR_ASSERT(result >= 0);
result = engine->RegisterObjectMethod("GameScriptClass", "Ogre::Image loadImageResource(const string &in, const string &in)", asMETHOD(GameScript, loadImageResource), asCALL_THISCALL); ROR_ASSERT(result >= 0);
result = engine->RegisterObjectMethod("GameScriptClass", "bool serializeMeshResource(const string &in, const string &in, const Ogre::MeshPtr &in)", asMETHOD(GameScript, serializeMeshResource), asCALL_THISCALL); ROR_ASSERT(result >= 0);

// > GUI
result = engine->RegisterObjectMethod("GameScriptClass", "void flashMessage(const string &in, float, float)", asMETHOD(GameScript, flashMessage), asCALL_THISCALL); ROR_ASSERT(result >= 0);
Expand Down
5 changes: 5 additions & 0 deletions source/main/scripting/bindings/OgreAngelscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,11 @@ void registerOgreManualObject(AngelScript::asIScriptEngine* engine)
engine->RegisterObjectMethod("ManualObject", "void colour(const color&in)", asMETHODPR(Ogre::ManualObject, colour, (const Ogre::ColourValue&), void), asCALL_THISCALL);
engine->RegisterObjectMethod("ManualObject", "void index(uint32)", asMETHOD(Ogre::ManualObject, index), asCALL_THISCALL);
engine->RegisterObjectMethod("ManualObject", "void end()", asMETHOD(Ogre::ManualObject, end), asCALL_THISCALL);

engine->RegisterObjectMethod("ManualObject", "uint getCurrentVertexCount()", asMETHOD(Ogre::ManualObject, getCurrentVertexCount), asCALL_THISCALL);
engine->RegisterObjectMethod("ManualObject", "void getCurrentIndexCount()", asMETHOD(Ogre::ManualObject, getCurrentIndexCount), asCALL_THISCALL);

engine->RegisterObjectMethod("ManualObject", "MeshPtr convertToMesh(const string&in name, const string&in group = 'General')", asMETHOD(Ogre::ManualObject, convertToMesh), asCALL_THISCALL);

engine->SetDefaultNamespace("");
}
Expand Down

0 comments on commit a35a9d8

Please sign in to comment.