From e2d0cc19819635ea321b63c90bb1dbae90c9e8cf Mon Sep 17 00:00:00 2001 From: JeffR Date: Tue, 5 Nov 2024 20:14:36 -0600 Subject: [PATCH] Fixes issue where Regenerate Bounds button for SceneGroup/SubScenes wasn't displaying by moving it to Editing inspector group Added mode toggle for if changing the transform influences the child objects of a SubScene or not Added onSelected/onUnselected callbacks for SimObjects to allow contextual behavior in the editor Added functionality of programmatic/dynamic Tool Button Palettes Added logic so when selecting SubScenes the world editor palette has new buttons for letting the move/rotate actions influence the child objects --- Engine/source/T3D/SceneGroup.cpp | 2 +- Engine/source/T3D/SubScene.cpp | 30 ++++ Engine/source/T3D/SubScene.h | 7 + Engine/source/console/simObject.cpp | 12 ++ Engine/source/console/simObject.h | 3 + .../game/tools/levels/DefaultEditorLevel.mis | 38 ++--- .../worldEditor/gui/ToolsPaletteWindow.ed.gui | 2 +- .../game/tools/worldEditor/main.tscript | 3 + .../worldEditor/scripts/EditorGui.ed.tscript | 27 ++- .../worldEditor/scripts/buttonPalette.tscript | 160 ++++++++++++++++++ .../interfaces/subSceneEditing.tscript | 59 +++++++ 11 files changed, 318 insertions(+), 25 deletions(-) create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/buttonPalette.tscript create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/subSceneEditing.tscript diff --git a/Engine/source/T3D/SceneGroup.cpp b/Engine/source/T3D/SceneGroup.cpp index 5dc0e6fecd..08195b976e 100644 --- a/Engine/source/T3D/SceneGroup.cpp +++ b/Engine/source/T3D/SceneGroup.cpp @@ -119,7 +119,7 @@ void SceneGroup::onInspect(GuiInspector* inspector) Parent::onInspect(inspector); //Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes - GuiInspectorGroup* sceneGroupGrp = inspector->findExistentGroup(StringTable->insert("SceneGroup")); + GuiInspectorGroup* sceneGroupGrp = inspector->findExistentGroup(StringTable->insert("Editing")); if (!sceneGroupGrp) return; diff --git a/Engine/source/T3D/SubScene.cpp b/Engine/source/T3D/SubScene.cpp index 371131a377..08e2941f00 100644 --- a/Engine/source/T3D/SubScene.cpp +++ b/Engine/source/T3D/SubScene.cpp @@ -10,6 +10,8 @@ #include "gui/editor/inspector/group.h" #include "T3D/gameBase/gameBase.h" +bool SubScene::smTransformChildren = false; + IMPLEMENT_CO_NETOBJECT_V1(SubScene); S32 SubScene::mUnloadTimeoutMs = 5000; @@ -86,6 +88,10 @@ void SubScene::consoleInit() Con::addVariable("$SubScene::UnloadTimeoutMS", TypeBool, &SubScene::mUnloadTimeoutMs, "The amount of time in milliseconds it takes for a SubScene to be unloaded if it's inactive.\n" "@ingroup Editors\n"); + + Con::addVariable("$SubScene::transformChildren", TypeBool, &SubScene::smTransformChildren, + "@brief If true, then transform manipulations modify child objects. If false, only triggering bounds is manipulated\n\n" + "@ingroup Editors"); } void SubScene::addObject(SimObject* object) @@ -163,6 +169,30 @@ void SubScene::inspectPostApply() setMaskBits(-1); } +void SubScene::setTransform(const MatrixF& mat) +{ + if(SubScene::smTransformChildren) + { + Parent::setTransform(mat); + } + else + { + SceneObject::setTransform(mat); + } +} + +void SubScene::setRenderTransform(const MatrixF& mat) +{ + if (SubScene::smTransformChildren) + { + Parent::setRenderTransform(mat); + } + else + { + SceneObject::setRenderTransform(mat); + } +} + bool SubScene::evaluateCondition() { if (!mLoadIf.isEmpty()) diff --git a/Engine/source/T3D/SubScene.h b/Engine/source/T3D/SubScene.h index e70ae812b5..4117e40c11 100644 --- a/Engine/source/T3D/SubScene.h +++ b/Engine/source/T3D/SubScene.h @@ -23,6 +23,9 @@ class SubScene : public SceneGroup void onLevelChanged() {} +protected: + static bool smTransformChildren; + private: DECLARE_LEVELASSET(SubScene, Level, onLevelChanged); @@ -47,6 +50,7 @@ class SubScene : public SceneGroup U32 mCurrTick; bool mGlobalLayer; + public: SubScene(); virtual ~SubScene(); @@ -71,6 +75,9 @@ class SubScene : public SceneGroup //void onEditorDisable() override; void inspectPostApply() override; + void setTransform(const MatrixF& mat) override; + void setRenderTransform(const MatrixF& mat) override; + bool testBox(const Box3F& testBox); bool evaluateCondition(); void _onSelected() override; diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 7a9bb4404b..b1863d5bd1 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -101,6 +101,8 @@ SimObjectId SimObject::smForcedId = 0; bool SimObject::preventNameChanging = false; IMPLEMENT_CALLBACK(SimObject, onInspectPostApply, void, (SimObject* obj), (obj), "Generic callback for when an object is edited"); +IMPLEMENT_CALLBACK(SimObject, onSelected, void, (SimObject* obj), (obj), "Generic callback for when an object is selected"); +IMPLEMENT_CALLBACK(SimObject, onUnselected, void, (SimObject* obj), (obj), "Generic callback for when an object is un-selected"); namespace Sim { @@ -527,6 +529,14 @@ bool SimObject::save(const char *pcFileName, bool bOnlySelected, const char *pre } +bool SimObject::saveAppend(const char* pcFileName, bool bOnlySelected, const char* preappend) +{ + + + return true; + +} + //----------------------------------------------------------------------------- SimPersistID* SimObject::getOrCreatePersistentId() @@ -2207,11 +2217,13 @@ void SimObject::setSelected( bool sel ) { mFlags.set( Selected ); _onSelected(); + onSelected_callback(this); } else { mFlags.clear( Selected ); _onUnselected(); + onUnselected_callback(this); } } diff --git a/Engine/source/console/simObject.h b/Engine/source/console/simObject.h index 563f86e12d..77f6cd1482 100644 --- a/Engine/source/console/simObject.h +++ b/Engine/source/console/simObject.h @@ -579,6 +579,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks /// Save object as a TorqueScript File. virtual bool save( const char* pcFilePath, bool bOnlySelected = false, const char *preappend = NULL ); + virtual bool saveAppend(const char* pcFilePath, bool bOnlySelected = false, const char* preappend = NULL); /// Check if a method exists in the objects current namespace. virtual bool isMethod( const char* methodName ); @@ -981,6 +982,8 @@ class SimObject: public ConsoleObject, public TamlCallbacks DECLARE_CONOBJECT( SimObject ); DECLARE_CALLBACK(void, onInspectPostApply, (SimObject* obj)); + DECLARE_CALLBACK(void, onSelected, (SimObject* obj)); + DECLARE_CALLBACK(void, onUnselected, (SimObject* obj)); static SimObject* __findObject( const char* id ) { return Sim::findObject( id ); } static const char* __getObjectId( ConsoleObject* object ) diff --git a/Templates/BaseGame/game/tools/levels/DefaultEditorLevel.mis b/Templates/BaseGame/game/tools/levels/DefaultEditorLevel.mis index 466e48cc79..f54cb5693c 100644 --- a/Templates/BaseGame/game/tools/levels/DefaultEditorLevel.mis +++ b/Templates/BaseGame/game/tools/levels/DefaultEditorLevel.mis @@ -1,33 +1,20 @@ //--- OBJECT WRITE BEGIN --- new Scene(EditorTemplateLevel) { - canSave = "1"; - canSaveDynamicFields = "1"; - Enabled = "1"; + isEditing = "1"; + enabled = "1"; new LevelInfo(theLevelInfo) { - nearClip = "0.1"; - visibleDistance = "1000"; - visibleGhostDistance = "0"; - decalBias = "0.0015"; - fogColor = "0.6 0.6 0.7 1"; - fogDensity = "0"; + FogColor = "0.6 0.6 0.7 1"; fogDensityOffset = "700"; - fogAtmosphereHeight = "0"; canvasClearColor = "0 0 0 255"; - ambientLightBlendPhase = "1"; - ambientLightBlendCurve = "0 0 -1 -1"; soundAmbience = "AudioAmbienceDefault"; - soundDistanceModel = "Linear"; - canSave = "1"; - canSaveDynamicFields = "1"; - Enabled = "1"; + enabled = "1"; }; new ScatterSky(DynamicSky) { sunScale = "0.991102 0.921582 0.83077 1"; zOffset = "-3000"; azimuth = "25"; brightness = "5"; - flareType = "LightFlareExample1"; MoonMatAsset = "Core_Rendering:moon_wglow"; useNightCubemap = "1"; nightCubemap = "nightCubemap"; @@ -44,16 +31,29 @@ new Scene(EditorTemplateLevel) { persistentId = "289ad401-3140-11ed-aae8-c0cb519281fc"; reflectionPath = "tools/levels/DefaultEditorLevel/probes/"; }; - new GroundPlane() { scaleU = "32"; scaleV = "32"; MaterialAsset = "Prototyping:FloorGray"; - Enabled = "1"; + enabled = "1"; position = "0 0 0"; rotation = "1 0 0 0"; scale = "1 1 1"; }; + new SubScene() { + LevelAsset = "Prototyping:PrefabTestSubScene"; + position = "4.38205 -5.66842 1.53303"; + scale = "33.0705 24.1137 4.59909"; + }; + new Trigger() { + dataBlock = "DefaultTrigger"; + position = "0 0 -7.19786"; + scale = "15.3957 15.3957 15.3957"; + firstDataCheck = "1"; + }; + new Prefab() { + fileName = "data/Prototyping/prefabs/testPrefab.prefab"; + }; }; //--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui index 79f5f2a295..b7732d6294 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui @@ -42,7 +42,7 @@ $guiContent = new GuiControl() { minSize = "50 50"; EdgeSnap = false; text = ""; - class = "EWToolsPaletteWindowClass"; + class = "ButtonPalette"; new GuiDynamicCtrlArrayControl(ToolsPaletteArray) { canSaveDynamicFields = "0"; diff --git a/Templates/BaseGame/game/tools/worldEditor/main.tscript b/Templates/BaseGame/game/tools/worldEditor/main.tscript index ef0dfdc050..c3a3e88add 100644 --- a/Templates/BaseGame/game/tools/worldEditor/main.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/main.tscript @@ -69,6 +69,7 @@ function initializeWorldEditor() exec("./scripts/probeBake.ed." @ $TorqueScriptFileExtension); exec("./scripts/visibility/visibilityLayer.ed." @ $TorqueScriptFileExtension); exec("./scripts/visibility/probeViz." @ $TorqueScriptFileExtension); + exec("./scripts/buttonPalette." @ $TorqueScriptFileExtension); exec("tools/gui/postFxEditor." @ $TorqueScriptFileExtension ); exec("tools/gui/renderTargetVisualizer.ed." @ $TorqueScriptFileExtension); @@ -77,6 +78,8 @@ function initializeWorldEditor() loadDirectory(expandFilename("./scripts/editors")); loadDirectory(expandFilename("./scripts/interfaces")); + exec("./scripts/interfaces/subSceneEditing.tscript"); + // Create the default editor plugins before calling buildMenus. new ScriptObject( WorldEditorPlugin ) diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript index f54b8b43d5..fecc479c6c 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript @@ -1069,11 +1069,13 @@ function WorldEditorInspectorPlugin::onWorldEditorStartup( %this ) //connect editor windows GuiWindowCtrl::attach( EWInspectorWindow, EWTreeWindow); - %map = new ActionMap(); + %map = new ActionMap(); + + /* %map.bindCmd( keyboard, "1", "EWorldEditorNoneModeBtn.performClick();", "" ); // Select %map.bindCmd( keyboard, "2", "EWorldEditorMoveModeBtn.performClick();", "" ); // Move %map.bindCmd( keyboard, "3", "EWorldEditorRotateModeBtn.performClick();", "" ); // Rotate - %map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale + %map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale*/ %map.bindCmd( keyboard, "f", "FitToSelectionBtn.performClick();", "" );// Fit Camera to Selection %map.bindCmd( keyboard, "z", "EditorGuiStatusBar.setCamera(\"Standard Camera\");", "" );// Free camera %map.bindCmd( keyboard, "n", "ToggleNodeBar->renderHandleBtn.performClick();", "" );// Render Node @@ -1093,21 +1095,38 @@ function WorldEditorInspectorPlugin::onWorldEditorStartup( %this ) function WorldEditorInspectorPlugin::onActivated( %this ) { Parent::onActivated( %this ); + + //Clears the button pallete stack + EWToolsPaletteWindow.setStackCtrl(ToolsPaletteArray); //legacy ctrl adhereance + EWToolsPaletteWindow.clearButtons(); + + EWToolsPaletteWindow.setActionMap(WorldEditorInspectorPlugin.map); + + //Adds a button to the pallete stack + //Name Icon Click Command Tooltip text Keybind + EWToolsPaletteWindow.addButton("Select", "ToolsModule:arrow_n_image", "EWorldEditorNoneModeBtn::onClick();", "", "Select Arrow", "1"); + EWToolsPaletteWindow.addButton("Move", "ToolsModule:translate_n_image", "EWorldEditorMoveModeBtn::onClick();", "", "Move Selection", "2"); + EWToolsPaletteWindow.addButton("Rotate", "ToolsModule:rotate_n_image", "EWorldEditorRotateModeBtn::onClick();", "", "Rotate Selection", "3"); + EWToolsPaletteWindow.addButton("Scale", "ToolsModule:Scale_n_image", "EWorldEditorScaleModeBtn::onClick();", "", "Scale Selection", "4"); + + EWToolsPaletteWindow.refresh(); EditorGui-->InspectorWindow.setVisible( true ); EditorGui-->TreeWindow.setVisible( true ); EditorGui-->WorldEditorToolbar.setVisible( true ); - %this.map.push(); + //%this.map.push(); } function WorldEditorInspectorPlugin::onDeactivated( %this ) { Parent::onDeactivated( %this ); + + EWToolsPaletteWindow.popActionMap(); EditorGui-->InspectorWindow.setVisible( false ); EditorGui-->TreeWindow.setVisible( false ); EditorGui-->WorldEditorToolbar.setVisible( false ); - %this.map.pop(); + //%this.map.pop(); } function WorldEditorInspectorPlugin::onEditMenuSelect( %this, %editMenu ) diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/buttonPalette.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/buttonPalette.tscript new file mode 100644 index 0000000000..f401f939d6 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/buttonPalette.tscript @@ -0,0 +1,160 @@ +function ButtonPalette::setStackCtrl(%this, %ctrl) +{ + %this.stackCtrl = %ctrl; +} + +function ButtonPalette::getStackCtrl(%this) +{ + if(isObject(%this.stackCtrl)) + return %this.stackCtrl; + else + return %this; +} + +function ButtonPalette::setActionMap(%this, %actionMap) +{ + %this.actionMap = %actionMap; + %this.actionMap.push(); +} + +function ButtonPalette::getActionMap(%this) +{ + return %this.actionMap; +} + +function ButtonPalette::popActionMap(%this, %actionMap) +{ + %this.actionMap.pop(); +} + +function ButtonPalette::clearButtons(%this) +{ + %stackCtrl = %this.getStackCtrl(); + %stackCtrl.clear(); + + for(%i=0; %i < %this.numButtons; %i++) + { + if(isObject(%this.actionMap)) + { + %this.actionMap.unbind("keyboard", getField(%this.buttons[%i], 5)); + } + + %this.buttons[%i] = ""; + } + + %this.numButtons = 0; +} + + //Name, Icon, Click Command, Variable, Tooltip text, Keybind +function ButtonPalette::addButton(%this, %name, %iconAsset, %command, %syncVariable, %toolTip, %keybind) +{ + if(%this.numButtons $= "") + %this.numButtons = 0; + + for(%i=0; %i < %this.numButtons; %i++) + { + %buttonInfo = %this.buttons[%i]; + + //echo("Testing button info: " @ getField(%buttonInfo, 0) @ " against new button name: " @ %name); + if(getField(%buttonInfo, 0) $= %name) //no duplicates + return; + } + + %this.buttons[%this.numButtons] = %name TAB %iconAsset TAB %command TAB %syncVariable TAB %toolTip TAB %keybind; + + %this.numButtons++; +} + +function ButtonPalette::removeButton(%this, %name) +{ + %found = false; + for(%i=0; %i < %this.numButtons; %i++) + { + if(getField(%this.buttons[%i], 0) $= %name) + { + %found = true; + + if(isObject(%this.actionMap)) + { + %this.actionMap.unbind("keyboard", getField(%this.buttons[%i], 5)); + } + } + + if(%found) + { + %this.buttons[%i] = %this.buttons[%i+1]; + } + } + + if(%found) + %this.numButtons--; + + return %found; +} + +function ButtonPalette::findButton(%this, %name) +{ + %stackCtrl = %this.getStackCtrl(); + for(%i=0; %i < %stackCtrl.getCount(); %i++) + { + %btnObj = %stackCtrl.getObject(%i); + + if(%btnObj.buttonName $= %name) + return %btnObj; + } + + return 0; +} + +function ButtonPalette::refresh(%this) +{ + %stackCtrl = %this.getStackCtrl(); + %stackCtrl.clear(); + + %this.visible = true; + %extents = "25 25"; + + if(%this.numButtons == 0) + { + %this.visible = false; + return; + } + + for(%i=0; %i < %this.numButtons; %i++) + { + %buttonInfo = %this.buttons[%i]; + + %paletteButton = new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = getField(%buttonInfo, 0) @ "_paletteButton"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = getField(%buttonInfo, 4) SPC "(" @ getField(%buttonInfo, 5) @ ")"; + hovertime = "1000"; + bitmapAsset = getField(%buttonInfo, 1); + buttonType = "RadioButton"; + useMouseEvents = "0"; + buttonName = getField(%buttonInfo, 0); + command = getField(%buttonInfo, 2); + variable = getField(%buttonInfo, 3); + }; + + %extents.y += 23; + + if(isObject(%this.actionMap)) + %this.actionMap.bindCmd( keyboard, getField(%buttonInfo, 5), %paletteButton @ ".performClick();", "" ); + + %stackCtrl.add(%paletteButton); + } + + %this.extent.y = %extents.y; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/subSceneEditing.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/subSceneEditing.tscript new file mode 100644 index 0000000000..435f88b4d0 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/subSceneEditing.tscript @@ -0,0 +1,59 @@ +function SubScene::onSelected(%this) +{ + echo("SELECTED SUBSCENE"); + + %moveButton = EWToolsPaletteWindow.findButton("Move"); + %rotateButton = EWToolsPaletteWindow.findButton("Rotate"); + + %moveButton.originalCommand = %moveButton.command; + %moveButton.command = "SubSceneMoveModeBtn::onClick();"; + + %rotateButton.originalCommand = %rotateButton.command; + %rotateButton.command = "SubSceneRotateModeBtn::onClick();"; + + EWToolsPaletteWindow.addButton("SubSceneMove", "ToolsModule:translate_n_image", "SubSceneChildMoveModeBtn::onClick();", "", "Move SubScene + Children", "1"); + EWToolsPaletteWindow.addButton("SubSceneRotate", "ToolsModule:rotate_n_image", "SubSceneChildRotateModeBtn::onClick();", "", "Rotate SubScene + Children", "2"); + + EWToolsPaletteWindow.refresh(); +} + +function SubScene::onUnselected(%this) +{ + echo("UN-SELECTED SUBSCENE"); + EWToolsPaletteWindow.removeButton("SubSceneMove"); + EWToolsPaletteWindow.removeButton("SubSceneRotate"); + + %moveButton = EWToolsPaletteWindow.findButton("Move"); + %rotateButton = EWToolsPaletteWindow.findButton("Rotate"); + + %moveButton.command = %moveButton.originalCommand; + %rotateButton.command = %rotateButton.originalCommand; + + $SubScene::transformChildren = false; + + EWToolsPaletteWindow.refresh(); +} + +function SubSceneMoveModeBtn::onClick(%this) +{ + EWorldEditorMoveModeBtn::onClick(); + $SubScene::transformChildren = false; +} + +function SubSceneRotateModeBtn::onClick(%this) +{ + EWorldEditorRotateModeBtn::onClick(); + $SubScene::transformChildren = false; +} + +function SubSceneChildMoveModeBtn::onClick() +{ + EWorldEditorMoveModeBtn::onClick(); + $SubScene::transformChildren = true; +} + +function SubSceneChildRotateModeBtn::onClick() +{ + EWorldEditorRotateModeBtn::onClick(); + $SubScene::transformChildren = true; +} \ No newline at end of file