From 1d754cbbad40b366f25a6dd82ad5ab83988301c4 Mon Sep 17 00:00:00 2001 From: Areloch Date: Sat, 10 Dec 2016 17:27:27 -0600 Subject: [PATCH 1/2] Makes it so prefabs can correctly 'export to collada' by running the buildPolyList function on it's children. Also adds a 'Bake Selection to Mesh' option to the tools menu in the editor to export the selected mesh to a collada file, and then replaces the selection with a TSStatic. --- Engine/source/T3D/prefab.cpp | 13 ++ Engine/source/T3D/prefab.h | 2 + Engine/source/T3D/tsStatic.h | 1 + Engine/source/gui/worldEditor/worldEditor.cpp | 161 +++++++++++++++++- Engine/source/gui/worldEditor/worldEditor.h | 2 + .../worldEditor/scripts/menuHandlers.ed.cs | 32 ++++ .../tools/worldEditor/scripts/menus.ed.cs | 1 + .../worldEditor/scripts/menuHandlers.ed.cs | 32 ++++ .../tools/worldEditor/scripts/menus.ed.cs | 1 + 9 files changed, 244 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/prefab.cpp b/Engine/source/T3D/prefab.cpp index f9cf743a09..6d357565f3 100644 --- a/Engine/source/T3D/prefab.cpp +++ b/Engine/source/T3D/prefab.cpp @@ -525,6 +525,19 @@ bool Prefab::isValidChild( SimObject *simobj, bool logWarnings ) return true; } +bool Prefab::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere) +{ + Vector foundObjects; + mChildGroup->findObjectByType(foundObjects); + + for (S32 i = 0; i < foundObjects.size(); i++) + { + foundObjects[i]->buildPolyList(context, polyList, box, sphere); + } + + return true; +} + ExplodePrefabUndoAction::ExplodePrefabUndoAction( Prefab *prefab ) : UndoAction( "Explode Prefab" ) { diff --git a/Engine/source/T3D/prefab.h b/Engine/source/T3D/prefab.h index ac61a74587..48d87c232a 100644 --- a/Engine/source/T3D/prefab.h +++ b/Engine/source/T3D/prefab.h @@ -96,6 +96,8 @@ class Prefab : public SceneObject /// which is added to the MissionGroup and returned to the caller. SimGroup* explode(); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere); + protected: void _closeFile( bool removeFileNotify ); diff --git a/Engine/source/T3D/tsStatic.h b/Engine/source/T3D/tsStatic.h index 57a7fc695b..db1ff138c0 100644 --- a/Engine/source/T3D/tsStatic.h +++ b/Engine/source/T3D/tsStatic.h @@ -228,6 +228,7 @@ class TSStatic : public SceneObject Resource getShape() const { return mShape; } StringTableEntry getShapeFileName() { return mShapeName; } + void setShapeFileName(StringTableEntry shapeName) { mShapeName = shapeName; } TSShapeInstance* getShapeInstance() const { return mShapeInstance; } diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index d9f2c7820a..087256f31c 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -47,7 +47,7 @@ #include "platform/typetraits.h" #include "T3D/prefab.h" #include "math/mEase.h" - +#include "T3D/tsStatic.h" IMPLEMENT_CONOBJECT( WorldEditor ); @@ -3753,6 +3753,158 @@ void WorldEditor::explodeSelectedPrefab() setDirty(); } +void WorldEditor::bakeSelectionToMesh(const char *filename) +{ + if (mSelected->size() == 0) + { + Con::errorf("WorldEditor::makeSelectionPrefab - Nothing selected."); + return; + } + + SimGroup *missionGroup; + if (!Sim::findObject("MissionGroup", missionGroup)) + { + Con::errorf("WorldEditor::makeSelectionPrefab - Could not find MissionGroup."); + return; + } + + Vector< SimObject* > stack; + Vector< SimObject* > found; + + for (S32 i = 0; i < mSelected->size(); i++) + { + SimObject *obj = (*mSelected)[i]; + stack.push_back(obj); + } + + Vector< SimGroup* > cleanup; + + while (!stack.empty()) + { + SimObject *obj = stack.last(); + SimGroup *grp = dynamic_cast< SimGroup* >(obj); + + stack.pop_back(); + + if (grp) + { + for (S32 i = 0; i < grp->size(); i++) + stack.push_back(grp->at(i)); + + SceneObject* scn = dynamic_cast< SceneObject* >(grp); + if (scn) + { + if (Prefab::isValidChild(obj, true)) + found.push_back(obj); + } + else + { + //Only push the cleanup of the group if it's ONLY a SimGroup. + cleanup.push_back(grp); + } + } + else + { + if (Prefab::isValidChild(obj, true)) + found.push_back(obj); + } + } + + if (found.empty()) + { + Con::warnf("WorldEditor::makeSelectionPrefab - No valid objects selected."); + return; + } + + // SimGroup we collect prefab objects into. + SimGroup *group = new SimGroup(); + group->registerObject(); + + // Transform from World to Prefab space. + MatrixF fabMat(true); + fabMat.setPosition(mSelected->getCentroid()); + fabMat.inverse(); + + MatrixF objMat; + SimObject *obj = NULL; + SceneObject *sObj = NULL; + + Vector< SceneObject* > objectList; + + for ( S32 i = 0; i < mSelected->size(); i++ ) + { + SceneObject *pObj = dynamic_cast< SceneObject* >( ( *mSelected )[i] ); + if ( pObj ) + objectList.push_back( pObj ); + } + + if ( objectList.empty() ) + return; + + // + Point3F centroid; + MatrixF orientation; + + if (objectList.size() == 1) + { + orientation = objectList[0]->getTransform(); + centroid = objectList[0]->getPosition(); + } + else + { + orientation.identity(); + centroid.zero(); + + S32 count = 0; + + for (S32 i = 0; i < objectList.size(); i++) + { + SceneObject *pObj = objectList[i]; + if (pObj->isGlobalBounds()) + continue; + + centroid += pObj->getPosition(); + count++; + } + + centroid /= count; + } + + orientation.setPosition(centroid); + orientation.inverse(); + + OptimizedPolyList polyList; + polyList.setBaseTransform(orientation); + + for (S32 i = 0; i < objectList.size(); i++) + { + SceneObject *pObj = objectList[i]; + if (!pObj->buildPolyList(PLC_Export, &polyList, pObj->getWorldBox(), pObj->getWorldSphere())) + Con::warnf("colladaExportObjectList() - object %i returned no geometry.", pObj->getId()); + } + + // Use a ColladaUtils function to do the actual export to a Collada file + ColladaUtils::exportToCollada(filename, polyList); + // + + // Allocate TSStatic object and add to level. + TSStatic *ts = new TSStatic(); + ts->setShapeFileName(StringTable->insert(filename)); + fabMat.inverse(); + ts->setTransform(fabMat); + ts->registerObject(); + missionGroup->addObject(ts); + + // Select it, mark level as dirty. + clearSelection(); + selectObject(ts); + setDirty(); + + // Delete original objects and temporary SimGroup. + for (S32 i = 0; i < objectList.size(); i++) + objectList[i]->deleteObject(); +} + DefineEngineMethod( WorldEditor, makeSelectionPrefab, void, ( const char* filename ),, "Save selected objects to a .prefab file and replace them in the level with a Prefab object." "@param filename Prefab file to save the selected objects to.") @@ -3766,6 +3918,13 @@ DefineEngineMethod( WorldEditor, explodeSelectedPrefab, void, (),, object->explodeSelectedPrefab(); } +DefineEngineMethod(WorldEditor, bakeSelectionToMesh, void, (const char* filename), , + "Save selected objects to a .dae collada file and replace them in the level with a TSStatic object." + "@param filename collada file to save the selected objects to.") +{ + object->bakeSelectionToMesh(filename); +} + DefineEngineMethod( WorldEditor, mountRelative, void, ( SceneObject *objA, SceneObject *objB ),, "Mount object B relatively to object A." "@param objA Object to mount to." diff --git a/Engine/source/gui/worldEditor/worldEditor.h b/Engine/source/gui/worldEditor/worldEditor.h index b1cbcda29d..35290ca408 100644 --- a/Engine/source/gui/worldEditor/worldEditor.h +++ b/Engine/source/gui/worldEditor/worldEditor.h @@ -117,6 +117,8 @@ class WorldEditor : public EditTSCtrl void makeSelectionPrefab( const char *filename ); void explodeSelectedPrefab(); + void bakeSelectionToMesh(const char *filename); + // static SceneObject* getClientObj(SceneObject *); static void markAsSelected( SimObject* object, bool state ); diff --git a/Templates/Empty/game/tools/worldEditor/scripts/menuHandlers.ed.cs b/Templates/Empty/game/tools/worldEditor/scripts/menuHandlers.ed.cs index 61f2141516..1823e0d550 100644 --- a/Templates/Empty/game/tools/worldEditor/scripts/menuHandlers.ed.cs +++ b/Templates/Empty/game/tools/worldEditor/scripts/menuHandlers.ed.cs @@ -555,6 +555,38 @@ function EditorExplodePrefab() EditorTree.buildVisibleTree( true ); } +function bakeSelectedToMesh() +{ + + %dlg = new SaveFileDialog() + { + Filters = "Collada file (*.dae)|*.dae|"; + DefaultPath = $Pref::WorldEditor::LastPath; + DefaultFile = ""; + ChangePath = false; + OverwritePrompt = true; + }; + + %ret = %dlg.Execute(); + if ( %ret ) + { + $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); + %saveFile = %dlg.FileName; + } + + if( fileExt( %saveFile ) !$= ".dae" ) + %saveFile = %saveFile @ ".dae"; + + %dlg.delete(); + + if ( !%ret ) + return; + + EWorldEditor.bakeSelectionToMesh( %saveFile ); + + EditorTree.buildVisibleTree( true ); +} + function EditorMount() { echo( "EditorMount" ); diff --git a/Templates/Empty/game/tools/worldEditor/scripts/menus.ed.cs b/Templates/Empty/game/tools/worldEditor/scripts/menus.ed.cs index 0916a00655..976d3c08b9 100644 --- a/Templates/Empty/game/tools/worldEditor/scripts/menus.ed.cs +++ b/Templates/Empty/game/tools/worldEditor/scripts/menus.ed.cs @@ -263,6 +263,7 @@ class = "EditorUtilitiesMenu"; item[0] = "Network Graph" TAB "n" TAB "toggleNetGraph();"; item[1] = "Profiler" TAB "ctrl F2" TAB "showMetrics(true);"; + item[2] = "Bake Selected to Mesh" TAB "" TAB "bakeSelectedToMesh();"; }; %this.menuBar.insert(%toolsMenu, %this.menuBar.getCount()); diff --git a/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs index 61f2141516..1823e0d550 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs @@ -555,6 +555,38 @@ function EditorExplodePrefab() EditorTree.buildVisibleTree( true ); } +function bakeSelectedToMesh() +{ + + %dlg = new SaveFileDialog() + { + Filters = "Collada file (*.dae)|*.dae|"; + DefaultPath = $Pref::WorldEditor::LastPath; + DefaultFile = ""; + ChangePath = false; + OverwritePrompt = true; + }; + + %ret = %dlg.Execute(); + if ( %ret ) + { + $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); + %saveFile = %dlg.FileName; + } + + if( fileExt( %saveFile ) !$= ".dae" ) + %saveFile = %saveFile @ ".dae"; + + %dlg.delete(); + + if ( !%ret ) + return; + + EWorldEditor.bakeSelectionToMesh( %saveFile ); + + EditorTree.buildVisibleTree( true ); +} + function EditorMount() { echo( "EditorMount" ); diff --git a/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs index 0916a00655..976d3c08b9 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs @@ -263,6 +263,7 @@ class = "EditorUtilitiesMenu"; item[0] = "Network Graph" TAB "n" TAB "toggleNetGraph();"; item[1] = "Profiler" TAB "ctrl F2" TAB "showMetrics(true);"; + item[2] = "Bake Selected to Mesh" TAB "" TAB "bakeSelectedToMesh();"; }; %this.menuBar.insert(%toolsMenu, %this.menuBar.getCount()); From eb2d3a908a57b2bba088eeac0582dce8536dc3d8 Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 11 Dec 2016 23:02:13 -0600 Subject: [PATCH 2/2] Tweaked the naming convention to a) be more in line with the prefab creation functions, and b) avoid common artist terminologies that may lead to confusion on what it does. --- Engine/source/gui/worldEditor/worldEditor.cpp | 10 +++++----- Engine/source/gui/worldEditor/worldEditor.h | 2 +- .../game/tools/worldEditor/scripts/menuHandlers.ed.cs | 4 ++-- .../Full/game/tools/worldEditor/scripts/menus.ed.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index 087256f31c..7708feb793 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -3753,18 +3753,18 @@ void WorldEditor::explodeSelectedPrefab() setDirty(); } -void WorldEditor::bakeSelectionToMesh(const char *filename) +void WorldEditor::makeSelectionAMesh(const char *filename) { if (mSelected->size() == 0) { - Con::errorf("WorldEditor::makeSelectionPrefab - Nothing selected."); + Con::errorf("WorldEditor::makeSelectionAMesh - Nothing selected."); return; } SimGroup *missionGroup; if (!Sim::findObject("MissionGroup", missionGroup)) { - Con::errorf("WorldEditor::makeSelectionPrefab - Could not find MissionGroup."); + Con::errorf("WorldEditor::makeSelectionAMesh - Could not find MissionGroup."); return; } @@ -3918,11 +3918,11 @@ DefineEngineMethod( WorldEditor, explodeSelectedPrefab, void, (),, object->explodeSelectedPrefab(); } -DefineEngineMethod(WorldEditor, bakeSelectionToMesh, void, (const char* filename), , +DefineEngineMethod(WorldEditor, makeSelectionAMesh, void, (const char* filename), , "Save selected objects to a .dae collada file and replace them in the level with a TSStatic object." "@param filename collada file to save the selected objects to.") { - object->bakeSelectionToMesh(filename); + object->makeSelectionAMesh(filename); } DefineEngineMethod( WorldEditor, mountRelative, void, ( SceneObject *objA, SceneObject *objB ),, diff --git a/Engine/source/gui/worldEditor/worldEditor.h b/Engine/source/gui/worldEditor/worldEditor.h index 35290ca408..f1c231ee96 100644 --- a/Engine/source/gui/worldEditor/worldEditor.h +++ b/Engine/source/gui/worldEditor/worldEditor.h @@ -117,7 +117,7 @@ class WorldEditor : public EditTSCtrl void makeSelectionPrefab( const char *filename ); void explodeSelectedPrefab(); - void bakeSelectionToMesh(const char *filename); + void makeSelectionAMesh(const char *filename); // static SceneObject* getClientObj(SceneObject *); diff --git a/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs index 1823e0d550..f476ccaebc 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/menuHandlers.ed.cs @@ -555,7 +555,7 @@ function EditorExplodePrefab() EditorTree.buildVisibleTree( true ); } -function bakeSelectedToMesh() +function makeSelectedAMesh() { %dlg = new SaveFileDialog() @@ -582,7 +582,7 @@ function bakeSelectedToMesh() if ( !%ret ) return; - EWorldEditor.bakeSelectionToMesh( %saveFile ); + EWorldEditor.makeSelectionAMesh( %saveFile ); EditorTree.buildVisibleTree( true ); } diff --git a/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs index 976d3c08b9..0558d93db5 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs @@ -263,7 +263,7 @@ class = "EditorUtilitiesMenu"; item[0] = "Network Graph" TAB "n" TAB "toggleNetGraph();"; item[1] = "Profiler" TAB "ctrl F2" TAB "showMetrics(true);"; - item[2] = "Bake Selected to Mesh" TAB "" TAB "bakeSelectedToMesh();"; + item[2] = "Make Selected a Mesh" TAB "" TAB "makeSelectedAMesh();"; }; %this.menuBar.insert(%toolsMenu, %this.menuBar.getCount());