Updated handling of subscenes in assets to be it's own distinct definition to avoid parsing and detection issues, as well as fields to be handled distinctly between the types

This commit is contained in:
JeffR 2025-03-30 16:36:15 -05:00
parent c2af4e578b
commit 5566f8a396
9 changed files with 469 additions and 47 deletions

View file

@ -24,7 +24,7 @@ IMPLEMENT_CALLBACK(SubScene, onUnloaded, void, (), (),
"@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
SubScene::SubScene() :
mLevelAssetId(StringTable->EmptyString()),
mSubSceneAssetId(StringTable->EmptyString()),
mGameModesNames(StringTable->EmptyString()),
mScopeDistance(-1),
mLoaded(false),
@ -67,7 +67,7 @@ void SubScene::initPersistFields()
{
addGroup("SubScene");
addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), "");
INITPERSISTFIELD_LEVELASSET(Level, SubScene, "The level asset to load.");
INITPERSISTFIELD_SUBSCENEASSET(SubScene, SubScene, "The subscene asset to load.");
addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)");
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
endGroup("SubScene");
@ -265,13 +265,13 @@ void SubScene::processTick(const Move* move)
void SubScene::_onFileChanged(const Torque::Path& path)
{
if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path)
if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path)
return;
if (mSaving)
return;
AssertFatal(path == mLevelAsset->getLevelPath(), "Prefab::_onFileChanged - path does not match filename.");
AssertFatal(path == mSubSceneAsset->getLevelPath(), "SubScene::_onFileChanged - path does not match filename.");
_closeFile(false);
_loadFile(false);
@ -307,9 +307,9 @@ void SubScene::_closeFile(bool removeFileNotify)
_removeContents(SimGroupIterator(this));
if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString())
if (removeFileNotify && mSubSceneAsset.notNull() && mSubSceneAsset->getLevelPath() != StringTable->EmptyString())
{
Torque::FS::RemoveChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
Torque::FS::RemoveChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged);
}
mGameModesList.clear();
@ -319,18 +319,18 @@ void SubScene::_loadFile(bool addFileNotify)
{
AssertFatal(isServerObject(), "Trying to load a SubScene file on the client is bad!");
if(mLevelAsset.isNull() || mLevelAsset->getLevelPath() == StringTable->EmptyString())
if(mSubSceneAsset.isNull() || mSubSceneAsset->getLevelPath() == StringTable->EmptyString())
return;
String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath());
String evalCmd = String::ToString("exec(\"%s\");", mSubSceneAsset->getLevelPath());
String instantGroup = Con::getVariable("InstantGroup");
Con::setIntVariable("InstantGroup", this->getId());
Con::evaluate((const char*)evalCmd.c_str(), false, mLevelAsset->getLevelPath());
Con::evaluate((const char*)evalCmd.c_str(), false, mSubSceneAsset->getLevelPath());
Con::setVariable("InstantGroup", instantGroup.c_str());
if (addFileNotify)
Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
Torque::FS::AddChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged);
}
void SubScene::load()
@ -432,7 +432,7 @@ bool SubScene::save()
if (size() == 0 || !isLoaded())
return false;
if (mLevelAsset.isNull())
if (mSubSceneAsset.isNull())
return false;
if (mSaving)
@ -446,7 +446,7 @@ bool SubScene::save()
PersistenceManager prMger;
StringTableEntry levelPath = mLevelAsset->getLevelPath();
StringTableEntry levelPath = mSubSceneAsset->getLevelPath();
FileStream fs;
fs.open(levelPath, Torque::FS::File::Write);
@ -460,7 +460,7 @@ bool SubScene::save()
{
if ((*itr)->isMethod("onSaving"))
{
Con::executef((*itr), "onSaving", mLevelAssetId);
Con::executef((*itr), "onSaving", mSubSceneAssetId);
}
if (childObj->getGroup() == this)
@ -481,14 +481,14 @@ bool SubScene::save()
bool saveSuccess = false;
//Get the level asset
if (mLevelAsset.isNull())
if (mSubSceneAsset.isNull())
return saveSuccess;
//update the gamemode list as well
mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
mSubSceneAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
//Finally, save
saveSuccess = mLevelAsset->saveAsset();
saveSuccess = mSubSceneAsset->saveAsset();
mSaving = false;

View file

@ -5,8 +5,8 @@
#ifndef SCENE_GROUP_H
#include "SceneGroup.h"
#endif
#ifndef LEVEL_ASSET_H
#include "assets/LevelAsset.h"
#ifndef SUBSCENE_ASSET_H
#include "assets/SubSceneAsset.h"
#endif
class GameMode;
@ -21,13 +21,13 @@ public:
NextFreeMask = Parent::NextFreeMask << 0
};
void onLevelChanged() {}
void onSubSceneChanged() {}
protected:
static bool smTransformChildren;
private:
DECLARE_LEVELASSET(SubScene, Level, onLevelChanged);
DECLARE_SUBSCENEASSET(SubScene, SubScene, onSubSceneChanged);
StringTableEntry mGameModesNames;
Vector<GameMode*> mGameModesList;
@ -61,7 +61,7 @@ public:
static void initPersistFields();
static void consoleInit();
StringTableEntry getTypeHint() const override { return (getLevelAsset()) ? getLevelAsset()->getAssetName() : StringTable->EmptyString(); }
StringTableEntry getTypeHint() const override { return (getSubSceneAsset()) ? getSubSceneAsset()->getAssetName() : StringTable->EmptyString(); }
// SimObject
bool onAdd() override;
@ -122,6 +122,6 @@ public:
DECLARE_CALLBACK(void, onLoaded, ());
DECLARE_CALLBACK(void, onUnloaded, ());
DECLARE_ASSET_SETGET(SubScene, Level);
DECLARE_ASSET_SETGET(SubScene, SubScene);
};
#endif

View file

@ -101,7 +101,7 @@ ConsoleSetType(TypeLevelAssetId)
}
//-----------------------------------------------------------------------------
LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
LevelAsset::LevelAsset() : AssetBase()
{
mLevelName = StringTable->EmptyString();
mLevelFile = StringTable->EmptyString();
@ -117,7 +117,6 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
mNavmeshPath = StringTable->EmptyString();
mGameModesNames = StringTable->EmptyString();
mMainLevelAsset = StringTable->EmptyString();
mEditorFile = StringTable->EmptyString();
mBakedSceneFile = StringTable->EmptyString();
@ -158,7 +157,6 @@ void LevelAsset::initPersistFields()
addProtectedField("BakedSceneFile", TypeAssetLooseFilePath, Offset(mBakedSceneFile, LevelAsset),
&setBakedSceneFile, &getBakedSceneFile, "Path to the level file with the objects generated as part of the baking process");
addField("isSubScene", TypeBool, Offset(mIsSubLevel, LevelAsset), "Is this a sublevel to another Scene");
addField("gameModesNames", TypeString, Offset(mGameModesNames, LevelAsset), "Name of the Game Mode to be used with this level");
}
@ -480,15 +478,8 @@ GuiControl* GuiInspectorTypeLevelAssetPtr::constructEditControl()
// Create "Open in Editor" button
mEditButton = new GuiBitmapButtonCtrl();
String setSubSceneValue = "$createLevelAssetIsSubScene = \"\";";
if(dynamic_cast<SubScene*>(mInspector->getInspectObject()) != NULL)
{
setSubSceneValue = "$createLevelAssetIsSubScene = true;";
}
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; %s AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
getIdString(),
setSubSceneValue.c_str());
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
getIdString());
mEditButton->setField("Command", szBuffer);
char bitmapName[512] = "ToolsModule:iconAdd_image";

View file

@ -66,9 +66,6 @@ class LevelAsset : public AssetBase
StringTableEntry mEditorFile;
StringTableEntry mBakedSceneFile;
bool mIsSubLevel;
StringTableEntry mMainLevelAsset;
StringTableEntry mGameModesNames;
Vector<AssetBase*> mAssetDependencies;

View file

@ -0,0 +1,166 @@
#include "SubSceneAsset.h"
#include "T3D/SubScene.h"
IMPLEMENT_CONOBJECT(SubSceneAsset);
ConsoleType(SubSceneAssetPtr, TypeSubSceneAssetPtr, const char*, "")
//-----------------------------------------------------------------------------
ConsoleGetType(TypeSubSceneAssetPtr)
{
// Fetch asset Id.
return *((const char**)(dptr));
}
//-----------------------------------------------------------------------------
ConsoleSetType(TypeSubSceneAssetPtr)
{
// Was a single argument specified?
if (argc == 1)
{
// Yes, so fetch field value.
*((const char**)dptr) = StringTable->insert(argv[0]);
return;
}
// Warn.
Con::warnf("(TypeSubSceneAssetPtr) - Cannot set multiple args to a single asset.");
}
//-----------------------------------------------------------------------------
ConsoleType(assetIdString, TypeSubSceneAssetId, const char*, "")
ConsoleGetType(TypeSubSceneAssetId)
{
// Fetch asset Id.
return *((const char**)(dptr));
}
ConsoleSetType(TypeSubSceneAssetId)
{
// Was a single argument specified?
if (argc == 1)
{
*((const char**)dptr) = StringTable->insert(argv[0]);
return;
}
// Warn.
Con::warnf("(TypeSubSceneAssetId) - Cannot set multiple args to a single asset.");
}
SubSceneAsset::SubSceneAsset() : LevelAsset()
{
}
SubSceneAsset::~SubSceneAsset()
{
}
void SubSceneAsset::initPersistFields()
{
docsURL;
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// GuiInspectorTypeAssetId
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiInspectorTypeSubSceneAssetPtr);
ConsoleDocClass(GuiInspectorTypeSubSceneAssetPtr,
"@brief Inspector field type for Shapes\n\n"
"Editor use only.\n\n"
"@internal"
);
void GuiInspectorTypeSubSceneAssetPtr::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeSubSceneAssetPtr)->setInspectorFieldType("GuiInspectorTypeSubSceneAssetPtr");
}
GuiControl* GuiInspectorTypeSubSceneAssetPtr::constructEditControl()
{
// Create base filename edit controls
GuiControl* retCtrl = Parent::constructEditControl();
if (retCtrl == NULL)
return retCtrl;
// Change filespec
char szBuffer[512];
dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"SubSceneAsset\", \"AssetBrowser.changeAsset\", %s, \"\");",
getIdString());
mBrowseButton->setField("Command", szBuffer);
setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString());
// Create "Open in Editor" button
mEditButton = new GuiBitmapButtonCtrl();
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"SubSceneAsset\", AssetBrowser.selectedModule);",
getIdString());
mEditButton->setField("Command", szBuffer);
char bitmapName[512] = "ToolsModule:iconAdd_image";
mEditButton->setBitmap(StringTable->insert(bitmapName));
mEditButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
mEditButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
mEditButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
mEditButton->setDataField(StringTable->insert("tooltip"), NULL, "Test play this sound");
mEditButton->registerObject();
addObject(mEditButton);
return retCtrl;
}
bool GuiInspectorTypeSubSceneAssetPtr::updateRects()
{
S32 dividerPos, dividerMargin;
mInspector->getDivider(dividerPos, dividerMargin);
Point2I fieldExtent = getExtent();
Point2I fieldPos = getPosition();
mCaptionRect.set(0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
mEditCtrlRect.set(fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 34, fieldExtent.y);
bool resized = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
if (mBrowseButton != NULL)
{
mBrowseRect.set(fieldExtent.x - 32, 2, 14, fieldExtent.y - 4);
resized |= mBrowseButton->resize(mBrowseRect.point, mBrowseRect.extent);
}
if (mEditButton != NULL)
{
RectI shapeEdRect(fieldExtent.x - 16, 2, 14, fieldExtent.y - 4);
resized |= mEditButton->resize(shapeEdRect.point, shapeEdRect.extent);
}
return resized;
}
IMPLEMENT_CONOBJECT(GuiInspectorTypeSubSceneAssetId);
ConsoleDocClass(GuiInspectorTypeSubSceneAssetId,
"@brief Inspector field type for SubScene\n\n"
"Editor use only.\n\n"
"@internal"
);
void GuiInspectorTypeSubSceneAssetId::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeSubSceneAssetId)->setInspectorFieldType("GuiInspectorTypeSubSceneAssetId");
}

View file

@ -0,0 +1,113 @@
#pragma once
#ifndef SUBSCENE_ASSET_H
#define SUBSCENE_ASSET_H
#ifndef LEVEL_ASSET_H
#include "LevelAsset.h"
#endif
#ifndef _ASSET_DEFINITION_H_
#include "assets/assetDefinition.h"
#endif
#ifndef _GUI_INSPECTOR_TYPES_H_
#include "gui/editor/guiInspectorTypes.h"
#endif
class SubSceneAsset : public LevelAsset
{
typedef LevelAsset Parent;
public:
SubSceneAsset();
virtual ~SubSceneAsset();
/// Engine.
static void initPersistFields();
/// Declare Console Object.
DECLARE_CONOBJECT(SubSceneAsset);
};
#ifdef TORQUE_TOOLS
class GuiInspectorTypeSubSceneAssetPtr : public GuiInspectorTypeFileName
{
typedef GuiInspectorTypeFileName Parent;
public:
GuiBitmapButtonCtrl* mEditButton;
DECLARE_CONOBJECT(GuiInspectorTypeSubSceneAssetPtr);
static void consoleInit();
GuiControl* constructEditControl() override;
bool updateRects() override;
};
class GuiInspectorTypeSubSceneAssetId : public GuiInspectorTypeSubSceneAssetPtr
{
typedef GuiInspectorTypeSubSceneAssetPtr Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeSubSceneAssetId);
static void consoleInit();
};
#endif
DefineConsoleType(TypeSubSceneAssetPtr, SubSceneAsset)
DefineConsoleType(TypeSubSceneAssetId, String)
#pragma region Singular Asset Macros
//Singular assets
/// <Summary>
/// Declares an SubScene asset
/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions
/// </Summary>
#define DECLARE_SUBSCENEASSET(className, name, changeFunc) public: \
StringTableEntry m##name##AssetId;\
AssetPtr<SubSceneAsset> m##name##Asset;\
public: \
const AssetPtr<SubSceneAsset> & get##name##Asset() const { return m##name##Asset; }\
void set##name##Asset(const AssetPtr<SubSceneAsset> &_in) { m##name##Asset = _in;}\
\
bool _set##name(StringTableEntry _in)\
{\
if(m##name##AssetId != _in)\
{\
if (m##name##Asset.notNull())\
{\
m##name##Asset->getChangedSignal().remove(this, &className::changeFunc);\
}\
if (_in == NULL || _in == StringTable->EmptyString())\
{\
m##name##AssetId = StringTable->EmptyString();\
m##name##Asset = NULL;\
return true;\
}\
if (AssetDatabase.isDeclaredAsset(_in))\
{\
m##name##AssetId = _in;\
m##name##Asset = _in;\
return true;\
}\
}\
\
if(get##name() == StringTable->EmptyString())\
return true;\
\
return false;\
}\
\
const StringTableEntry get##name() const\
{\
return m##name##AssetId;\
}\
bool name##Valid() {return (get##name() != StringTable->EmptyString() && m##name##Asset->getStatus() == AssetBase::Ok); }
#define INITPERSISTFIELD_SUBSCENEASSET(name, consoleClass, docs) \
addProtectedField(assetText(name, Asset), TypeSubSceneAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
#pragma endregion
#endif // SUBSCENE_ASSET_H

View file

@ -75,6 +75,7 @@ function initializeAssetBrowser()
exec("./scripts/assetTypes/gui." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/image." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/level." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/subScene." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/material." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/postFX." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/script." @ $TorqueScriptFileExtension);

View file

@ -3,17 +3,9 @@ AssetBrowser::registerAssetType("LevelAsset", "Levels");
function LevelAsset::setupCreateNew()
{
NewAssetPropertiesInspector.startGroup("Level");
NewAssetPropertiesInspector.addField("LevelName", "Level Name", "String", "Human-readable name of new level", "", "", $CurrentAssetBrowser.callAssetTypeFunc(%assetType, "setupCreateNew");.newAssetSettings);
NewAssetPropertiesInspector.addField("LevelName", "Level Name", "String", "Human-readable name of new level", "", "", $CurrentAssetBrowser.newAssetSettings);
NewAssetPropertiesInspector.addField("levelPreviewImage", "Level Preview Image", "Image", "Preview Image for the level", "", "", $CurrentAssetBrowser.newAssetSettings);
//If we forcefully set it elsewhere, adhere
if($createLevelAssetIsSubScene == true)
%this.newAssetSettings.isSubScene = true;
else
%this.newAssetSettings.isSubScene = false;
NewAssetPropertiesInspector.addField("isSubScene", "Is SubScene", "bool", "Is this levelAsset intended as a subScene", %this.newAssetSettings.isSubScene, "", %this.newAssetSettings);
NewAssetPropertiesInspector.endGroup();
}
@ -88,11 +80,17 @@ function LevelAsset::onCreateNew(%this)
if(!%addSuccess)
{
error("AssetBrowser::createLevelAsset() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef);
error("LevelAsset::onCreateNew() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef);
}
$CurrentAssetBrowser.refresh();
if(%addSuccess && isObject($createAndAssignField))
{
$createAndAssignField.apply(%moduleName @ ":" @ %assetName);
$createAndAssignField = "";
}
return %tamlpath;
}

View file

@ -0,0 +1,156 @@
AssetBrowser::registerAssetType("SubSceneAsset", "SubScenes");
function SubSceneAsset::setupCreateNew()
{
NewAssetPropertiesInspector.startGroup("SubScene");
NewAssetPropertiesInspector.addField("LevelName", "SubScene Name", "String", "Human-readable name of new subScene", "", "", $CurrentAssetBrowser.newAssetSettings);
NewAssetPropertiesInspector.endGroup();
}
function SubSceneAsset::onCreateNew(%this)
{
%moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName;
%modulePath = "data/" @ %moduleName;
%assetName = $CurrentAssetBrowser.newAssetSettings.assetName;
%assetPath = NewAssetTargetAddress.getText() @ "/";
%misExtension = ".subMis";
%tamlpath = %assetPath @ %assetName @ ".asset.taml";
%levelPath = %assetPath @ %assetName @ %misExtension;
%asset = new SubSceneAsset()
{
AssetName = %assetName;
versionId = 1;
LevelFile = %assetName @ %misExtension;
LevelName = $CurrentAssetBrowser.newAssetSettings.levelName;
AssetDescription = $CurrentAssetBrowser.newAssetSettings.description;
};
TamlWrite(%asset, %tamlpath);
%fileObj = new FileObject();
if (!%fileObj.openForWrite(%levelPath))
{
echo("Unable to write subScene file!");
}
else
{
%fileObj.writeLine("");
%fileObj.close();
%fileObj.delete();
}
%moduleDef = ModuleDatabase.findModule(%moduleName, 1);
%addSuccess = AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath);
if(!%addSuccess)
{
error("SubSceneAsset::onCreateNew() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef);
}
$CurrentAssetBrowser.refresh();
if(%addSuccess && isObject($createAndAssignField))
{
$createAndAssignField.apply(%moduleName @ ":" @ %assetName);
$createAndAssignField = "";
}
return %tamlpath;
}
function SubSceneAsset::buildBrowserElement(%this, %previewData)
{
%previewData.assetName = %this.assetName;
%previewData.assetPath = %this.getLevelPath();
%previewData.doubleClickCommand = "AssetBrowser.selectAsset(" @ %this @ ");";
%levelPreviewImage = %this.PreviewImage;
if(isFile(%levelPreviewImage))
%previewData.previewImage = %levelPreviewImage;
else
%previewData.previewImage = "ToolsModule:levelIcon_image";
%previewData.assetFriendlyName = %this.assetName;
%previewData.assetDesc = %this.description;
%previewData.tooltip = "Asset Name: " @ %this.assetName @ "\n" @
"Asset Type: SubScene Asset\n" @
"Asset Definition ID: " @ %this @ "\n" @
"SubScene File path: " @ %this.getLevelPath();
}
function EWorldEditor::createSelectedAsSubScene( %this, %object )
{
//create new level asset here
AssetBrowser.setupCreateNewAsset("SubSceneAsset", AssetBrowser.selectedModule, "finishCreateSelectedAsSubScene");
%this.contextActionObject = %object;
}
function finishCreateSelectedAsSubScene(%assetId)
{
//echo("finishCreateSelectedAsSubScene");
%subScene = new SubScene() {
levelAsset = %assetId;
};
%levelAssetDef = AssetDatabase.acquireAsset(%assetId);
%levelFilePath = %levelAssetDef.getLevelPath();
//write out to file
EWorldEditor.contextActionObject.save(%levelFilePath);
AssetDatabase.releaseAsset(%assetId);
getRootScene().add(%subScene);
EWorldEditor.contextActionObject.delete();
%subScene.load();
%subScene.recalculateBounds();
%scalar = $SubScene::createScalar;
if(%scalar $= "")
%scalar = 1.5;
//pad for loading boundary
%subScene.scale = VectorScale(%subScene.scale, %scalar);
}
function SubSceneAsset::onWorldEditorDropped(%assetDef, %position)
{
%assetId = %assetDef.getAssetId();
if(!%assetDef.isSubScene)
{
errorf("Cannot drag-and-drop LevelAsset into WorldEditor");
return;
}
%newSubScene = new SubScene()
{
position = %position;
levelAsset = %assetId;
};
getScene(0).add(%newSubScene);
%newSubScene.load();
%newSubScene.recalculateBounds();
EWorldEditor.clearSelection();
EWorldEditor.selectObject(%newSubScene);
EWorldEditor.dropSelection();
EWorldEditor.isDirty = true;
MECreateUndoAction::submit(%newSubScene );
}