mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-02 20:10:32 +00:00
- 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:
parent
70502d1b0f
commit
bb7ee38bf4
33 changed files with 978 additions and 237 deletions
|
|
@ -291,7 +291,7 @@ StringTableEntry Scene::getLevelAsset()
|
|||
return query->mAssetList[0];
|
||||
}
|
||||
|
||||
bool Scene::saveScene(StringTableEntry fileName)
|
||||
bool Scene::saveScene(StringTableEntry fileName, const bool& saveSubScenes)
|
||||
{
|
||||
if (!isServerObject())
|
||||
return false;
|
||||
|
|
@ -316,9 +316,12 @@ bool Scene::saveScene(StringTableEntry fileName)
|
|||
|
||||
//Inform our subscenes we're saving so they can do any
|
||||
//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);
|
||||
|
|
@ -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)
|
||||
|
|
@ -460,15 +484,37 @@ DefineEngineMethod(Scene, removeDynamicObject, void, (SceneObject* sceneObj), (n
|
|||
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"
|
||||
"@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)
|
||||
return "";
|
||||
|
||||
//return object->getObjectsByClass(className);
|
||||
return "";
|
||||
Vector<SimObject*>* objectsList = new Vector<SimObject*>();
|
||||
|
||||
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, (), ,
|
||||
|
|
@ -492,12 +538,12 @@ DefineEngineMethod(Scene, getLevelAsset, const char*, (), ,
|
|||
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"
|
||||
"@param fileName The name of the file to save to."
|
||||
"@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),
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public:
|
|||
StringTableEntry getOriginatingFile();
|
||||
StringTableEntry getLevelAsset();
|
||||
|
||||
bool saveScene(StringTableEntry fileName);
|
||||
bool saveScene(StringTableEntry fileName, const bool& saveSubScenes = true);
|
||||
|
||||
//
|
||||
//Networking
|
||||
|
|
@ -86,7 +86,7 @@ public:
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "physics/physicsShape.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "Scene.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SceneGroup);
|
||||
|
||||
|
|
@ -156,6 +157,37 @@ void SceneGroup::onInspect(GuiInspector* inspector)
|
|||
regenButton->setConsoleCommand(rgBuffer);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -279,6 +311,27 @@ void SceneGroup::recalculateBoundingBox()
|
|||
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 retMask = Parent::packUpdate(conn, mask, stream);
|
||||
|
|
@ -363,3 +416,9 @@ DefineEngineMethod(SceneGroup, recalculateBounds, void, (), ,
|
|||
{
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public:
|
|||
void addObject(SimObject* object) override;
|
||||
void removeObject(SimObject* object) override;
|
||||
void recalculateBoundingBox();
|
||||
void reparentOOBObjects();
|
||||
|
||||
///
|
||||
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) override;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "gui/editor/inspector/group.h"
|
||||
#include "gui/worldEditor/editor.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
|
||||
bool SubScene::smTransformChildren = false;
|
||||
|
|
@ -32,7 +34,9 @@ SubScene::SubScene() :
|
|||
mTickPeriodMS(1000),
|
||||
mCurrTick(0),
|
||||
mGlobalLayer(false),
|
||||
mSaving(false)
|
||||
mSaving(false),
|
||||
mUseSeparateLoadBounds(false),
|
||||
mLoadBounds(Point3F::One)
|
||||
{
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
|
|
@ -70,6 +74,8 @@ void SubScene::initPersistFields()
|
|||
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.");
|
||||
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");
|
||||
|
||||
addGroup("LoadingManagement");
|
||||
|
|
@ -113,6 +119,11 @@ U32 SubScene::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
|
|||
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
||||
|
||||
stream->writeFlag(mGlobalLayer);
|
||||
if(stream->writeFlag(mUseSeparateLoadBounds))
|
||||
{
|
||||
mathWrite(*stream, mLoadBounds);
|
||||
}
|
||||
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
|
@ -123,6 +134,11 @@ void SubScene::unpackUpdate(NetConnection* conn, BitStream* stream)
|
|||
|
||||
mGlobalLayer = stream->readFlag();
|
||||
|
||||
mUseSeparateLoadBounds = stream->readFlag();
|
||||
if(mUseSeparateLoadBounds)
|
||||
{
|
||||
mathRead(*stream, &mLoadBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void SubScene::onInspect(GuiInspector* inspector)
|
||||
|
|
@ -220,7 +236,21 @@ bool SubScene::testBox(const Box3F& testBox)
|
|||
bool passes = mGlobalLayer;
|
||||
|
||||
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)
|
||||
passes = evaluateCondition();
|
||||
|
|
@ -268,6 +298,9 @@ void SubScene::processTick(const Move* move)
|
|||
|
||||
void SubScene::_onFileChanged(const Torque::Path& path)
|
||||
{
|
||||
if (gEditingMission)
|
||||
return;
|
||||
|
||||
if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path)
|
||||
return;
|
||||
|
||||
|
|
@ -426,7 +459,7 @@ void SubScene::unload()
|
|||
|
||||
}
|
||||
|
||||
bool SubScene::save()
|
||||
bool SubScene::save(const String& filename)
|
||||
{
|
||||
if (!isServerObject())
|
||||
return false;
|
||||
|
|
@ -451,6 +484,9 @@ bool SubScene::save()
|
|||
|
||||
StringTableEntry levelPath = mSubSceneAsset->getLevelPath();
|
||||
|
||||
if (filename.isNotEmpty())
|
||||
levelPath = StringTable->insert(filename.c_str());
|
||||
|
||||
FileStream fs;
|
||||
fs.open(levelPath, Torque::FS::File::Write);
|
||||
fs.close();
|
||||
|
|
@ -547,8 +583,26 @@ void SubScene::renderObject(ObjectRenderInst* ri,
|
|||
//Box3F scale = getScale()
|
||||
//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();
|
||||
Box3F bounds = Box3F(-scale/2, scale/2);
|
||||
Box3F bounds = Box3F(-scale / 2, scale / 2);
|
||||
|
||||
ColorI boundsColor = ColorI(135, 206, 235, 50);
|
||||
|
||||
|
|
@ -565,10 +619,11 @@ void SubScene::renderObject(ObjectRenderInst* ri,
|
|||
drawer->drawCube(desc, bounds, ColorI::BLACK);
|
||||
}
|
||||
|
||||
DefineEngineMethod(SubScene, save, bool, (),,
|
||||
"Save out the subScene.\n")
|
||||
DefineEngineMethod(SubScene, save, bool, (const char* filename), (""),
|
||||
"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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ private:
|
|||
|
||||
bool mGlobalLayer;
|
||||
|
||||
bool mUseSeparateLoadBounds;
|
||||
Point3F mLoadBounds;
|
||||
|
||||
public:
|
||||
SubScene();
|
||||
virtual ~SubScene();
|
||||
|
|
@ -118,7 +121,7 @@ public:
|
|||
return mStartUnloadTimerMS;
|
||||
}
|
||||
|
||||
bool save();
|
||||
bool save(const String& filename = String());
|
||||
|
||||
DECLARE_CALLBACK(void, onLoaded, ());
|
||||
DECLARE_CALLBACK(void, onUnloaded, ());
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ AssetImportConfig::AssetImportConfig() :
|
|||
SeparateAnimations(false),
|
||||
SeparateAnimationPrefix(""),
|
||||
animTiming("FrameCount"),
|
||||
animFPS(false),
|
||||
animFPS(30),
|
||||
AlwaysAddShapeAnimationSuffix(true),
|
||||
AddedShapeAnimationSuffix("_anim"),
|
||||
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("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("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("AddedShapeAnimationSuffix", TypeString, Offset(AddedShapeAnimationSuffix, AssetImportConfig), " If AlwaysAddShapeAnimationSuffix is on, this is the suffix to be added");
|
||||
endGroup("Animation");
|
||||
|
|
|
|||
|
|
@ -349,6 +349,14 @@ void GameBase::inspectPostApply()
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -262,6 +262,8 @@ public:
|
|||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
virtual void onInspect(GuiInspector*) override;
|
||||
|
||||
/// @}
|
||||
|
||||
///@name Datablock
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue