- Reimplements autosave logic to handle levels, subscenes and terrains in a more consistent, reliable way.

- Adds entry to RMB menu in Asset Browser to restore an asset to a backup copy taken from autosaves
- Adds reparent out-of-bounds objects button to SceneGroup inspector
- Adds ability to have SubScene have a different loading bounds from the actual subscene bounds, allowing load triggering to happen ahead of the bounds of the subscene itself
- Fixes asset importer handling of animFPS field to be the correct type
- Adds onInspect handling to GameBase allowing better handling for any game class type with editor integration
- Add getAssetLooseFileCount and getAssetLooseFile to AssetManager to be able to iterate over all loose files associated to an asset
- Add standard/default preload function def to forestItem
- Fixes handling of text placement on GuiIconButtonCtrl when text is set to the right
- Adds setGlobalCenter utility function
- Adds ability to set guiInputCtrl active state
- Matched util functions for tracking if left and right mouse buttons are down to EditTSCtrl alongside the existing middle mouse
- Add empty element sanity check to appMesh loader
- Add callback for GameBase when game is created
- Add default graphics options config for steamdeck
- Fix typo in assetImportConfig default
- Filters SceneGroup utility buttons in inspector to only show for relevent class types
This commit is contained in:
JeffR 2025-05-25 07:40:10 -05:00
parent 70502d1b0f
commit bb7ee38bf4
33 changed files with 978 additions and 237 deletions

View file

@ -291,7 +291,7 @@ StringTableEntry Scene::getLevelAsset()
return query->mAssetList[0]; return query->mAssetList[0];
} }
bool Scene::saveScene(StringTableEntry fileName) bool Scene::saveScene(StringTableEntry fileName, const bool& saveSubScenes)
{ {
if (!isServerObject()) if (!isServerObject())
return false; return false;
@ -316,9 +316,12 @@ bool Scene::saveScene(StringTableEntry fileName)
//Inform our subscenes we're saving so they can do any //Inform our subscenes we're saving so they can do any
//special work required as well //special work required as well
for (U32 i = 0; i < mSubScenes.size(); i++) if (saveSubScenes)
{ {
mSubScenes[i]->save(); for (U32 i = 0; i < mSubScenes.size(); i++)
{
mSubScenes[i]->save();
}
} }
bool saveSuccess = save(fileName); bool saveSuccess = save(fileName);
@ -381,9 +384,30 @@ void Scene::getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTab
} }
// //
Vector<SceneObject*> Scene::getObjectsByClass(String className) void Scene::getObjectsByClass(SimObject* object, StringTableEntry className, Vector<SimObject*>* objectsList, bool checkSubscenes)
{ {
return Vector<SceneObject*>(); if(object->getClassName() == className)
{
objectsList->push_back(object);
}
//If it's a subscene and we DON'T want to scan through them, bail out now
SubScene* subScene = dynamic_cast<SubScene*>(object);
if (subScene && !checkSubscenes)
return;
//If possible, now we iterate over the children
SimGroup* group = dynamic_cast<SimGroup*>(object);
if (group)
{
for (U32 c = 0; c < group->size(); c++)
{
SimObject* childObj = dynamic_cast<SimObject*>(group->getObject(c));
//Recurse down
getObjectsByClass(childObj, className, objectsList);
}
}
} }
void Scene::loadAtPosition(const Point3F& position) void Scene::loadAtPosition(const Point3F& position)
@ -460,15 +484,37 @@ DefineEngineMethod(Scene, removeDynamicObject, void, (SceneObject* sceneObj), (n
object->removeDynamicObject(sceneObj); object->removeDynamicObject(sceneObj);
} }
DefineEngineMethod(Scene, getObjectsByClass, String, (String className), (""), DefineEngineMethod(Scene, getObjectsByClass, String, (String className, bool checkSubScenes), ("", false),
"Get the root Scene object that is loaded.\n" "Get the root Scene object that is loaded.\n"
"@return The id of the Root Scene. Will be 0 if no root scene is loaded") "@param className The name of the class of objects to get a list of.\n"
"@param checkSubScenes If true, will also scan through currently loaded subscenes to get matching objects.\n"
"@return A space-separated list of object ids that match the searched-for className")
{ {
if (className == String::EmptyString) if (className == String::EmptyString)
return ""; return "";
//return object->getObjectsByClass(className); Vector<SimObject*>* objectsList = new Vector<SimObject*>();
return "";
object->getObjectsByClass(object, StringTable->insert(className.c_str()), objectsList, checkSubScenes);
char* retBuffer = Con::getReturnBuffer(1024);
U32 len = 0;
S32 i;
//Get the length of our return string
for(U32 i=0; i < objectsList->size(); i++)
len += dStrlen((*objectsList)[i]->getIdString());
char* ret = Con::getReturnBuffer(len + 1);
ret[0] = 0;
for (U32 i = 0; i < objectsList->size(); i++)
{
dStrcat(ret, (*objectsList)[i]->getIdString(), len + 1);
dStrcat(ret, " ", len + 1);
}
return ret;
} }
DefineEngineMethod(Scene, dumpUtilizedAssets, void, (), , DefineEngineMethod(Scene, dumpUtilizedAssets, void, (), ,
@ -492,12 +538,12 @@ DefineEngineMethod(Scene, getLevelAsset, const char*, (), ,
return object->getLevelAsset(); return object->getLevelAsset();
} }
DefineEngineMethod(Scene, save, bool, (const char* fileName), (""), DefineEngineMethod(Scene, save, bool, (const char* fileName, bool saveSubScenes), ("", true),
"Save out the object to the given file.\n" "Save out the object to the given file.\n"
"@param fileName The name of the file to save to." "@param fileName The name of the file to save to."
"@param True on success, false on failure.") "@param True on success, false on failure.")
{ {
return object->saveScene(StringTable->insert(fileName)); return object->saveScene(StringTable->insert(fileName), saveSubScenes);
} }
DefineEngineMethod(Scene, loadAtPosition, void, (Point3F position), (Point3F::Zero), DefineEngineMethod(Scene, loadAtPosition, void, (Point3F position), (Point3F::Zero),

View file

@ -78,7 +78,7 @@ public:
StringTableEntry getOriginatingFile(); StringTableEntry getOriginatingFile();
StringTableEntry getLevelAsset(); StringTableEntry getLevelAsset();
bool saveScene(StringTableEntry fileName); bool saveScene(StringTableEntry fileName, const bool& saveSubScenes = true);
// //
//Networking //Networking
@ -86,7 +86,7 @@ public:
void unpackUpdate(NetConnection *conn, BitStream *stream) override; void unpackUpdate(NetConnection *conn, BitStream *stream) override;
// //
Vector<SceneObject*> getObjectsByClass(String className); void getObjectsByClass(SimObject* object, StringTableEntry className, Vector<SimObject*>* objectsList, bool checkSubscenes = false);
void getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTableEntry>* usedAssetsList); void getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTableEntry>* usedAssetsList);

View file

@ -9,6 +9,7 @@
#include "physics/physicsShape.h" #include "physics/physicsShape.h"
#include "renderInstance/renderPassManager.h" #include "renderInstance/renderPassManager.h"
#include "scene/sceneRenderState.h" #include "scene/sceneRenderState.h"
#include "Scene.h"
IMPLEMENT_CO_NETOBJECT_V1(SceneGroup); IMPLEMENT_CO_NETOBJECT_V1(SceneGroup);
@ -156,6 +157,37 @@ void SceneGroup::onInspect(GuiInspector* inspector)
regenButton->setConsoleCommand(rgBuffer); regenButton->setConsoleCommand(rgBuffer);
regenFieldGui->addObject(regenButton); regenFieldGui->addObject(regenButton);
//
//Regen bounds button
GuiInspectorField* reparentFieldGui = sceneGroupGrp->createInspectorField();
reparentFieldGui->init(inspector, sceneGroupGrp);
reparentFieldGui->setSpecialEditField(true);
reparentFieldGui->setTargetObject(this);
fldnm = StringTable->insert("ReparentOOBObjs");
reparentFieldGui->setSpecialEditVariableName(fldnm);
reparentFieldGui->setInspectorField(NULL, fldnm);
reparentFieldGui->setDocs("");
stack->addObject(reparentFieldGui);
GuiButtonCtrl* reparentButton = new GuiButtonCtrl();
reparentButton->registerObject();
reparentButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
reparentButton->setText("Reparent Out-of-bounds Objs");
reparentButton->resize(Point2I::Zero, regenFieldGui->getExtent());
reparentButton->setHorizSizing(GuiControl::horizResizeWidth);
reparentButton->setVertSizing(GuiControl::vertResizeHeight);
char rprntBuffer[512];
dSprintf(rprntBuffer, 512, "%d.reparentOOBObjects();", this->getId());
reparentButton->setConsoleCommand(rprntBuffer);
reparentFieldGui->addObject(reparentButton);
#endif #endif
} }
@ -279,6 +311,27 @@ void SceneGroup::recalculateBoundingBox()
setMaskBits(TransformMask); setMaskBits(TransformMask);
} }
void SceneGroup::reparentOOBObjects()
{
if (empty())
return;
// Extend the bounding box to include each child's bounding box
for (SimSetIterator itr(this); *itr; ++itr)
{
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
if (child)
{
const Box3F& childBox = child->getWorldBox();
if(!mWorldBox.isOverlapped(childBox))
{
Scene::getRootScene()->addObject(child);
}
}
}
}
U32 SceneGroup::packUpdate(NetConnection* conn, U32 mask, BitStream* stream) U32 SceneGroup::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
{ {
U32 retMask = Parent::packUpdate(conn, mask, stream); U32 retMask = Parent::packUpdate(conn, mask, stream);
@ -363,3 +416,9 @@ DefineEngineMethod(SceneGroup, recalculateBounds, void, (), ,
{ {
object->recalculateBoundingBox(); object->recalculateBoundingBox();
} }
DefineEngineMethod(SceneGroup, reparentOOBObjects, void, (), ,
"Finds objects that are children of the SceneGroup and, if not overlapping or in the bounds, reparents them to the root scene.\n")
{
object->reparentOOBObjects();
}

View file

@ -46,6 +46,7 @@ public:
void addObject(SimObject* object) override; void addObject(SimObject* object) override;
void removeObject(SimObject* object) override; void removeObject(SimObject* object) override;
void recalculateBoundingBox(); void recalculateBoundingBox();
void reparentOOBObjects();
/// ///
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) override; bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) override;

View file

@ -10,6 +10,8 @@
#include "gfx/gfxDrawUtil.h" #include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTransformSaver.h" #include "gfx/gfxTransformSaver.h"
#include "gui/editor/inspector/group.h" #include "gui/editor/inspector/group.h"
#include "gui/worldEditor/editor.h"
#include "math/mathIO.h"
#include "T3D/gameBase/gameBase.h" #include "T3D/gameBase/gameBase.h"
bool SubScene::smTransformChildren = false; bool SubScene::smTransformChildren = false;
@ -32,7 +34,9 @@ SubScene::SubScene() :
mTickPeriodMS(1000), mTickPeriodMS(1000),
mCurrTick(0), mCurrTick(0),
mGlobalLayer(false), mGlobalLayer(false),
mSaving(false) mSaving(false),
mUseSeparateLoadBounds(false),
mLoadBounds(Point3F::One)
{ {
mNetFlags.set(Ghostable | ScopeAlways); mNetFlags.set(Ghostable | ScopeAlways);
@ -70,6 +74,8 @@ void SubScene::initPersistFields()
INITPERSISTFIELD_SUBSCENEASSET(SubScene, SubScene, "The subscene asset to load."); INITPERSISTFIELD_SUBSCENEASSET(SubScene, SubScene, "The subscene asset to load.");
addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)"); addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)");
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with."); addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
addField("UseSeparateLoadBounds", TypeBool, Offset(mUseSeparateLoadBounds, SubScene), "If true, this subscene will utilize a separate bounds for triggering loading/unloading than it's object bounds");
addField("LoadBounds", TypePoint3F, Offset(mLoadBounds, SubScene), "If UseSeparateLoadBounds is true, this subscene will use this value to set up the load/unload bounds");
endGroup("SubScene"); endGroup("SubScene");
addGroup("LoadingManagement"); addGroup("LoadingManagement");
@ -113,6 +119,11 @@ U32 SubScene::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
U32 retMask = Parent::packUpdate(conn, mask, stream); U32 retMask = Parent::packUpdate(conn, mask, stream);
stream->writeFlag(mGlobalLayer); stream->writeFlag(mGlobalLayer);
if(stream->writeFlag(mUseSeparateLoadBounds))
{
mathWrite(*stream, mLoadBounds);
}
return retMask; return retMask;
} }
@ -123,6 +134,11 @@ void SubScene::unpackUpdate(NetConnection* conn, BitStream* stream)
mGlobalLayer = stream->readFlag(); mGlobalLayer = stream->readFlag();
mUseSeparateLoadBounds = stream->readFlag();
if(mUseSeparateLoadBounds)
{
mathRead(*stream, &mLoadBounds);
}
} }
void SubScene::onInspect(GuiInspector* inspector) void SubScene::onInspect(GuiInspector* inspector)
@ -220,7 +236,21 @@ bool SubScene::testBox(const Box3F& testBox)
bool passes = mGlobalLayer; bool passes = mGlobalLayer;
if (!passes) if (!passes)
passes = getWorldBox().isOverlapped(testBox); {
if(mUseSeparateLoadBounds)
{
Box3F loadBox = Box3F(-mLoadBounds.x, -mLoadBounds.y, -mLoadBounds.z,
mLoadBounds.x, mLoadBounds.y, mLoadBounds.z);
loadBox.setCenter(getPosition());
passes = loadBox.isOverlapped(testBox);
}
else
{
passes = getWorldBox().isOverlapped(testBox);
}
}
if (passes) if (passes)
passes = evaluateCondition(); passes = evaluateCondition();
@ -268,6 +298,9 @@ void SubScene::processTick(const Move* move)
void SubScene::_onFileChanged(const Torque::Path& path) void SubScene::_onFileChanged(const Torque::Path& path)
{ {
if (gEditingMission)
return;
if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path) if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path)
return; return;
@ -426,7 +459,7 @@ void SubScene::unload()
} }
bool SubScene::save() bool SubScene::save(const String& filename)
{ {
if (!isServerObject()) if (!isServerObject())
return false; return false;
@ -451,6 +484,9 @@ bool SubScene::save()
StringTableEntry levelPath = mSubSceneAsset->getLevelPath(); StringTableEntry levelPath = mSubSceneAsset->getLevelPath();
if (filename.isNotEmpty())
levelPath = StringTable->insert(filename.c_str());
FileStream fs; FileStream fs;
fs.open(levelPath, Torque::FS::File::Write); fs.open(levelPath, Torque::FS::File::Write);
fs.close(); fs.close();
@ -547,8 +583,26 @@ void SubScene::renderObject(ObjectRenderInst* ri,
//Box3F scale = getScale() //Box3F scale = getScale()
//Box3F bounds = Box3F(-m) //Box3F bounds = Box3F(-m)
if(mUseSeparateLoadBounds && !mGlobalLayer)
{
Box3F loadBounds = Box3F(-mLoadBounds.x, -mLoadBounds.y, -mLoadBounds.z,
mLoadBounds.x, mLoadBounds.y, mLoadBounds.z);
//bounds.setCenter(getPosition());
ColorI loadBoundsColor = ColorI(200, 200, 100, 50);
drawer->drawCube(desc, loadBounds, loadBoundsColor);
// Render wireframe.
desc.setFillModeWireframe();
drawer->drawCube(desc, loadBounds, ColorI::BLACK);
desc.setFillModeSolid();
}
Point3F scale = getScale(); Point3F scale = getScale();
Box3F bounds = Box3F(-scale/2, scale/2); Box3F bounds = Box3F(-scale / 2, scale / 2);
ColorI boundsColor = ColorI(135, 206, 235, 50); ColorI boundsColor = ColorI(135, 206, 235, 50);
@ -565,10 +619,11 @@ void SubScene::renderObject(ObjectRenderInst* ri,
drawer->drawCube(desc, bounds, ColorI::BLACK); drawer->drawCube(desc, bounds, ColorI::BLACK);
} }
DefineEngineMethod(SubScene, save, bool, (),, DefineEngineMethod(SubScene, save, bool, (const char* filename), (""),
"Save out the subScene.\n") "Save out the subScene.\n"
"@param filename (optional) If empty, the subScene will save to it's regular asset path. If defined, it will save out to the filename provided")
{ {
return object->save(); return object->save(filename);
} }

View file

@ -52,6 +52,9 @@ private:
bool mGlobalLayer; bool mGlobalLayer;
bool mUseSeparateLoadBounds;
Point3F mLoadBounds;
public: public:
SubScene(); SubScene();
virtual ~SubScene(); virtual ~SubScene();
@ -118,7 +121,7 @@ public:
return mStartUnloadTimerMS; return mStartUnloadTimerMS;
} }
bool save(); bool save(const String& filename = String());
DECLARE_CALLBACK(void, onLoaded, ()); DECLARE_CALLBACK(void, onLoaded, ());
DECLARE_CALLBACK(void, onUnloaded, ()); DECLARE_CALLBACK(void, onUnloaded, ());

View file

@ -78,7 +78,7 @@ AssetImportConfig::AssetImportConfig() :
SeparateAnimations(false), SeparateAnimations(false),
SeparateAnimationPrefix(""), SeparateAnimationPrefix(""),
animTiming("FrameCount"), animTiming("FrameCount"),
animFPS(false), animFPS(30),
AlwaysAddShapeAnimationSuffix(true), AlwaysAddShapeAnimationSuffix(true),
AddedShapeAnimationSuffix("_anim"), AddedShapeAnimationSuffix("_anim"),
GenerateCollisions(false), GenerateCollisions(false),
@ -193,7 +193,7 @@ void AssetImportConfig::initPersistFields()
addField("SeparateAnimations", TypeBool, Offset(SeparateAnimations, AssetImportConfig), "When importing a shape file, should the animations within be separated out into unique files"); addField("SeparateAnimations", TypeBool, Offset(SeparateAnimations, AssetImportConfig), "When importing a shape file, should the animations within be separated out into unique files");
addField("SeparateAnimationPrefix", TypeRealString, Offset(SeparateAnimationPrefix, AssetImportConfig), "If separating animations out from a source file, what prefix should be added to the names for grouping association"); addField("SeparateAnimationPrefix", TypeRealString, Offset(SeparateAnimationPrefix, AssetImportConfig), "If separating animations out from a source file, what prefix should be added to the names for grouping association");
addField("animTiming", TypeRealString, Offset(animTiming, AssetImportConfig), "Defines the animation timing for the given animation sequence. Options are FrameTime, Seconds, Milliseconds"); addField("animTiming", TypeRealString, Offset(animTiming, AssetImportConfig), "Defines the animation timing for the given animation sequence. Options are FrameTime, Seconds, Milliseconds");
addField("animFPS", TypeBool, Offset(animFPS, AssetImportConfig), "The FPS of the animation sequence"); addField("animFPS", TypeF32, Offset(animFPS, AssetImportConfig), "The FPS of the animation sequence");
addField("AlwaysAddShapeAnimationSuffix", TypeBool, Offset(AlwaysAddShapeAnimationSuffix, AssetImportConfig), "When importing a shape animation, this indicates if it should automatically add a standard suffix onto the name"); addField("AlwaysAddShapeAnimationSuffix", TypeBool, Offset(AlwaysAddShapeAnimationSuffix, AssetImportConfig), "When importing a shape animation, this indicates if it should automatically add a standard suffix onto the name");
addField("AddedShapeAnimationSuffix", TypeString, Offset(AddedShapeAnimationSuffix, AssetImportConfig), " If AlwaysAddShapeAnimationSuffix is on, this is the suffix to be added"); addField("AddedShapeAnimationSuffix", TypeString, Offset(AddedShapeAnimationSuffix, AssetImportConfig), " If AlwaysAddShapeAnimationSuffix is on, this is the suffix to be added");
endGroup("Animation"); endGroup("Animation");

View file

@ -349,6 +349,14 @@ void GameBase::inspectPostApply()
setMaskBits(ExtendedInfoMask); setMaskBits(ExtendedInfoMask);
} }
void GameBase::onInspect(GuiInspector* inspector)
{
if (mDataBlock && mDataBlock->isMethod("onInspect"))
Con::executef(mDataBlock, "onInspect", this, inspector);
else
Parent::onInspect(inspector);
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void GameBase::processTick(const Move * move) void GameBase::processTick(const Move * move)

View file

@ -262,6 +262,8 @@ public:
static void initPersistFields(); static void initPersistFields();
static void consoleInit(); static void consoleInit();
virtual void onInspect(GuiInspector*) override;
/// @} /// @}
///@name Datablock ///@name Datablock

View file

@ -2435,6 +2435,69 @@ AssetManager::typeAssetDependsOnHash* AssetManager::getDependedOnAssets()
// Find any asset dependencies. // Find any asset dependencies.
return &mAssetDependsOn; return &mAssetDependsOn;
} }
//-----------------------------------------------------------------------------
S32 AssetManager::getAssetLooseFileCount(const char* pAssetId)
{
// Debug Profiling.
PROFILE_SCOPE(AssetManager_getAssetLooseFileCount);
// Sanity!
AssertFatal(pAssetId != NULL, "Cannot get loose files for NULL asset Id.");
// Find asset.
AssetDefinition* pAssetDefinition = findAsset(pAssetId);
// Did we find the asset?
if (pAssetDefinition == NULL)
{
// No, so warn.
Con::warnf("Asset Manager: Failed to get loose files for asset Id '%s' as it does not exist.", pAssetId);
return false;
}
S32 looseFileCount = pAssetDefinition->mAssetLooseFiles.size();
// Cleanup our reference
pAssetDefinition = NULL;
return looseFileCount;
}
//-----------------------------------------------------------------------------
const char* AssetManager::getAssetLooseFile(const char* pAssetId, const S32& index)
{
// Debug Profiling.
PROFILE_SCOPE(AssetManager_getAssetLooseFile);
// Sanity!
AssertFatal(pAssetId != NULL, "Cannot get loose file for NULL asset Id.");
// Find asset.
AssetDefinition* pAssetDefinition = findAsset(pAssetId);
// Did we find the asset?
if (pAssetDefinition == NULL)
{
// No, so warn.
Con::warnf("Asset Manager: Failed to get loose file for asset Id '%s' as it does not exist.", pAssetId);
return false;
}
if(index < 0 || index >= pAssetDefinition->mAssetLooseFiles.size())
{
Con::warnf("Asset Manager : Failed to get loose file for asset Id '%s' as the index was out of range.", pAssetId);
}
StringTableEntry looseFile = pAssetDefinition->mAssetLooseFiles[index];
// Cleanup our reference
pAssetDefinition = NULL;
return looseFile;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool AssetManager::scanDeclaredAssets( const char* pPath, const char* pExtension, const bool recurse, ModuleDefinition* pModuleDefinition ) bool AssetManager::scanDeclaredAssets( const char* pPath, const char* pExtension, const bool recurse, ModuleDefinition* pModuleDefinition )

View file

@ -376,6 +376,9 @@ public:
typeAssetDependsOnHash* getDependedOnAssets(); typeAssetDependsOnHash* getDependedOnAssets();
S32 getAssetLooseFileCount(const char* pAssetId);
const char* getAssetLooseFile(const char* pAssetId, const S32& index);
/// Declare Console Object. /// Declare Console Object.
DECLARE_CONOBJECT( AssetManager ); DECLARE_CONOBJECT( AssetManager );

View file

@ -763,6 +763,36 @@ DefineEngineMethod(AssetManager, findAssetLooseFile, S32, (const char* assetQuer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
DefineEngineMethod(AssetManager, getAssetLooseFileCount, S32, (const char* assetId), (""),
"Gets the number of loose files associated with the given assetId.\n"
"@param assetId The assetId to check.\n"
"@return The number of loose files associated with the assetId.\n")
{
// Fetch asset loose file.
const char* pAssetId = assetId;
// Perform query.
return object->getAssetLooseFileCount(pAssetId);
}
//-----------------------------------------------------------------------------
DefineEngineMethod(AssetManager, getAssetLooseFile, const char*, (const char* assetId, S32 index), ("", 0),
"Gets the loose file associated to the given assetId at the provided index.\n"
"@param assetId The assetId to check.\n"
"@param index The index of the loose file to get.\n"
"@return The file name of the associated loose file.\n")
{
// Fetch asset loose file.
const char* pAssetId = assetId;
// Perform query.
return object->getAssetLooseFile(pAssetId, index);
}
//-----------------------------------------------------------------------------
DefineEngineMethod(AssetManager, getDeclaredAssetCount, bool, (),, DefineEngineMethod(AssetManager, getDeclaredAssetCount, bool, (),,
"Gets the number of declared assets.\n" "Gets the number of declared assets.\n"
"@return Returns the number of declared assets.\n") "@return Returns the number of declared assets.\n")

View file

@ -118,9 +118,10 @@ public:
/// Called from Forest the first time a datablock is used /// Called from Forest the first time a datablock is used
/// in order to lazy load content. /// in order to lazy load content.
void preload() bool preload(bool server, String& errorStr) override { return false; }; // we don't ghost ForestItemData specifically. we do do so for TSForestItemData
{ void preload()
if ( !mNeedPreload ) {
if (!mNeedPreload)
return; return;
_preload(); _preload();

View file

@ -382,7 +382,7 @@ void GuiIconButtonCtrl::renderButton( Point2I &offset, const RectI& updateRect )
Point2I start( mTextMargin, ( getHeight() - mProfile->mFont->getHeight() ) / 2 ); Point2I start( mTextMargin, ( getHeight() - mProfile->mFont->getHeight() ) / 2 );
if (mBitmapAsset.notNull() && mIconLocation != IconLocNone) if (mBitmapAsset.notNull() && mIconLocation != IconLocNone)
{ {
start.x = iconRect.extent.x + mButtonMargin.x + mTextMargin; start.x = getWidth() - (iconRect.extent.x + mButtonMargin.x + textWidth);
} }
drawer->setBitmapModulation(fontColor); drawer->setBitmapModulation(fontColor);

View file

@ -2855,6 +2855,23 @@ DefineEngineMethod( GuiControl, getGlobalCenter, Point2I, (),,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
DefineEngineMethod(GuiControl, setGlobalCenter, void, (S32 x, S32 y), ,
"Set the coordinate of the control's center point in coordinates relative to the root control in its control hierarchy.\n"
"@param x The X coordinate of the new center point of the control relative to the root control's.\n"
"@param y The Y coordinate of the new center point of the control relative to the root control's.")
{
//see if we can turn the x/y into ints directly,
Point2I lPosOffset = object->globalToLocalCoord(Point2I(x, y));
lPosOffset += object->getPosition();
const Point2I ext = object->getExtent();
Point2I newpos(lPosOffset.x - ext.x / 2, lPosOffset.y - ext.y / 2);
object->setPosition(newpos);
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiControl, getGlobalPosition, Point2I, (),, DefineEngineMethod( GuiControl, getGlobalPosition, Point2I, (),,
"Get the position of the control relative to the root of the GuiControl hierarchy it is contained in.\n" "Get the position of the control relative to the root of the GuiControl hierarchy it is contained in.\n"
"@return The control's current position in root-relative coordinates." ) "@return The control's current position in root-relative coordinates." )

View file

@ -133,6 +133,25 @@ void GuiInputCtrl::onSleep()
clearFirstResponder(); clearFirstResponder();
} }
void GuiInputCtrl::setActive(bool value)
{
Parent::setActive(value);
if (value)
{
if (!smDesignTime && !mIgnoreMouseEvents)
mouseLock();
setFirstResponder();
}
else
{
mouseUnlock();
clearFirstResponder();
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static bool isModifierKey( U16 keyCode ) static bool isModifierKey( U16 keyCode )

View file

@ -51,6 +51,8 @@ public:
bool onWake() override; bool onWake() override;
void onSleep() override; void onSleep() override;
virtual void setActive(bool state);
bool onInputEvent( const InputEventInfo &event ) override; bool onInputEvent( const InputEventInfo &event ) override;
static void initPersistFields(); static void initPersistFields();

View file

@ -1409,3 +1409,13 @@ DefineEngineMethod( EditTSCtrl, isMiddleMouseDown, bool, (),, "" )
{ {
return object->isMiddleMouseDown(); return object->isMiddleMouseDown();
} }
DefineEngineMethod(EditTSCtrl, isLeftMouseDown, bool, (), , "")
{
return object->isLeftMouseDown();
}
DefineEngineMethod(EditTSCtrl, isRightMouseDown, bool, (), , "")
{
return object->isRightMouseDown();
}

View file

@ -189,7 +189,9 @@ class EditTSCtrl : public GuiTSCtrl
virtual void on3DMouseWheelDown(const Gui3DMouseEvent &){}; virtual void on3DMouseWheelDown(const Gui3DMouseEvent &){};
virtual void get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &); virtual void get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &);
virtual bool isLeftMouseDown() { return mLeftMouseDown; }
virtual bool isMiddleMouseDown() {return mMiddleMouseDown;} virtual bool isMiddleMouseDown() {return mMiddleMouseDown;}
virtual bool isRightMouseDown() { return mLeftMouseDown; }
bool resize(const Point2I& newPosition, const Point2I& newExtent) override; bool resize(const Point2I& newPosition, const Point2I& newExtent) override;

View file

@ -101,6 +101,9 @@ void AssimpAppMesh::computeBounds(Box3F& bounds)
TSMesh* AssimpAppMesh::constructTSMesh() TSMesh* AssimpAppMesh::constructTSMesh()
{ {
if (points.empty() || normals.empty() || primitives.empty() || indices.empty())
return NULL;
TSMesh* tsmesh; TSMesh* tsmesh;
if (isSkin()) if (isSkin())
{ {

View file

@ -126,6 +126,9 @@ void AppMesh::computeNormals()
TSMesh* AppMesh::constructTSMesh() TSMesh* AppMesh::constructTSMesh()
{ {
if (points.empty() || normals.empty() || primitives.empty() || indices.empty())
return NULL;
TSMesh* tsmesh; TSMesh* tsmesh;
if (isSkin()) if (isSkin())
{ {

View file

@ -156,7 +156,7 @@ function loadMissionStage3()
function GameBase::onCreateGame(%this) function GameBase::onCreateGame(%this)
{ {
%db = %this.getDatablock(); %db = %this.getDatablock();
if (%db.isMethod("onCreateGame")) if (isObject(%db) && %db.isMethod("onCreateGame"))
{ {
%db.onCreateGame(%this); %db.onCreateGame(%this);
} }

View file

@ -1199,140 +1199,169 @@ function AutodetectGraphics()
%intel = ( strstr( strupr( getDisplayDeviceInformation() ), "INTEL" ) != -1 ) ? true : false; %intel = ( strstr( strupr( getDisplayDeviceInformation() ), "INTEL" ) != -1 ) ? true : false;
%videoMem = GFXCardProfilerAPI::getVideoMemoryMB(); %videoMem = GFXCardProfilerAPI::getVideoMemoryMB();
if ( %shaderVer < 2.0 ) //Is this a steamdeck?
{ if(startsWith(getDisplayDeviceInformation(), "AMD Custom GPU 0405"))
echo("Your video card does not meet the minimum requirment of shader model 2.0.");
}
if ( %shaderVer < 3.0 || %intel )
{ {
// Allow specular and normals for 2.0a and 2.0b //If we're on a steamdeck, we can specifically calibrate settings for the platform here
if ( %shaderVer > 2.0 ) MeshQualityGroup.applySetting("Medium");
{ TextureQualityGroup.applySetting("Medium");
MeshQualityGroup.applySetting("Lowest"); GroundCoverDensityGroup.applySetting("Medium");
TextureQualityGroup.applySetting("Lowest"); DecalLifetimeGroup.applySetting("Medium");
GroundCoverDensityGroup.applySetting("Lowest"); TerrainQualityGroup.applySetting("Medium");
DecalLifetimeGroup.applySetting("None"); ShaderQualityGroup.applySetting("High");
TerrainQualityGroup.applySetting("Lowest");
ShaderQualityGroup.applySetting("High"); ShadowQualityList.applySetting("None");
ShadowQualityList.applySetting("None"); SoftShadowList.applySetting("Low");
SoftShadowList.applySetting("Off"); $pref::Shadows::useShadowCaching = true;
$pref::Shadows::useShadowCaching = true; AnisotropicFilterOptionsGroup.applySetting("4x");
AntiAliasingOptionsGroup.applySetting("SMAA");
AnisotropicFilterOptionsGroup.applySetting("None"); ParallaxOptionsGroup.applySetting("On");
AntiAliasingOptionsGroup.applySetting("Off"); TrueWaterReflectionsOptionsGroup.applySetting("On");
ParallaxOptionsGroup.applySetting("Off"); PostFXSSAOOptionsGroup.applySetting("Off");
TrueWaterReflectionsOptionsGroup.applySetting("Off"); PostFXDOFOptionsGroup.applySetting("On");
PostFXSSAOOptionsGroup.applySetting("Off"); PostFXVignetteOptionsGroup.applySetting("On");
PostFXDOFOptionsGroup.applySetting("Off"); PostFXLightRayOptionsGroup.applySetting("On");
PostFXVignetteOptionsGroup.applySetting("Off"); }
PostFXLightRayOptionsGroup.applySetting("Off");
}
else
{
MeshQualityGroup.applySetting("Lowest");
TextureQualityGroup.applySetting("Lowest");
GroundCoverDensityGroup.applySetting("Lowest");
DecalLifetimeGroup.applySetting("None");
TerrainQualityGroup.applySetting("Lowest");
ShaderQualityGroup.applySetting("Low");
ShadowQualityList.applySetting("None");
SoftShadowList.applySetting("Off");
$pref::Shadows::useShadowCaching = true;
AnisotropicFilterOptionsGroup.applySetting("None");
AntiAliasingOptionsGroup.applySetting("Off");
ParallaxOptionsGroup.applySetting("Off");
TrueWaterReflectionsOptionsGroup.applySetting("Off");
PostFXSSAOOptionsGroup.applySetting("Off");
PostFXDOFOptionsGroup.applySetting("Off");
PostFXVignetteOptionsGroup.applySetting("Off");
PostFXLightRayOptionsGroup.applySetting("Off");
}
}
else else
{ {
if ( %videoMem > 1000 ) if ( %shaderVer < 2.0 )
{ {
MeshQualityGroup.applySetting("High"); echo("Your video card does not meet the minimum requirment of shader model 2.0.");
TextureQualityGroup.applySetting("High");
GroundCoverDensityGroup.applySetting("High");
DecalLifetimeGroup.applySetting("High");
TerrainQualityGroup.applySetting("High");
ShaderQualityGroup.applySetting("High");
ShadowQualityList.applySetting("High");
SoftShadowList.applySetting("High");
//Should this default to on in ultra settings?
$pref::Shadows::useShadowCaching = true;
AnisotropicFilterOptionsGroup.applySetting("16x");
AntiAliasingOptionsGroup.applySetting("SMAA High");
ParallaxOptionsGroup.applySetting("On");
TrueWaterReflectionsOptionsGroup.applySetting("On");
PostFXSSAOOptionsGroup.applySetting("On");
PostFXDOFOptionsGroup.applySetting("On");
PostFXVignetteOptionsGroup.applySetting("On");
PostFXLightRayOptionsGroup.applySetting("On");
} }
else if ( %videoMem > 400 || %videoMem == 0 )
if ( %shaderVer < 3.0 || %intel )
{ {
MeshQualityGroup.applySetting("Medium"); // Allow specular and normals for 2.0a and 2.0b
TextureQualityGroup.applySetting("Medium"); if ( %shaderVer > 2.0 )
GroundCoverDensityGroup.applySetting("Medium"); {
DecalLifetimeGroup.applySetting("Medium"); MeshQualityGroup.applySetting("Lowest");
TerrainQualityGroup.applySetting("Medium"); TextureQualityGroup.applySetting("Lowest");
ShaderQualityGroup.applySetting("High"); GroundCoverDensityGroup.applySetting("Lowest");
DecalLifetimeGroup.applySetting("None");
ShadowQualityList.applySetting("Medium"); TerrainQualityGroup.applySetting("Lowest");
ShaderQualityGroup.applySetting("High");
SoftShadowList.applySetting("Low");
ShadowQualityList.applySetting("None");
$pref::Shadows::useShadowCaching = true;
SoftShadowList.applySetting("Off");
AnisotropicFilterOptionsGroup.applySetting("4x");
AntiAliasingOptionsGroup.applySetting("SMAA"); $pref::Shadows::useShadowCaching = true;
ParallaxOptionsGroup.applySetting("On");
TrueWaterReflectionsOptionsGroup.applySetting("On"); AnisotropicFilterOptionsGroup.applySetting("None");
PostFXSSAOOptionsGroup.applySetting("Off"); AntiAliasingOptionsGroup.applySetting("Off");
PostFXDOFOptionsGroup.applySetting("On"); ParallaxOptionsGroup.applySetting("Off");
PostFXVignetteOptionsGroup.applySetting("On"); TrueWaterReflectionsOptionsGroup.applySetting("Off");
PostFXLightRayOptionsGroup.applySetting("On"); PostFXSSAOOptionsGroup.applySetting("Off");
PostFXDOFOptionsGroup.applySetting("Off");
if ( %videoMem == 0 ) PostFXVignetteOptionsGroup.applySetting("Off");
echo("Torque was unable to detect available video memory. Applying 'Medium' quality."); PostFXLightRayOptionsGroup.applySetting("Off");
} }
else
{
MeshQualityGroup.applySetting("Lowest");
TextureQualityGroup.applySetting("Lowest");
GroundCoverDensityGroup.applySetting("Lowest");
DecalLifetimeGroup.applySetting("None");
TerrainQualityGroup.applySetting("Lowest");
ShaderQualityGroup.applySetting("Low");
ShadowQualityList.applySetting("None");
SoftShadowList.applySetting("Off");
$pref::Shadows::useShadowCaching = true;
AnisotropicFilterOptionsGroup.applySetting("None");
AntiAliasingOptionsGroup.applySetting("Off");
ParallaxOptionsGroup.applySetting("Off");
TrueWaterReflectionsOptionsGroup.applySetting("Off");
PostFXSSAOOptionsGroup.applySetting("Off");
PostFXDOFOptionsGroup.applySetting("Off");
PostFXVignetteOptionsGroup.applySetting("Off");
PostFXLightRayOptionsGroup.applySetting("Off");
}
}
else else
{ {
MeshQualityGroup.applySetting("Low"); if ( %videoMem > 1000 )
TextureQualityGroup.applySetting("Low"); {
GroundCoverDensityGroup.applySetting("Low"); MeshQualityGroup.applySetting("High");
DecalLifetimeGroup.applySetting("Low"); TextureQualityGroup.applySetting("High");
TerrainQualityGroup.applySetting("Low"); GroundCoverDensityGroup.applySetting("High");
ShaderQualityGroup.applySetting("Low"); DecalLifetimeGroup.applySetting("High");
TerrainQualityGroup.applySetting("High");
ShadowQualityList.applySetting("None"); ShaderQualityGroup.applySetting("High");
SoftShadowList.applySetting("Off"); ShadowQualityList.applySetting("High");
$pref::Shadows::useShadowCaching = true; SoftShadowList.applySetting("High");
AnisotropicFilterOptionsGroup.applySetting("None"); //Should this default to on in ultra settings?
AntiAliasingOptionsGroup.applySetting("FXAA"); $pref::Shadows::useShadowCaching = true;
ParallaxOptionsGroup.applySetting("On");
TrueWaterReflectionsOptionsGroup.applySetting("On"); AnisotropicFilterOptionsGroup.applySetting("16x");
PostFXSSAOOptionsGroup.applySetting("Off"); AntiAliasingOptionsGroup.applySetting("SMAA High");
PostFXDOFOptionsGroup.applySetting("Off"); ParallaxOptionsGroup.applySetting("On");
PostFXVignetteOptionsGroup.applySetting("Off"); TrueWaterReflectionsOptionsGroup.applySetting("On");
PostFXLightRayOptionsGroup.applySetting("Off"); PostFXSSAOOptionsGroup.applySetting("On");
PostFXDOFOptionsGroup.applySetting("On");
PostFXVignetteOptionsGroup.applySetting("On");
PostFXLightRayOptionsGroup.applySetting("On");
}
else if ( %videoMem > 400 || %videoMem == 0 )
{
MeshQualityGroup.applySetting("Medium");
TextureQualityGroup.applySetting("Medium");
GroundCoverDensityGroup.applySetting("Medium");
DecalLifetimeGroup.applySetting("Medium");
TerrainQualityGroup.applySetting("Medium");
ShaderQualityGroup.applySetting("High");
ShadowQualityList.applySetting("Medium");
SoftShadowList.applySetting("Low");
$pref::Shadows::useShadowCaching = true;
AnisotropicFilterOptionsGroup.applySetting("4x");
AntiAliasingOptionsGroup.applySetting("SMAA");
ParallaxOptionsGroup.applySetting("On");
TrueWaterReflectionsOptionsGroup.applySetting("On");
PostFXSSAOOptionsGroup.applySetting("Off");
PostFXDOFOptionsGroup.applySetting("On");
PostFXVignetteOptionsGroup.applySetting("On");
PostFXLightRayOptionsGroup.applySetting("On");
if ( %videoMem == 0 )
echo("Torque was unable to detect available video memory. Applying 'Medium' quality.");
}
else
{
MeshQualityGroup.applySetting("Low");
TextureQualityGroup.applySetting("Low");
GroundCoverDensityGroup.applySetting("Low");
DecalLifetimeGroup.applySetting("Low");
TerrainQualityGroup.applySetting("Low");
ShaderQualityGroup.applySetting("Low");
ShadowQualityList.applySetting("None");
SoftShadowList.applySetting("Off");
$pref::Shadows::useShadowCaching = true;
AnisotropicFilterOptionsGroup.applySetting("None");
AntiAliasingOptionsGroup.applySetting("FXAA");
ParallaxOptionsGroup.applySetting("On");
TrueWaterReflectionsOptionsGroup.applySetting("On");
PostFXSSAOOptionsGroup.applySetting("Off");
PostFXDOFOptionsGroup.applySetting("Off");
PostFXVignetteOptionsGroup.applySetting("Off");
PostFXLightRayOptionsGroup.applySetting("Off");
}
} }
} }

View file

@ -35,9 +35,7 @@
<Setting <Setting
name="AutomaticallyPromptMissingFiles">0</Setting> name="AutomaticallyPromptMissingFiles">0</Setting>
<Setting <Setting
name="DuplicatAutoResolution">AutoPrune</Setting> name="DuplicateAutoResolution">AutoPrune</Setting>
<Setting
name="DuplicateAutoResolution">FolderPrefix</Setting>
<Setting <Setting
name="PreventImportWithErrors">1</Setting> name="PreventImportWithErrors">1</Setting>
<Setting <Setting

View file

@ -99,6 +99,52 @@ function LevelAsset::onEdit(%this)
schedule( 1, 0, "EditorOpenMission", %this); schedule( 1, 0, "EditorOpenMission", %this);
} }
function LevelAsset::onShowActionMenu(%this)
{
if( !isObject( EditLevelAssetPopup ) )
{
new PopupMenu( EditLevelAssetPopup )
{
superClass = "MenuBuilder";
class = "EditorWorldMenu";
jumpFileName = "";
jumpLineNumber = "";
};
}
//Regen the menu so we're fully up and current with options and references
EditLevelAssetPopup.clearItems();
EditLevelAssetPopup.appendItem("Edit Asset" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();");
EditLevelAssetPopup.appendItem("Rename Asset" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();");
EditLevelAssetPopup.appendItem("Reload Asset" TAB "" TAB $CurrentAssetBrowser @ ".refreshAsset();");
EditLevelAssetPopup.appendItem("Asset Properties" TAB "" TAB $CurrentAssetBrowser @ ".editAssetInfo();");
EditLevelAssetPopup.appendItem("-");
EditLevelAssetPopup.appendItem("Duplicate Asset" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();");
EditLevelAssetPopup.appendItem("-");
EditLevelAssetPopup.appendItem("Regenerate Preview Image" TAB "" TAB $CurrentAssetBrowser @ ".regeneratePreviewImage();");
EditLevelAssetPopup.appendItem("-");
EditLevelAssetPopup.appendItem("Restore Backup" TAB RestoreBackupListPopup);
EditLevelAssetPopup.appendItem("-");
EditLevelAssetPopup.appendItem("Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFileLocation();");
EditLevelAssetPopup.appendItem("-");
EditLevelAssetPopup.appendItem("Delete Asset" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();");
%assetId = %this.getAssetId();
%assetType = AssetDatabase.getAssetType(%assetId);
EditLevelAssetPopup.objectData = %assetId;
EditLevelAssetPopup.objectType = %assetType;
RestoreBackupListPopup.populateList(%assetId);
EditLevelAssetPopup.reloadItems();
EditLevelAssetPopup.showPopup(Canvas);
$CurrentAssetBrowser.popupMenu = EditLevelAssetPopup;
}
function LevelAsset::buildBrowserElement(%this, %previewData) function LevelAsset::buildBrowserElement(%this, %previewData)
{ {
%previewData.assetName = %this.assetName; %previewData.assetName = %this.assetName;

View file

@ -64,6 +64,52 @@ function SubSceneAsset::onCreateNew(%this)
return %tamlpath; return %tamlpath;
} }
function SubSceneAsset::onShowActionMenu(%this)
{
if( !isObject( EditSubSceneAssetPopup ) )
{
new PopupMenu( EditSubSceneAssetPopup )
{
superClass = "MenuBuilder";
class = "EditorWorldMenu";
jumpFileName = "";
jumpLineNumber = "";
};
}
//Regen the menu so we're fully up and current with options and references
EditSubSceneAssetPopup.clearItems();
EditSubSceneAssetPopup.appendItem("Edit Asset" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();");
EditSubSceneAssetPopup.appendItem("Rename Asset" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();");
EditSubSceneAssetPopup.appendItem("Reload Asset" TAB "" TAB $CurrentAssetBrowser @ ".refreshAsset();");
EditSubSceneAssetPopup.appendItem("Asset Properties" TAB "" TAB $CurrentAssetBrowser @ ".editAssetInfo();");
EditSubSceneAssetPopup.appendItem("-");
EditSubSceneAssetPopup.appendItem("Duplicate Asset" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();");
EditSubSceneAssetPopup.appendItem("-");
EditSubSceneAssetPopup.appendItem("Regenerate Preview Image" TAB "" TAB $CurrentAssetBrowser @ ".regeneratePreviewImage();");
EditSubSceneAssetPopup.appendItem("-");
EditSubSceneAssetPopup.appendItem("Restore Backup" TAB RestoreBackupListPopup);
EditSubSceneAssetPopup.appendItem("-");
EditSubSceneAssetPopup.appendItem("Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFileLocation();");
EditSubSceneAssetPopup.appendItem("-");
EditSubSceneAssetPopup.appendItem("Delete Asset" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();");
%assetId = %this.getAssetId();
%assetType = AssetDatabase.getAssetType(%assetId);
EditSubSceneAssetPopup.objectData = %assetId;
EditSubSceneAssetPopup.objectType = %assetType;
RestoreBackupListPopup.populateList(%assetId);
EditSubSceneAssetPopup.reloadItems();
EditSubSceneAssetPopup.showPopup(Canvas);
$CurrentAssetBrowser.popupMenu = EditSubSceneAssetPopup;
}
function SubSceneAsset::buildBrowserElement(%this, %previewData) function SubSceneAsset::buildBrowserElement(%this, %previewData)
{ {
%previewData.assetName = %this.assetName; %previewData.assetName = %this.assetName;

View file

@ -132,6 +132,49 @@ function createTerrainBlock(%assetId)
// //
} }
function TerrainAsset::onShowActionMenu(%this)
{
if( !isObject( EditTerrainAssetPopup ) )
{
new PopupMenu( EditTerrainAssetPopup )
{
superClass = "MenuBuilder";
class = "EditorWorldMenu";
jumpFileName = "";
jumpLineNumber = "";
};
}
//Regen the menu so we're fully up and current with options and references
EditTerrainAssetPopup.clearItems();
EditTerrainAssetPopup.appendItem("Edit Asset" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();");
EditTerrainAssetPopup.appendItem("Rename Asset" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();");
EditTerrainAssetPopup.appendItem("Reload Asset" TAB "" TAB $CurrentAssetBrowser @ ".refreshAsset();");
EditTerrainAssetPopup.appendItem("Asset Properties" TAB "" TAB $CurrentAssetBrowser @ ".editAssetInfo();");
EditTerrainAssetPopup.appendItem("-");
EditTerrainAssetPopup.appendItem("Duplicate Asset" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();");
EditTerrainAssetPopup.appendItem("-");
EditTerrainAssetPopup.appendItem("Restore Backup" TAB RestoreBackupListPopup);
EditTerrainAssetPopup.appendItem("-");
EditTerrainAssetPopup.appendItem("Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFileLocation();");
EditTerrainAssetPopup.appendItem("-");
EditTerrainAssetPopup.appendItem("Delete Asset" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();");
%assetId = %this.getAssetId();
%assetType = AssetDatabase.getAssetType(%assetId);
EditTerrainAssetPopup.objectData = %assetId;
EditTerrainAssetPopup.objectType = %assetType;
RestoreBackupListPopup.populateList(%assetId);
EditTerrainAssetPopup.reloadItems();
EditTerrainAssetPopup.showPopup(Canvas);
$CurrentAssetBrowser.popupMenu = EditTerrainAssetPopup;
}
function TerrainAsset::onWorldEditorDropped(%this, %position) function TerrainAsset::onWorldEditorDropped(%this, %position)
{ {

View file

@ -520,4 +520,87 @@ function AssetBrowser::openFolderLocation(%this, %folderPath)
systemCommand(%cmd); systemCommand(%cmd);
} }
} }
}
//-------------------------------------------------------------
function AssetBrowser::getAssetBackupCount(%this, %assetId)
{
//process it and then check if we have any autosave backups
%processedId = strReplace(EditAssetPopup.assetId, ":", "_");
%autosavePath = "tools/autosave/" @ %processedId @ "/";
RestoreBackupListPopup.clearItems();
if(isDirectory(%autosavePath))
{
%dirs = getDirectoryList(%autosavePath);
%count = getFieldCount(%dirs);
return %count;
}
return 0;
}
function AssetBrowser::restoreAssetBackup(%this, %assetId, %index)
{
if(!AssetDatabase.isDeclaredAsset(%assetId))
{
error("AssetBrowser::restoreAssetBackup() - Attempted to restore backed up version of asset: " @ %assetId @ " but asset is not validly declared!");
return;
}
//process it and then check if we have any autosave backups
%processedId = strReplace(%assetId, ":", "_");
%autosavePath = "tools/autosave/" @ %processedId @ "/";
if(isDirectory(%autosavePath))
{
%dirs = getDirectoryList(%autosavePath);
%count = getFieldCount(%dirs);
if(%count > 0)
{
%saveArray = new ArrayObject(){};
//loop over the entries and find the oldest one
for(%f=0; %f < %count; %f++)
{
%saveArray.add(getField(%dirs, %f));
}
%saveArray.sortk();
%folderName = %saveArray.getKey(%index);
//now we just copy the contents of the folder into our assetId path and refresh
%assetPath = AssetDatabase.getAssetPath(%assetId);
%autosaveFullPath = %autosavePath @ "/" @ %folderName @ "/";
%autosaveFullPath = strReplace(%autosaveFullPath, "//", "/");
%file = findFirstFile( %autosaveFullPath @ "*.*" );
while( %file !$= "" )
{
%fileName = fileName(%file);
%assetFileName = %assetPath @ "/" @ %fileName;
warn("| Copying file from: " @ %file @ " to: " @ %assetFileName);
if(!pathCopy(%file, %assetFileName, false))
{
error("AssetBrowser::restoreAssetBackup() - Something went wrong when copying the file: " @ %file @ " to " @ %assetFileName);
}
%file = findNextFile( %autosaveFullPath @ "*.*" );
}
AssetBrowser.reloadAsset(%assetId);
}
else
{
error("AssetBrowser::restoreAssetBackup() - Attempted to restore backed up version of asset: " @ %assetId @ " but no autosaves were found!");
}
}
else
{
error("AssetBrowser::restoreAssetBackup() - Attempted to restore backed up version of asset: " @ %assetId @ " but autosave directory doesn't exist!");
}
} }

View file

@ -43,6 +43,18 @@ function AssetBrowser::buildPopupMenus(%this)
}; };
} }
if( !isObject( RestoreBackupListPopup ) )
{
new PopupMenu( RestoreBackupListPopup )
{
superClass = "MenuBuilder";
class = "EditorWorldMenu";
//isPopup = true;
radioSelection = false;
};
}
if( !isObject( EditLevelAssetPopup ) ) if( !isObject( EditLevelAssetPopup ) )
{ {
new PopupMenu( EditLevelAssetPopup ) new PopupMenu( EditLevelAssetPopup )
@ -59,9 +71,11 @@ function AssetBrowser::buildPopupMenus(%this)
item[ 5 ] = "-"; item[ 5 ] = "-";
Item[ 6 ] = "Duplicate Asset" TAB "" TAB "AssetBrowser.duplicateAsset();"; Item[ 6 ] = "Duplicate Asset" TAB "" TAB "AssetBrowser.duplicateAsset();";
item[ 7 ] = "-"; item[ 7 ] = "-";
item[ 8 ] = "Open File Location" TAB "" TAB "AssetBrowser.openFileLocation();"; Item[ 8 ] = "Restore Backup" TAB RestoreBackupListPopup;
item[ 9 ] = "-"; item[ 9 ] = "-";
item[ 10 ] = "Delete Asset" TAB "" TAB "AssetBrowser.deleteAsset();"; item[ 10 ] = "Open File Location" TAB "" TAB "AssetBrowser.openFileLocation();";
item[ 11 ] = "-";
item[ 12 ] = "Delete Asset" TAB "" TAB "AssetBrowser.deleteAsset();";
jumpFileName = ""; jumpFileName = "";
jumpLineNumber = ""; jumpLineNumber = "";
@ -82,9 +96,11 @@ function AssetBrowser::buildPopupMenus(%this)
item[ 3 ] = "-"; item[ 3 ] = "-";
Item[ 4 ] = "Duplicate Asset" TAB "" TAB "AssetBrowser.duplicateAsset();"; Item[ 4 ] = "Duplicate Asset" TAB "" TAB "AssetBrowser.duplicateAsset();";
item[ 5 ] = "-"; item[ 5 ] = "-";
item[ 6 ] = "Open File Location" TAB "" TAB "AssetBrowser.openFileLocation();"; Item[ 6 ] = "Restore Backup" TAB RestoreBackupListPopup;
item[ 7 ] = "-"; item[ 7 ] = "-";
item[ 8 ] = "Delete Asset" TAB "" TAB "AssetBrowser.deleteAsset();"; item[ 8 ] = "Open File Location" TAB "" TAB "AssetBrowser.openFileLocation();";
item[ 9 ] = "-";
item[ 10 ] = "Delete Asset" TAB "" TAB "AssetBrowser.deleteAsset();";
jumpFileName = ""; jumpFileName = "";
jumpLineNumber = ""; jumpLineNumber = "";
@ -398,3 +414,41 @@ function AddNewScriptAssetPopupMenu::setupDefaultState(%this)
function AddNewScriptAssetPopupMenu::setupGuiControls(%this) function AddNewScriptAssetPopupMenu::setupGuiControls(%this)
{ {
} }
function RestoreBackupListPopup::populateList(%this, %assetId)
{
//process it and then check if we have any autosave backups
%processedId = strReplace(%assetId, ":", "_");
%autosavePath = "tools/autosave/" @ %processedId @ "/";
RestoreBackupListPopup.clearItems();
if(isDirectory(%autosavePath))
{
%dirs = getDirectoryList(%autosavePath);
%count = getFieldCount(%dirs);
if(%count > 0)
{
%saveArray = new ArrayObject(){};
//loop over the entries and find the oldest one
for(%f=0; %f < %count; %f++)
{
%saveArray.add(getField(%dirs, %f));
}
%saveArray.sortk();
for(%i=0; %i < %count; %i++)
{
%folderName = %saveArray.getKey(%i);
%labelText = %folderName @ " (" @ fileModifiedTime(%autosavePath @ %folderName) @ ")";
RestoreBackupListPopup.addItem(%i, %labelText TAB "" TAB "AssetBrowser.restoreAssetBackup(\"" @ %assetId @ "\"," @ %i @ ");");
echo("Added restore item: " @ %labelText TAB "" TAB "AssetBrowser.restoreAssetBackup(\"" @ %assetId @ "\"," @ %i @ ");");
}
%saveArray.delete();
}
}
}

View file

@ -333,11 +333,13 @@ function MenuBuilder::addItem(%this, %pos, %item)
{ {
%this.insertItem(%pos, %name !$= "-" ? %name : "", %accel, %cmd, %bitmapIdx $= "" ? -1 : %bitmapIdx); %this.insertItem(%pos, %name !$= "-" ? %name : "", %accel, %cmd, %bitmapIdx $= "" ? -1 : %bitmapIdx);
} }
return %pos;
} }
function MenuBuilder::appendItem(%this, %item) function MenuBuilder::appendItem(%this, %item)
{ {
%this.addItem(%this.getItemCount(), %item); return %this.addItem(%this.getItemCount(), %item);
} }
function MenuBuilder::onAdd(%this) function MenuBuilder::onAdd(%this)

View file

@ -41,7 +41,7 @@
<Setting <Setting
name="doubleClickAction">Edit Asset</Setting> name="doubleClickAction">Edit Asset</Setting>
<Setting <Setting
name="LastPosExt">0 634 1560 360</Setting> name="LastPosExt">0 1047 2200 360</Setting>
<Setting <Setting
name="previewTileSize">1</Setting> name="previewTileSize">1</Setting>
<Setting <Setting
@ -76,7 +76,7 @@
<Group <Group
name="Grid"> name="Grid">
<Setting <Setting
name="forceSnapRotations">1</Setting> name="forceSnapRotations">0</Setting>
<Setting <Setting
name="gridColor">255 255 255 20</Setting> name="gridColor">255 255 255 20</Setting>
<Setting <Setting
@ -210,7 +210,7 @@
<Setting <Setting
name="backgroundBuild">1</Setting> name="backgroundBuild">1</Setting>
<Setting <Setting
name="SpawnClass">AIPlayer</Setting> name="SpawnClass">Player</Setting>
<Setting <Setting
name="spawnDatablock">DefaultPlayerData</Setting> name="spawnDatablock">DefaultPlayerData</Setting>
</Group> </Group>
@ -276,7 +276,7 @@
<Group <Group
name="TerrainEditor"> name="TerrainEditor">
<Setting <Setting
name="currentAction">lowerHeight</Setting> name="currentAction">raiseHeight</Setting>
<Group <Group
name="ActionValues"> name="ActionValues">
<Setting <Setting
@ -367,6 +367,8 @@
name="dropType">screenCenter</Setting> name="dropType">screenCenter</Setting>
<Setting <Setting
name="EditorLayoutMode">Modern</Setting> name="EditorLayoutMode">Modern</Setting>
<Setting
name="forceLoadDAE">0</Setting>
<Setting <Setting
name="forceSidebarToSide">1</Setting> name="forceSidebarToSide">1</Setting>
<Setting <Setting

View file

@ -555,7 +555,7 @@ function simGroup::onInspectPostApply(%this)
%this.callOnChildren("setLocked",%this.locked); %this.callOnChildren("setLocked",%this.locked);
} }
function simGroup::SelectFiteredObjects(%this, %min, %max) function simGroup::SelectFilteredObjects(%this, %min, %max)
{ {
EWorldEditor.clearSelection(); EWorldEditor.clearSelection();
%this.callOnChildren("filteredSelect", %min, %max ); %this.callOnChildren("filteredSelect", %min, %max );
@ -577,16 +577,29 @@ function SceneObject::filteredSelect(%this, %min, %max)
function simGroup::onInspect(%obj, %inspector) function simGroup::onInspect(%obj, %inspector)
{ {
//Find the 'Editing' group in the inspector if(%obj.isInNamespaceHierarchy("SceneGroup") || %obj.isInNamespaceHierarchy("SubScene"))
%group = %inspector.findExistentGroup("Editing");
if(isObject(%group))
{ {
//We add a field of the type 'SimGroupSelectionButton'. This isn't a 'real' type, so when the inspector group tries to add it //Find the 'Editing' group in the inspector
//it will route down through GuiInspectorGroup(the namespace of %group) and call onConstructField in an attemp to see if there's any %group = %inspector.findExistentGroup("Editing");
//script defined functions that can build a field of that type. if(isObject(%group))
//We happen to define the required 'build @ <fieldTypeName> @ Field()' function below, allowing us to build out the custom field type {
%group.addField("Select Objects", "SimGroupSelectionButton", "Select filtered objects"); //We add a field of the type 'SimGroupSelectionButton'. This isn't a 'real' type, so when the inspector group tries to add it
} //it will route down through GuiInspectorGroup(the namespace of %group) and call onConstructField in an attemp to see if there's any
//script defined functions that can build a field of that type.
//We happen to define the required 'build @ <fieldTypeName> @ Field()' function below, allowing us to build out the custom field type
%group.addField("Select Objects", "SimGroupSelectionButton", "Select filtered objects");
}
}
}
function scene::onInspect(%obj, %inspector)
{
simGroup::onInspect(%obj, %inspector);
}
function subScene::onInspect(%obj, %inspector)
{
simGroup::onInspect(%obj, %inspector);
} }
function GuiInspectorGroup::buildSimGroupSelectionButtonField(%this, %fieldName, %fieldLabel, %fieldDesc, function GuiInspectorGroup::buildSimGroupSelectionButtonField(%this, %fieldName, %fieldLabel, %fieldDesc,
@ -687,7 +700,7 @@ function GuiInspectorGroup::buildSimGroupSelectionButtonField(%this, %fieldName,
tooltipProfile = "EditorToolTipProfile"; tooltipProfile = "EditorToolTipProfile";
text = "Select"; text = "Select";
maxLength = "1024"; maxLength = "1024";
command = %ownerObj @ ".SelectFiteredObjects("@ %ownerObj.minSize @","@ %ownerObj.maxSize @");"; command = %ownerObj @ ".SelectFilteredObjects("@ %ownerObj @".getFieldValue(\"minSize\"),"@ %ownerObj @".getFieldValue(\"maxSize\"));";
}; };
}; };

View file

@ -427,79 +427,148 @@ function EditorSaveMissionAs( %levelAsset )
function EditorAutoSaveMission() function EditorAutoSaveMission()
{ {
// just save the mission without renaming it //re-init the schedule
%autosaveInterval = EditorSettings.value("WorldEditor/AutosaveInterval", "5");
%autosaveInterval = %autosaveInterval * 60000; //convert to milliseconds from minutes
if($Editor::AutoSaveIndex $= "" || $Editor::AutoSaveIndex $= "5") if(EditorGui.autosaveSchedule !$= "")
$Editor::AutoSaveIndex = 1; cancel(EditorGui.autosaveSchedule);
else
$Editor::AutoSaveIndex++;
%autosaveFileName = "tools/autosave/" @ fileBase($Server::MissionFile) @ "_autosave" @ $Editor::AutoSaveIndex @ fileExt($Server::MissionFile); EditorGui.autosaveSchedule = schedule( %autosaveInterval, 0, "EditorAutoSaveMission" );
// first check for dirty and read-only files: // first check for dirty
if((EWorldEditor.isDirty || ETerrainEditor.isMissionDirty) && !isWriteableFileName(%autosaveFileName)) if(!EWorldEditor.isDirty && !ETerrainEditor.isMissionDirty)
{ {
return false; return false;
} }
//TODO: Make Autosave work with terrains //Also skip out if we're actively performing an action
/*if(ETerrainEditor.isDirty) if(EditorGui.currentEditor.editorGui.isLeftMouseDown() || EditorGui.currentEditor.editorGui.isMiddleMouseDown() ||
{ EditorGui.currentEditor.editorGui.isRightMouseDown())
// Find all of the terrain files return false;
initContainerTypeSearch($TypeMasks::TerrainObjectType);
while ((%terrainObject = containerSearchNext()) != 0)
{
if (!isWriteableFileName(%terrainObject.terrainFile))
{
if (toolsMessageBox("Error", "Terrain file \""@ %terrainObject.terrainFile @ "\" is read-only. Continue?", "Ok", "Stop") == $MROk)
continue;
else
return false;
}
}
}*/
// now write the terrain and mission files out:
if(EWorldEditor.isDirty || ETerrainEditor.isMissionDirty)
getScene(0).save(%autosaveFileName);
//TODO: Make Autosave work with terrains %backupFilePathBase = "tools/autosave/";
/*if(ETerrainEditor.isDirty) if(!isObject(AssetBackupListArray))
{ {
// Find all of the terrain files new ArrayObject(AssetBackupListArray){};
initContainerTypeSearch($TypeMasks::TerrainObjectType); }
while ((%terrainObject = containerSearchNext()) != 0) AssetBackupListArray.empty();
//Next, we figure out what all we're planning to save
%terrainObjects = getRootScene().getObjectsByClass("TerrainBlock", false);
for(%i=0; %i < getWordCount(%terrainObjects); %i++)
{
%terrObj = getWord(%terrainObjects, %i);
%terrAssetId = %terrObj.terrainAsset;
%sanitizedName = strReplace(%terrAssetId, ":", "_");
%terrAssetBackupPath = %backupFilePathBase @ %sanitizedName @ "/";
AssetBackupListArray.add(%terrAssetBackupPath, %terrAssetId SPC %terrObj);
}
%subScenes = getRootScene().getObjectsByClass("SubScene", false);
for(%i=0; %i < getWordCount(%subScenes); %i++)
{
%subSceneObj = getWord(%subScenes, %i);
%subSceneAssetId = %subSceneObj.levelAsset;
%sanitizedName = strReplace(%subSceneAssetId, ":", "_");
%subSceneAssetBackupPath = %backupFilePathBase @ %sanitizedName @ "/";
AssetBackupListArray.add(%subSceneAssetBackupPath, %subSceneAssetId SPC %subSceneObj);
}
%levelAssetId = $Server::LevelAsset.getAssetId();
%levelSanitizedName = strReplace(%levelAssetId, ":", "_");
%levelAssetBackupPath = %backupFilePathBase @ %levelSanitizedName @ "/";
AssetBackupListArray.add(%levelAssetBackupPath, %levelAssetId SPC getRootScene());
//Now we process through our assets to find index counts and save off a copy duplicate to the backup path
if($Editor::MaxAutosaves $= "")
$Editor::MaxAutosaves = 10;
for(%i=0; %i < AssetBackupListArray.count(); %i++)
{
%path = AssetBackupListArray.getKey(%i);
%assetId = getWord(AssetBackupListArray.getValue(%i), 0);
%obj = getWord(AssetBackupListArray.getValue(%i), 1);
%dirList = getDirectoryList(%path);
%savesCount = getFieldCount(%dirList);
%newestFolder = 0;
%oldestFolder = -1;
if(%savesCount != 0)
{ {
if(%terrainObject.terrainAsset !$= "") %saveArray = new ArrayObject(){};
//loop over the entries and find the oldest one
for(%f=0; %f < %savesCount; %f++)
{ {
//we utilize a terrain asset, so we'll update our dependencies while we're at it %saveArray.add(getField(%dirList, %f), %f);
%terrainObject.saveAsset();
} }
else
%saveArray.sortk(true);
%oldestFolder = %saveArray.getKey(0);
%newestFolder = %saveArray.getKey(%savesCount-1);
%saveArray.delete();
}
if(%savesCount >= $Editor::MaxAutosaves)
{
AssetBrowser.dirHandler.deleteFolder(%path @ %oldestFolder @ "/");
}
%newSaveFolder = %newestFolder + 1;
%newSaveFolderPath = %path @ %newSaveFolder @ "/";
if(!isDirectory(%newSaveFolderPath))
{
AssetBrowser.dirHandler.createFolder(%newSaveFolderPath);
}
else
{
error("EditorAutoSaveMission() - Somehow we indicated a brand new save folder, but it already exists? Stopping to avoid problems");
continue;
}
%assetFilePath = AssetDatabase.getAssetFilePath(%assetId);
%assetFileName = fileName(%assetFilePath);
%assetPath = filePath(%assetFilePath) @ "/";
if(!pathCopy(%assetFilePath, %newSaveFolderPath @ %assetFileName))
{
error("EditorAutoSaveMission() - failed to copy the asset file: " @ %assetFilePath @ " to backup directory!");
continue;
}
//Do the actual copy of the files for backup purposes now
%looseFileCount = AssetDatabase.getAssetLooseFileCount(%assetId);
for(%lf = 0; %lf < %looseFileCount; %lf++)
{
%looseFile = AssetDatabase.getAssetLooseFile(%assetId, %lf);
%looseFileName = fileName(%looseFile);
if(!isFile(%looseFile))
continue; //only bother with real files
if(!pathCopy(%looseFile, %newSaveFolderPath @ %looseFileName))
{ {
%terrainObject.save(%terrainObject.terrainFile); error("EditorAutoSaveMission() - failed to copy the asset loose file: " @ %assetPath @ %looseFileName @ " to backup directory!");
} }
%fileExt = fileExt(%looseFile);
if(%fileExt $= ".mis")
%obj.save(%newSaveFolderPath @ %looseFileName, false);
else if(%fileExt $= ".subMis" || %fileExt $= ".ter")
%obj.save(%newSaveFolderPath @ %looseFileName); //Save out the current status of it to the file so we have our actual snapshot
} }
} }
ETerrainPersistMan.saveDirty();*/
// Give EditorPlugins a chance to save.
for ( %i = 0; %i < EditorPluginSet.getCount(); %i++ )
{
%obj = EditorPluginSet.getObject(%i);
if ( %obj.isDirty() )
%obj.onSaveMission( %autosaveFileName );
}
%autosaveInterval = EditorSettings.value("WorldEditor/AutosaveInterval", "5");
%autosaveInterval = %autosaveInterval * 60000; //convert to milliseconds from minutes
EditorGui.autosaveSchedule = schedule( %autosaveInterval, 0, "EditorAutoSaveMission" );
return true;
} }
function EditorOpenMission(%levelAsset) function EditorOpenMission(%levelAsset)
@ -722,6 +791,14 @@ function EditorExplodePrefab()
function makeSelectedAMesh(%assetId) function makeSelectedAMesh(%assetId)
{ {
%selectedCount = EWorldEditor.getSelectionSize();
if(!%selectedCount)
{
error("You need to select at least one object to turn it into a mesh!");
return;
}
%assetDef = AssetDatabase.acquireAsset(%assetId); %assetDef = AssetDatabase.acquireAsset(%assetId);
%assetPath = AssetDatabase.getAssetPath(%assetId); %assetPath = AssetDatabase.getAssetPath(%assetId);
@ -740,6 +817,21 @@ function makeSelectedAMesh(%assetId)
{ {
//Next, for safety purposes(and convenience!) we'll make them a prefab aping off the filepath/name provided //Next, for safety purposes(and convenience!) we'll make them a prefab aping off the filepath/name provided
//TODO: Make this an editor option //TODO: Make this an editor option
//We want to figure out where this stuff goes, so scan through our selected objects
//And see if we have a common parent
%sameParent = true;
%firstParent = EWorldEditor.getSelectedObject(0).parentGroup;
for(%i=1; %i < %selectedCount; %i++)
{
%selectedObj = EWorldEditor.getSelectedObject(%i);
if(%firstParent != %selectedObj.parentGroup)
{
%sameParent = false;
break;
}
}
%prefabPath = %assetPath @ "/" @ %assetDef.AssetName @ ".prefab"; %prefabPath = %assetPath @ "/" @ %assetDef.AssetName @ ".prefab";
EWorldEditor.makeSelectionPrefab(%prefabPath, false); EWorldEditor.makeSelectionPrefab(%prefabPath, false);
%selectionPos = EWorldEditor.getSelectedObject(0).getPosition(); %selectionPos = EWorldEditor.getSelectedObject(0).getPosition();
@ -752,8 +844,14 @@ function makeSelectedAMesh(%assetId)
shapeAsset = %assetId; shapeAsset = %assetId;
position = %selectionPos; position = %selectionPos;
}; };
getRootScene().add(%newStatic); if(%sameParent)
%firstParent.add(%newStatic);
else
getRootScene().add(%newStatic);
EWorldEditor.clearSelection();
EWorldEditor.selectObject(%newStatic);
} }
EditorTree.buildVisibleTree( true ); EditorTree.buildVisibleTree( true );