mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-13 19:53:48 +00:00
Implementation of Subscenes, SceneGroups and Gamemodes
Standardizes Gamemodes to be an actual class with data and utility functions that can be parsed Adds inspector field handling for selecting gamemodes Updated Scene class to work with Gamemodes for the gamemode field Updates editor suite elements to be able to create SubScenes, SceneGroups and Gamemodes Adds ability to convert SimGroup to SubScene Updates BaseUI's chooselevel menu to have gamemode selection and filters shown levels based on selected gamemode
This commit is contained in:
parent
0d07823ecd
commit
ae8eca48e1
36 changed files with 2963 additions and 173 deletions
|
|
@ -1,20 +1,26 @@
|
|||
#include "Scene.h"
|
||||
#include "T3D/assets/LevelAsset.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/gameMode.h"
|
||||
|
||||
Scene * Scene::smRootScene = nullptr;
|
||||
Vector<Scene*> Scene::smSceneList;
|
||||
|
||||
IMPLEMENT_CALLBACK(Scene, onSaving, void, (const char* fileName), (fileName),
|
||||
"@brief Called when a scene is saved to allow scenes to special-handle prepwork for saving if required.\n\n"
|
||||
|
||||
"@param fileName The level file being saved\n");
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Scene);
|
||||
|
||||
Scene::Scene() :
|
||||
mIsSubScene(false),
|
||||
mParentScene(nullptr),
|
||||
mSceneId(-1),
|
||||
mIsEditing(false),
|
||||
mIsDirty(false),
|
||||
mEditPostFX(0)
|
||||
{
|
||||
mGameModeName = StringTable->EmptyString();
|
||||
mGameModesNames = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
Scene::~Scene()
|
||||
|
|
@ -28,13 +34,12 @@ void Scene::initPersistFields()
|
|||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Internal");
|
||||
addField("isSubscene", TypeBool, Offset(mIsSubScene, Scene), "", AbstractClassRep::FIELD_HideInInspectors);
|
||||
addField("isEditing", TypeBool, Offset(mIsEditing, Scene), "", AbstractClassRep::FIELD_HideInInspectors);
|
||||
addField("isDirty", TypeBool, Offset(mIsDirty, Scene), "", AbstractClassRep::FIELD_HideInInspectors);
|
||||
endGroup("Internal");
|
||||
|
||||
addGroup("Gameplay");
|
||||
addField("gameModeName", TypeString, Offset(mGameModeName, Scene), "The name of the gamemode that this scene utilizes");
|
||||
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, Scene), "The game modes that this Scene is associated with.");
|
||||
endGroup("Gameplay");
|
||||
|
||||
addGroup("PostFX");
|
||||
|
|
@ -51,48 +56,33 @@ bool Scene::onAdd()
|
|||
smSceneList.push_back(this);
|
||||
mSceneId = smSceneList.size() - 1;
|
||||
|
||||
/*if (smRootScene == nullptr)
|
||||
{
|
||||
//we're the first scene, so we're the root. woo!
|
||||
smRootScene = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIsSubScene = true;
|
||||
smRootScene->mSubScenes.push_back(this);
|
||||
}*/
|
||||
GameMode::findGameModes(mGameModesNames, &mGameModesList);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scene::onRemove()
|
||||
{
|
||||
for (U32 i = 0; i < mGameModesList.size(); i++)
|
||||
{
|
||||
mGameModesList[i]->onSceneUnloaded_callback();
|
||||
}
|
||||
|
||||
Parent::onRemove();
|
||||
|
||||
smSceneList.remove(this);
|
||||
mSceneId = -1;
|
||||
|
||||
/*if (smRootScene == this)
|
||||
{
|
||||
for (U32 i = 0; i < mSubScenes.size(); i++)
|
||||
{
|
||||
mSubScenes[i]->deleteObject();
|
||||
}
|
||||
}
|
||||
else if (smRootScene != nullptr)
|
||||
{
|
||||
for (U32 i = 0; i < mSubScenes.size(); i++)
|
||||
{
|
||||
if(mSubScenes[i]->getId() == getId())
|
||||
smRootScene->mSubScenes.erase(i);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void Scene::onPostAdd()
|
||||
{
|
||||
if (isMethod("onPostAdd"))
|
||||
Con::executef(this, "onPostAdd");
|
||||
|
||||
for (U32 i = 0; i < mGameModesList.size(); i++)
|
||||
{
|
||||
mGameModesList[i]->onSceneLoaded_callback();
|
||||
}
|
||||
}
|
||||
|
||||
bool Scene::_editPostEffects(void* object, const char* index, const char* data)
|
||||
|
|
@ -110,11 +100,12 @@ bool Scene::_editPostEffects(void* object, const char* index, const char* data)
|
|||
void Scene::addObject(SimObject* object)
|
||||
{
|
||||
//Child scene
|
||||
Scene* scene = dynamic_cast<Scene*>(object);
|
||||
SubScene* scene = dynamic_cast<SubScene*>(object);
|
||||
if (scene)
|
||||
{
|
||||
//We'll keep these principly separate so they don't get saved into each other
|
||||
mSubScenes.push_back(scene);
|
||||
Parent::addObject(object);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +126,7 @@ void Scene::addObject(SimObject* object)
|
|||
void Scene::removeObject(SimObject* object)
|
||||
{
|
||||
//Child scene
|
||||
Scene* scene = dynamic_cast<Scene*>(object);
|
||||
SubScene* scene = dynamic_cast<SubScene*>(object);
|
||||
if (scene)
|
||||
{
|
||||
//We'll keep these principly separate so they don't get saved into each other
|
||||
|
|
@ -175,12 +166,58 @@ void Scene::removeDynamicObject(SceneObject* object)
|
|||
|
||||
void Scene::interpolateTick(F32 delta)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Scene::processTick()
|
||||
{
|
||||
if (!isServerObject())
|
||||
return;
|
||||
|
||||
//iterate over our subscenes to update their status of loaded or unloaded based on if any control objects intersect their bounds
|
||||
for (U32 i = 0; i < mSubScenes.size(); i++)
|
||||
{
|
||||
bool hasClients = false;
|
||||
|
||||
SimGroup* pClientGroup = Sim::getClientGroup();
|
||||
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
|
||||
{
|
||||
GameConnection* gc = dynamic_cast<GameConnection*>(*itr);
|
||||
if (gc)
|
||||
{
|
||||
GameBase* controlObj = gc->getControlObject();
|
||||
if (controlObj == nullptr)
|
||||
{
|
||||
controlObj = gc->getCameraObject();
|
||||
}
|
||||
|
||||
if (controlObj != nullptr)
|
||||
{
|
||||
if (mSubScenes[i]->testBox(controlObj->getWorldBox()))
|
||||
{
|
||||
//we have a client controlling object in the bounds, so we ensure the contents are loaded
|
||||
hasClients = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasClients)
|
||||
{
|
||||
mSubScenes[i]->setUnloadTimeMS(-1);
|
||||
mSubScenes[i]->load();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mSubScenes[i]->isLoaded() && mSubScenes[i]->getUnloadTimsMS() == -1)
|
||||
{
|
||||
mSubScenes[i]->setUnloadTimeMS(Sim::getCurrentTime());
|
||||
}
|
||||
|
||||
if (Sim::getCurrentTime() - mSubScenes[i]->getUnloadTimsMS() > 5000)
|
||||
mSubScenes[i]->unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::advanceTime(F32 timeDelta)
|
||||
|
|
@ -257,6 +294,21 @@ bool Scene::saveScene(StringTableEntry fileName)
|
|||
fileName = getOriginatingFile();
|
||||
}
|
||||
|
||||
//Inform our objects we're saving, so if they do any special stuff
|
||||
//they can do it before the actual write-out
|
||||
for (U32 i = 0; i < mPermanentObjects.size(); i++)
|
||||
{
|
||||
SceneObject* obj = mPermanentObjects[i];
|
||||
obj->onSaving_callback(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++)
|
||||
{
|
||||
mSubScenes[i]->save();
|
||||
}
|
||||
|
||||
bool saveSuccess = save(fileName);
|
||||
|
||||
if (!saveSuccess)
|
||||
|
|
@ -286,9 +338,12 @@ bool Scene::saveScene(StringTableEntry fileName)
|
|||
dSprintf(depValue, sizeof(depValue), "%s=%s", ASSET_ID_SIGNATURE, utilizedAssetsList[i]);
|
||||
|
||||
levelAssetDef->setDataField(StringTable->insert(depSlotName), NULL, StringTable->insert(depValue));
|
||||
|
||||
}
|
||||
|
||||
//update the gamemode list as well
|
||||
levelAssetDef->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
|
||||
|
||||
//Finally, save
|
||||
saveSuccess = levelAssetDef->saveAsset();
|
||||
|
||||
return saveSuccess;
|
||||
|
|
@ -413,8 +468,6 @@ DefineEngineMethod(Scene, getLevelAsset, const char*, (), ,
|
|||
DefineEngineMethod(Scene, save, bool, (const char* fileName), (""),
|
||||
"Save out the object to the given file.\n"
|
||||
"@param fileName The name of the file to save to."
|
||||
"@param selectedOnly If true, only objects marked as selected will be saved out.\n"
|
||||
"@param preAppendString Text which will be preprended directly to the object serialization.\n"
|
||||
"@param True on success, false on failure.")
|
||||
{
|
||||
return object->saveScene(StringTable->insert(fileName));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef SCENE_H
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#ifndef _NETOBJECT_H_
|
||||
|
|
@ -9,8 +9,16 @@
|
|||
#ifndef _ITICKABLE_H_
|
||||
#include "core/iTickable.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef GAME_MODE_H
|
||||
#include "gameMode.h"
|
||||
#endif
|
||||
#ifndef SUB_SCENE_H
|
||||
#include "SubScene.h"
|
||||
#endif
|
||||
|
||||
/// Scene
|
||||
/// This object is effectively a smart container to hold and manage any relevent scene objects and data
|
||||
|
|
@ -19,14 +27,11 @@ class Scene : public NetObject, public virtual ITickable
|
|||
{
|
||||
typedef NetObject Parent;
|
||||
|
||||
bool mIsSubScene;
|
||||
|
||||
Scene* mParentScene;
|
||||
|
||||
Vector<Scene*> mSubScenes;
|
||||
Vector<SubScene*> mSubScenes;
|
||||
|
||||
Vector<SceneObject*> mPermanentObjects;
|
||||
|
||||
Vector<SceneObject*> mDynamicObjects;
|
||||
|
||||
S32 mSceneId;
|
||||
|
|
@ -37,7 +42,8 @@ class Scene : public NetObject, public virtual ITickable
|
|||
|
||||
bool mEditPostFX;
|
||||
|
||||
StringTableEntry mGameModeName;
|
||||
StringTableEntry mGameModesNames;
|
||||
Vector<GameMode*> mGameModesList;
|
||||
|
||||
protected:
|
||||
static Scene * smRootScene;
|
||||
|
|
@ -96,6 +102,8 @@ public:
|
|||
}
|
||||
|
||||
static Vector<Scene*> smSceneList;
|
||||
|
||||
DECLARE_CALLBACK(void, onSaving, (const char* fileName));
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -136,3 +144,4 @@ Vector<T*> Scene::getObjectsByClass(bool checkSubscenes)
|
|||
|
||||
return foundObjects;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
350
Engine/source/T3D/SceneGroup.cpp
Normal file
350
Engine/source/T3D/SceneGroup.cpp
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
#include "SceneGroup.h"
|
||||
|
||||
#include "gameBase/gameConnection.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "gui/editor/inspector/group.h"
|
||||
#include "gui/worldEditor/editor.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "physics/physicsShape.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SceneGroup);
|
||||
|
||||
ConsoleDocClass(SceneGroup,
|
||||
"@brief A collection of arbitrary objects which can be allocated and manipulated as a group.\n\n"
|
||||
|
||||
"%SceneGroup always points to a (.SceneGroup) file which defines its objects. In "
|
||||
"fact more than one %SceneGroup can reference this file and both will update "
|
||||
"if the file is modified.\n\n"
|
||||
|
||||
"%SceneGroup is a very simple object and only exists on the server. When it is "
|
||||
"created it allocates children objects by reading the (.SceneGroup) file like "
|
||||
"a list of instructions. It then sets their transform relative to the %SceneGroup "
|
||||
"and Torque networking handles the rest by ghosting the new objects to clients. "
|
||||
"%SceneGroup itself is not ghosted.\n\n"
|
||||
|
||||
"@ingroup enviroMisc"
|
||||
);
|
||||
|
||||
SceneGroup::SceneGroup()
|
||||
{
|
||||
// Not ghosted unless we're editing
|
||||
mNetFlags.clear(Ghostable | ScopeAlways);
|
||||
|
||||
mTypeMask |= StaticObjectType;
|
||||
}
|
||||
|
||||
SceneGroup::~SceneGroup()
|
||||
{
|
||||
}
|
||||
|
||||
void SceneGroup::initPersistFields()
|
||||
{
|
||||
docsURL;
|
||||
|
||||
addGroup("SceneGroup");
|
||||
endGroup("SceneGroup");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool SceneGroup::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
mObjBox.set(Point3F(-0.5f, -0.5f, -0.5f),
|
||||
Point3F(0.5f, 0.5f, 0.5f));
|
||||
|
||||
resetWorldBox();
|
||||
|
||||
// Not added to the scene unless we are editing.
|
||||
if (gEditingMission)
|
||||
onEditorEnable();
|
||||
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneGroup::onRemove()
|
||||
{
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void SceneGroup::onEditorEnable()
|
||||
{
|
||||
if (isClientObject())
|
||||
return;
|
||||
|
||||
// Just in case we are already in the scene, lets not cause an assert.
|
||||
if (mContainer != NULL)
|
||||
return;
|
||||
|
||||
// Enable ghosting so we can see this on the client.
|
||||
mNetFlags.set(Ghostable);
|
||||
setScopeAlways();
|
||||
addToScene();
|
||||
|
||||
Parent::onEditorEnable();
|
||||
}
|
||||
|
||||
void SceneGroup::onEditorDisable()
|
||||
{
|
||||
if (isClientObject())
|
||||
return;
|
||||
|
||||
// Just in case we are not in the scene, lets not cause an assert.
|
||||
if (mContainer == NULL)
|
||||
return;
|
||||
|
||||
// Do not need this on the client if we are not editing.
|
||||
removeFromScene();
|
||||
mNetFlags.clear(Ghostable);
|
||||
clearScopeAlways();
|
||||
|
||||
Parent::onEditorDisable();
|
||||
}
|
||||
|
||||
void SceneGroup::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
}
|
||||
|
||||
void SceneGroup::onInspect(GuiInspector* inspector)
|
||||
{
|
||||
//Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
|
||||
GuiInspectorGroup* sceneGroupGrp = inspector->findExistentGroup(StringTable->insert("SceneGroup"));
|
||||
if (!sceneGroupGrp)
|
||||
return;
|
||||
|
||||
GuiControl* stack = dynamic_cast<GuiControl*>(sceneGroupGrp->findObjectByInternalName(StringTable->insert("Stack")));
|
||||
|
||||
//Regen bounds button
|
||||
GuiInspectorField* regenFieldGui = sceneGroupGrp->createInspectorField();
|
||||
regenFieldGui->init(inspector, sceneGroupGrp);
|
||||
|
||||
regenFieldGui->setSpecialEditField(true);
|
||||
regenFieldGui->setTargetObject(this);
|
||||
|
||||
StringTableEntry fldnm = StringTable->insert("RegenerateBounds");
|
||||
|
||||
regenFieldGui->setSpecialEditVariableName(fldnm);
|
||||
|
||||
regenFieldGui->setInspectorField(NULL, fldnm);
|
||||
regenFieldGui->setDocs("");
|
||||
|
||||
stack->addObject(regenFieldGui);
|
||||
|
||||
GuiButtonCtrl* regenButton = new GuiButtonCtrl();
|
||||
regenButton->registerObject();
|
||||
regenButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
|
||||
regenButton->setText("Regenerate Bounds");
|
||||
regenButton->resize(Point2I::Zero, regenFieldGui->getExtent());
|
||||
regenButton->setHorizSizing(GuiControl::horizResizeWidth);
|
||||
regenButton->setVertSizing(GuiControl::vertResizeHeight);
|
||||
|
||||
char rgBuffer[512];
|
||||
dSprintf(rgBuffer, 512, "%d.recalculateBounds();", this->getId());
|
||||
regenButton->setConsoleCommand(rgBuffer);
|
||||
|
||||
regenFieldGui->addObject(regenButton);
|
||||
}
|
||||
|
||||
void SceneGroup::setTransform(const MatrixF& mat)
|
||||
{
|
||||
//transform difference
|
||||
MatrixF oldTransform = getTransform();
|
||||
|
||||
Parent::setTransform(mat);
|
||||
|
||||
// Calculate the delta transformation
|
||||
MatrixF deltaTransform;
|
||||
oldTransform.inverse();
|
||||
deltaTransform.mul(oldTransform, getTransform());
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
setMaskBits(TransformMask);
|
||||
|
||||
// Update all child transforms
|
||||
for (SimSetIterator itr(this); *itr; ++itr)
|
||||
{
|
||||
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
|
||||
if (child)
|
||||
{
|
||||
MatrixF childTransform = child->getTransform();
|
||||
MatrixF relativeTransform;
|
||||
relativeTransform.mul(deltaTransform, childTransform);
|
||||
child->setTransform(relativeTransform);
|
||||
child->setScale(childTransform.getScale()); //we don't modify scale
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneGroup::setRenderTransform(const MatrixF& mat)
|
||||
{
|
||||
//transform difference
|
||||
MatrixF oldTransform = getRenderTransform();
|
||||
|
||||
Parent::setRenderTransform(mat);
|
||||
|
||||
// Calculate the delta transformation
|
||||
MatrixF deltaTransform;
|
||||
oldTransform.inverse();
|
||||
deltaTransform.mul(oldTransform, getRenderTransform());
|
||||
|
||||
// Update all child transforms
|
||||
for (SimSetIterator itr(this); *itr; ++itr)
|
||||
{
|
||||
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
|
||||
if (child)
|
||||
{
|
||||
MatrixF childTransform = child->getRenderTransform();
|
||||
MatrixF relativeTransform;
|
||||
relativeTransform.mul(deltaTransform, childTransform);
|
||||
child->setRenderTransform(relativeTransform);
|
||||
child->setScale(childTransform.getScale()); //we don't modify scale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneGroup::addObject(SimObject* object)
|
||||
{
|
||||
Parent::addObject(object);
|
||||
|
||||
// Recalculate the bounding box from scratch (simpler but potentially costly)
|
||||
recalculateBoundingBox();
|
||||
}
|
||||
|
||||
void SceneGroup::removeObject(SimObject* object)
|
||||
{
|
||||
Parent::removeObject(object);
|
||||
|
||||
// Recalculate the bounding box from scratch (simpler but potentially costly)
|
||||
recalculateBoundingBox();
|
||||
}
|
||||
|
||||
void SceneGroup::recalculateBoundingBox()
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
// Reset the bounding box
|
||||
Box3F bounds;
|
||||
|
||||
bounds.minExtents.set(1e10, 1e10, 1e10);
|
||||
bounds.maxExtents.set(-1e10, -1e10, -1e10);
|
||||
|
||||
// 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();
|
||||
|
||||
bounds.minExtents.setMin(childBox.minExtents);
|
||||
bounds.maxExtents.setMax(childBox.maxExtents);
|
||||
}
|
||||
}
|
||||
|
||||
MatrixF newTrans = mObjToWorld;
|
||||
newTrans.setPosition(bounds.getCenter());
|
||||
Parent::setTransform(newTrans);
|
||||
|
||||
mObjScale = Point3F(bounds.len_x(), bounds.len_y(), bounds.len_z());
|
||||
mWorldBox = bounds;
|
||||
resetObjectBox();
|
||||
|
||||
setMaskBits(TransformMask);
|
||||
}
|
||||
|
||||
U32 SceneGroup::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
||||
|
||||
mathWrite(*stream, mObjBox);
|
||||
|
||||
if (stream->writeFlag(mask & TransformMask))
|
||||
{
|
||||
mathWrite(*stream, getTransform());
|
||||
mathWrite(*stream, getScale());
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void SceneGroup::unpackUpdate(NetConnection* conn, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
mathRead(*stream, &mObjBox);
|
||||
resetWorldBox();
|
||||
|
||||
// TransformMask
|
||||
if (stream->readFlag())
|
||||
{
|
||||
mathRead(*stream, &mObjToWorld);
|
||||
mathRead(*stream, &mObjScale);
|
||||
|
||||
setTransform(mObjToWorld);
|
||||
}
|
||||
}
|
||||
|
||||
bool SceneGroup::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
|
||||
{
|
||||
Vector<SceneObject*> foundObjects;
|
||||
if (empty())
|
||||
{
|
||||
Con::warnf("SceneGroup::buildPolyList() - SceneGroup %s is empty!", getName());
|
||||
return false;
|
||||
}
|
||||
findObjectByType(foundObjects);
|
||||
|
||||
for (S32 i = 0; i < foundObjects.size(); i++)
|
||||
{
|
||||
foundObjects[i]->buildPolyList(context, polyList, box, sphere);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SceneGroup::buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF& sphere)
|
||||
{
|
||||
Vector<SceneObject*> foundObjects;
|
||||
findObjectByType(foundObjects);
|
||||
|
||||
for (S32 i = 0; i < foundObjects.size(); i++)
|
||||
{
|
||||
foundObjects[i]->buildExportPolyList(exportData, box, sphere);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneGroup::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
|
||||
{
|
||||
//if (empty())
|
||||
return;
|
||||
|
||||
Vector<SceneObject*> foundObjects;
|
||||
findObjectByType(foundObjects);
|
||||
|
||||
for (S32 i = 0; i < foundObjects.size(); i++)
|
||||
{
|
||||
SceneObject* child = foundObjects[i];
|
||||
|
||||
child->getUtilizedAssets(usedAssetsList);
|
||||
}
|
||||
}
|
||||
|
||||
DefineEngineMethod(SceneGroup, recalculateBounds, void, (), ,
|
||||
"Recalculates the SceneGroups' bounds and centerpoint.\n")
|
||||
{
|
||||
object->recalculateBoundingBox();
|
||||
}
|
||||
55
Engine/source/T3D/SceneGroup.h
Normal file
55
Engine/source/T3D/SceneGroup.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#ifndef SCENE_GROUP_H
|
||||
#define SCENE_GROUP_H
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
class SceneGroup : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
public:
|
||||
enum MaskBits
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask << 0,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
public:
|
||||
SceneGroup();
|
||||
virtual ~SceneGroup();
|
||||
|
||||
DECLARE_CONOBJECT(SceneGroup);
|
||||
DECLARE_CATEGORY("Object \t Collection");
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
// SimObject
|
||||
bool onAdd() override;
|
||||
void onRemove() override;
|
||||
void onEditorEnable() override;
|
||||
void onEditorDisable() override;
|
||||
void inspectPostApply() override;
|
||||
void onInspect(GuiInspector* inspector) override;
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection* conn, U32 mask, BitStream* stream) override;
|
||||
void unpackUpdate(NetConnection* conn, BitStream* stream) override;
|
||||
|
||||
// SceneObject
|
||||
void setTransform(const MatrixF& mat) override;
|
||||
void setRenderTransform(const MatrixF& mat) override;
|
||||
|
||||
// Object management
|
||||
void addObject(SimObject* object) override;
|
||||
void removeObject(SimObject* object) override;
|
||||
void recalculateBoundingBox();
|
||||
|
||||
///
|
||||
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) override;
|
||||
bool buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF&) override;
|
||||
void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList) override;
|
||||
};
|
||||
#endif
|
||||
427
Engine/source/T3D/SubScene.cpp
Normal file
427
Engine/source/T3D/SubScene.cpp
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
#include "SubScene.h"
|
||||
|
||||
#include "gameMode.h"
|
||||
#include "console/script.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "gui/editor/inspector/group.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SubScene);
|
||||
|
||||
S32 SubScene::mUnloadTimeoutMs = 5000;
|
||||
|
||||
SubScene::SubScene() :
|
||||
mLevelAssetId(StringTable->EmptyString()),
|
||||
mGameModesNames(StringTable->EmptyString()),
|
||||
mScopeDistance(-1),
|
||||
mLoaded(false),
|
||||
mGlobalLayer(false)
|
||||
{
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
mTypeMask |= StaticObjectType;
|
||||
}
|
||||
|
||||
SubScene::~SubScene()
|
||||
{
|
||||
}
|
||||
|
||||
bool SubScene::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SubScene::onRemove()
|
||||
{
|
||||
if (isClientObject())
|
||||
removeFromScene();
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void SubScene::initPersistFields()
|
||||
{
|
||||
addGroup("SubScene");
|
||||
addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), "");
|
||||
INITPERSISTFIELD_LEVELASSET(Level, SubScene, "The level asset to load.");
|
||||
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
|
||||
endGroup("SubScene");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void SubScene::consoleInit()
|
||||
{
|
||||
Parent::consoleInit();
|
||||
|
||||
Con::addVariable("$SubScene::UnloadTimeoutMS", TypeBool, &SubScene::mUnloadTimeoutMs, "The amount of time in milliseconds it takes for a SubScene to be unloaded if it's inactive.\n"
|
||||
"@ingroup Editors\n");
|
||||
}
|
||||
|
||||
void SubScene::addObject(SimObject* object)
|
||||
{
|
||||
SceneObject::addObject(object);
|
||||
}
|
||||
|
||||
void SubScene::removeObject(SimObject* object)
|
||||
{
|
||||
SceneObject::removeObject(object);
|
||||
}
|
||||
|
||||
U32 SubScene::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
||||
|
||||
stream->writeFlag(mGlobalLayer);
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void SubScene::unpackUpdate(NetConnection* conn, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
mGlobalLayer = stream->readFlag();
|
||||
|
||||
}
|
||||
|
||||
void SubScene::onInspect(GuiInspector* inspector)
|
||||
{
|
||||
Parent::onInspect(inspector);
|
||||
|
||||
//Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
|
||||
GuiInspectorGroup* subsceneGrp = inspector->findExistentGroup(StringTable->insert("SubScene"));
|
||||
if (!subsceneGrp)
|
||||
return;
|
||||
|
||||
GuiControl* stack = dynamic_cast<GuiControl*>(subsceneGrp->findObjectByInternalName(StringTable->insert("Stack")));
|
||||
|
||||
//Save button
|
||||
GuiInspectorField* saveFieldGui = subsceneGrp->createInspectorField();
|
||||
saveFieldGui->init(inspector, subsceneGrp);
|
||||
|
||||
saveFieldGui->setSpecialEditField(true);
|
||||
saveFieldGui->setTargetObject(this);
|
||||
|
||||
StringTableEntry fldnm = StringTable->insert("SaveSubScene");
|
||||
|
||||
saveFieldGui->setSpecialEditVariableName(fldnm);
|
||||
|
||||
saveFieldGui->setInspectorField(NULL, fldnm);
|
||||
saveFieldGui->setDocs("");
|
||||
|
||||
stack->addObject(saveFieldGui);
|
||||
|
||||
GuiButtonCtrl* saveButton = new GuiButtonCtrl();
|
||||
saveButton->registerObject();
|
||||
saveButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
|
||||
saveButton->setText("Save SubScene");
|
||||
saveButton->resize(Point2I::Zero, saveFieldGui->getExtent());
|
||||
saveButton->setHorizSizing(GuiControl::horizResizeWidth);
|
||||
saveButton->setVertSizing(GuiControl::vertResizeHeight);
|
||||
|
||||
char szBuffer[512];
|
||||
dSprintf(szBuffer, 512, "%d.save();", this->getId());
|
||||
saveButton->setConsoleCommand(szBuffer);
|
||||
|
||||
saveFieldGui->addObject(saveButton);
|
||||
}
|
||||
|
||||
void SubScene::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits(-1);
|
||||
}
|
||||
|
||||
bool SubScene::testBox(const Box3F& testBox)
|
||||
{
|
||||
if (mGlobalLayer)
|
||||
return true;
|
||||
|
||||
bool isOverlapped = getWorldBox().isOverlapped(testBox);
|
||||
|
||||
return getWorldBox().isOverlapped(testBox);
|
||||
}
|
||||
|
||||
void SubScene::write(Stream& stream, U32 tabStop, U32 flags)
|
||||
{
|
||||
MutexHandle handle;
|
||||
handle.lock(mMutex);
|
||||
|
||||
// export selected only?
|
||||
if ((flags & SelectedOnly) && !isSelected())
|
||||
{
|
||||
for (U32 i = 0; i < size(); i++)
|
||||
(*this)[i]->write(stream, tabStop, flags);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
stream.writeTabs(tabStop);
|
||||
char buffer[2048];
|
||||
const U32 bufferWriteLen = dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() && !(flags & NoName) ? getName() : "");
|
||||
stream.write(bufferWriteLen, buffer);
|
||||
writeFields(stream, tabStop + 1);
|
||||
|
||||
//The only meaningful difference between this and simSet for writing is we skip the children, since they're just the levelAsset contents
|
||||
|
||||
stream.writeTabs(tabStop);
|
||||
stream.write(4, "};\r\n");
|
||||
}
|
||||
|
||||
void SubScene::processTick(const Move* move)
|
||||
{
|
||||
}
|
||||
|
||||
void SubScene::_onFileChanged(const Torque::Path& path)
|
||||
{
|
||||
if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path)
|
||||
return;
|
||||
|
||||
AssertFatal(path == mLevelAsset->getLevelPath(), "Prefab::_onFileChanged - path does not match filename.");
|
||||
|
||||
_closeFile(false);
|
||||
_loadFile(false);
|
||||
setMaskBits(U32_MAX);
|
||||
}
|
||||
|
||||
void SubScene::_closeFile(bool removeFileNotify)
|
||||
{
|
||||
AssertFatal(isServerObject(), "Trying to close out a subscene file on the client is bad!");
|
||||
|
||||
U32 count = size();
|
||||
|
||||
for (SimSetIterator itr(this); *itr; ++itr)
|
||||
{
|
||||
SimObject* child = dynamic_cast<SimObject*>(*itr);
|
||||
|
||||
if (child)
|
||||
child->safeDeleteObject();
|
||||
}
|
||||
|
||||
if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString())
|
||||
{
|
||||
Torque::FS::RemoveChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
||||
}
|
||||
|
||||
mGameModesList.clear();
|
||||
}
|
||||
|
||||
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())
|
||||
return;
|
||||
|
||||
String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath());
|
||||
|
||||
String instantGroup = Con::getVariable("InstantGroup");
|
||||
Con::setIntVariable("InstantGroup", this->getId());
|
||||
Con::evaluate((const char*)evalCmd.c_str(), false, mLevelAsset->getLevelPath());
|
||||
Con::setVariable("InstantGroup", instantGroup.c_str());
|
||||
|
||||
if (addFileNotify)
|
||||
Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
|
||||
}
|
||||
|
||||
void SubScene::load()
|
||||
{
|
||||
mStartUnloadTimerMS = -1; //reset unload timers
|
||||
|
||||
//no need to load multiple times
|
||||
if (mLoaded)
|
||||
return;
|
||||
|
||||
_loadFile(true);
|
||||
mLoaded = true;
|
||||
|
||||
GameMode::findGameModes(mGameModesNames, &mGameModesList);
|
||||
|
||||
for (U32 i = 0; i < mGameModesList.size(); i++)
|
||||
{
|
||||
mGameModesList[i]->onSubsceneLoaded_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void SubScene::unload()
|
||||
{
|
||||
if (!mLoaded)
|
||||
return;
|
||||
|
||||
if (isSelected())
|
||||
{
|
||||
mStartUnloadTimerMS = Sim::getCurrentTime();
|
||||
return; //if a child is selected, then we don't want to unload
|
||||
}
|
||||
|
||||
//scan down through our child objects, see if any are marked as selected,
|
||||
//if so, skip unloading and reset the timer
|
||||
for (SimSetIterator itr(this); *itr; ++itr)
|
||||
{
|
||||
SimGroup* childGrp = dynamic_cast<SimGroup*>(*itr);
|
||||
if (childGrp && childGrp->isSelected())
|
||||
{
|
||||
mStartUnloadTimerMS = Sim::getCurrentTime();
|
||||
return; //if a child is selected, then we don't want to unload
|
||||
}
|
||||
|
||||
for (SimSetIterator cldItr(childGrp); *cldItr; ++cldItr)
|
||||
{
|
||||
SimObject* chldChld = dynamic_cast<SimObject*>(*cldItr);
|
||||
if (chldChld && chldChld->isSelected())
|
||||
{
|
||||
mStartUnloadTimerMS = Sim::getCurrentTime();
|
||||
return; //if a child is selected, then we don't want to unload
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_closeFile(true);
|
||||
mLoaded = false;
|
||||
|
||||
for (U32 i = 0; i < mGameModesList.size(); i++)
|
||||
{
|
||||
mGameModesList[i]->onSubsceneUnloaded_callback();
|
||||
}
|
||||
}
|
||||
|
||||
bool SubScene::save()
|
||||
{
|
||||
//if there's nothing TO save, don't bother
|
||||
if (size() == 0 || !isLoaded())
|
||||
return false;
|
||||
|
||||
if (mLevelAsset.isNull())
|
||||
return false;
|
||||
|
||||
//If we're flagged for unload, push back the unload timer so we can't accidentally trip be saving partway through an unload
|
||||
if (mStartUnloadTimerMS != -1)
|
||||
mStartUnloadTimerMS = Sim::getCurrentTime();
|
||||
|
||||
StringTableEntry levelPath = mLevelAsset->getLevelPath();
|
||||
|
||||
for (SimGroup::iterator itr = begin(); itr != end(); itr++)
|
||||
{
|
||||
//Just in case there's a valid callback the scene object would like to invoke for saving
|
||||
SceneObject* gc = dynamic_cast<SceneObject*>(*itr);
|
||||
if (gc)
|
||||
{
|
||||
gc->onSaving_callback(mLevelAssetId);
|
||||
}
|
||||
|
||||
SimObject* sO = static_cast<SimObject*>(*itr);
|
||||
|
||||
if (!sO->save(levelPath))
|
||||
{
|
||||
Con::errorf("SubScene::save() - error, failed to write object %s to file: %s", sO->getIdString(), levelPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//process our gameModeList and write it out to the levelAsset for metadata stashing
|
||||
bool saveSuccess = false;
|
||||
|
||||
//Get the level asset
|
||||
if (mLevelAsset.isNull())
|
||||
return saveSuccess;
|
||||
|
||||
//update the gamemode list as well
|
||||
mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
|
||||
|
||||
//Finally, save
|
||||
saveSuccess = mLevelAsset->saveAsset();
|
||||
|
||||
}
|
||||
|
||||
void SubScene::_onSelected()
|
||||
{
|
||||
if (!isLoaded() && isServerObject())
|
||||
load();
|
||||
}
|
||||
|
||||
void SubScene::_onUnselected()
|
||||
{
|
||||
}
|
||||
|
||||
void SubScene::prepRenderImage(SceneRenderState* state)
|
||||
{
|
||||
// only render if selected or render flag is set
|
||||
if (/*!smRenderTriggers && */!isSelected())
|
||||
return;
|
||||
|
||||
ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||
ri->renderDelegate.bind(this, &SubScene::renderObject);
|
||||
ri->type = RenderPassManager::RIT_Editor;
|
||||
ri->translucentSort = true;
|
||||
ri->defaultKey = 1;
|
||||
state->getRenderPass()->addInst(ri);
|
||||
}
|
||||
|
||||
void SubScene::renderObject(ObjectRenderInst* ri,
|
||||
SceneRenderState* state,
|
||||
BaseMatInstance* overrideMat)
|
||||
{
|
||||
if (overrideMat)
|
||||
return;
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setZReadWrite(true, false);
|
||||
desc.setBlend(true);
|
||||
|
||||
// Trigger polyhedrons are set up with outward facing normals and CCW ordering
|
||||
// so can't enable backface culling.
|
||||
desc.setCullMode(GFXCullNone);
|
||||
|
||||
GFXTransformSaver saver;
|
||||
|
||||
MatrixF mat = getRenderTransform();
|
||||
GFX->multWorld(mat);
|
||||
|
||||
GFXDrawUtil* drawer = GFX->getDrawUtil();
|
||||
|
||||
//Box3F scale = getScale()
|
||||
//Box3F bounds = Box3F(-m)
|
||||
|
||||
Point3F scale = getScale();
|
||||
Box3F bounds = Box3F(-scale/2, scale/2);
|
||||
|
||||
ColorI boundsColor = ColorI(135, 206, 235, 50);
|
||||
|
||||
if (mGlobalLayer)
|
||||
boundsColor = ColorI(200, 100, 100, 25);
|
||||
else if (mLoaded)
|
||||
boundsColor = ColorI(50, 200, 50, 50);
|
||||
|
||||
drawer->drawCube(desc, bounds, boundsColor);
|
||||
|
||||
// Render wireframe.
|
||||
|
||||
desc.setFillModeWireframe();
|
||||
drawer->drawCube(desc, bounds, ColorI::BLACK);
|
||||
}
|
||||
|
||||
DefineEngineMethod(SubScene, save, bool, (),,
|
||||
"Save out the subScene.\n")
|
||||
{
|
||||
return object->save();
|
||||
}
|
||||
|
||||
|
||||
DefineEngineMethod(SubScene, load, void, (), ,
|
||||
"Loads the SubScene's level file.\n")
|
||||
{
|
||||
object->load();
|
||||
}
|
||||
|
||||
DefineEngineMethod(SubScene, unload, void, (), ,
|
||||
"Unloads the SubScene's level file.\n")
|
||||
{
|
||||
object->unload();
|
||||
}
|
||||
108
Engine/source/T3D/SubScene.h
Normal file
108
Engine/source/T3D/SubScene.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
#ifndef SUB_SCENE_H
|
||||
#define SUB_SCENE_H
|
||||
|
||||
#ifndef SCENE_GROUP_H
|
||||
#include "SceneGroup.h"
|
||||
#endif
|
||||
#ifndef LEVEL_ASSET_H
|
||||
#include "assets/LevelAsset.h"
|
||||
#endif
|
||||
#ifndef GAME_MODE_H
|
||||
#include "gameMode.h"
|
||||
#endif
|
||||
|
||||
class SubScene : public SceneGroup
|
||||
{
|
||||
typedef SceneGroup Parent;
|
||||
|
||||
public:
|
||||
enum MaskBits
|
||||
{
|
||||
NextFreeMask = Parent::NextFreeMask << 0
|
||||
};
|
||||
|
||||
void onLevelChanged() {}
|
||||
|
||||
private:
|
||||
DECLARE_LEVELASSET(SubScene, Level, onLevelChanged);
|
||||
|
||||
StringTableEntry mGameModesNames;
|
||||
Vector<GameMode*> mGameModesList;
|
||||
|
||||
F32 mScopeDistance;
|
||||
|
||||
/// <summary>
|
||||
/// How long we wait once every control object has left the SubScene's load boundary for us to unload the levelAsset.
|
||||
/// </summary>
|
||||
S32 mStartUnloadTimerMS;
|
||||
|
||||
bool mLoaded;
|
||||
|
||||
bool mGlobalLayer;
|
||||
public:
|
||||
SubScene();
|
||||
virtual ~SubScene();
|
||||
|
||||
DECLARE_CONOBJECT(SubScene);
|
||||
DECLARE_CATEGORY("Object \t Collection");
|
||||
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
// SimObject
|
||||
bool onAdd() override;
|
||||
void onRemove() override;
|
||||
|
||||
U32 packUpdate(NetConnection* conn, U32 mask, BitStream* stream) override;
|
||||
void unpackUpdate(NetConnection* conn, BitStream* stream) override;
|
||||
|
||||
void addObject(SimObject* object);
|
||||
void removeObject(SimObject* object);
|
||||
//void onEditorEnable() override;
|
||||
//void onEditorDisable() override;
|
||||
void inspectPostApply() override;
|
||||
|
||||
bool testBox(const Box3F& testBox);
|
||||
|
||||
void _onSelected() override;
|
||||
void _onUnselected() override;
|
||||
|
||||
static S32 mUnloadTimeoutMs;
|
||||
|
||||
protected:
|
||||
void write(Stream& stream, U32 tabStop, U32 flags = 0) override;
|
||||
|
||||
//
|
||||
void _onFileChanged(const Torque::Path& path);
|
||||
void _closeFile(bool removeFileNotify);
|
||||
void _loadFile(bool addFileNotify);
|
||||
|
||||
//
|
||||
public:
|
||||
void processTick(const Move* move) override;
|
||||
|
||||
//
|
||||
void onInspect(GuiInspector* inspector) override;
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
|
||||
void prepRenderImage(SceneRenderState* state) override;
|
||||
void renderObject(ObjectRenderInst* ri,
|
||||
SceneRenderState* state,
|
||||
BaseMatInstance* overrideMat);
|
||||
|
||||
bool isLoaded() { return mLoaded; }
|
||||
void setUnloadTimeMS(S32 unloadTimeMS) {
|
||||
mStartUnloadTimerMS = unloadTimeMS;
|
||||
}
|
||||
inline S32 getUnloadTimsMS() {
|
||||
return mStartUnloadTimerMS;
|
||||
}
|
||||
|
||||
bool save();
|
||||
|
||||
DECLARE_ASSET_SETGET(SubScene, Level);
|
||||
};
|
||||
#endif
|
||||
|
|
@ -104,7 +104,7 @@ ConsoleSetType(TypeImageAssetId)
|
|||
}
|
||||
|
||||
// Warn.
|
||||
Con::warnf("(TypeAssetId) - Cannot set multiple args to a single asset.");
|
||||
Con::warnf("(TypeImageAssetId) - Cannot set multiple args to a single asset.");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -748,7 +748,7 @@ void GuiInspectorTypeImageAssetPtr::setPreviewImage(StringTableEntry assetId)
|
|||
IMPLEMENT_CONOBJECT(GuiInspectorTypeImageAssetId);
|
||||
|
||||
ConsoleDocClass(GuiInspectorTypeImageAssetId,
|
||||
"@brief Inspector field type for Shapes\n\n"
|
||||
"@brief Inspector field type for Images\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -42,12 +42,14 @@
|
|||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT(LevelAsset);
|
||||
|
||||
ConsoleType(LevelAssetPtr, TypeLevelAssetPtr, const char*, ASSET_ID_FIELD_PREFIX)
|
||||
ConsoleType(LevelAssetPtr, TypeLevelAssetPtr, const char*, "")
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -74,6 +76,28 @@ ConsoleSetType(TypeLevelAssetPtr)
|
|||
Con::warnf("(TypeLevelAssetPtr) - Cannot set multiple args to a single asset.");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ConsoleType(assetIdString, TypeLevelAssetId, const char*, "")
|
||||
|
||||
ConsoleGetType(TypeLevelAssetId)
|
||||
{
|
||||
// Fetch asset Id.
|
||||
return *((const char**)(dptr));
|
||||
}
|
||||
|
||||
ConsoleSetType(TypeLevelAssetId)
|
||||
{
|
||||
// Was a single argument specified?
|
||||
if (argc == 1)
|
||||
{
|
||||
*((const char**)dptr) = StringTable->insert(argv[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Warn.
|
||||
Con::warnf("(TypeLevelAssetId) - Cannot set multiple args to a single asset.");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
|
||||
|
|
@ -91,7 +115,7 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
|
|||
mForestPath = StringTable->EmptyString();
|
||||
mNavmeshPath = StringTable->EmptyString();
|
||||
|
||||
mGamemodeName = StringTable->EmptyString();
|
||||
mGameModesNames = StringTable->EmptyString();
|
||||
mMainLevelAsset = StringTable->EmptyString();
|
||||
|
||||
mEditorFile = StringTable->EmptyString();
|
||||
|
|
@ -134,7 +158,7 @@ void LevelAsset::initPersistFields()
|
|||
&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("gameModeName", TypeString, Offset(mGamemodeName, LevelAsset), "Name of the Game Mode to be used with this level");
|
||||
addField("gameModesNames", TypeString, Offset(mGameModesNames, LevelAsset), "Name of the Game Mode to be used with this level");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -357,7 +381,7 @@ void LevelAsset::unloadDependencies()
|
|||
}
|
||||
}
|
||||
|
||||
DefineEngineMethod(LevelAsset, getLevelPath, const char*, (),,
|
||||
DefineEngineMethod(LevelAsset, getLevelPath, const char*, (), ,
|
||||
"Gets the full path of the asset's defined level file.\n"
|
||||
"@return The string result of the level path")
|
||||
{
|
||||
|
|
@ -417,3 +441,99 @@ DefineEngineMethod(LevelAsset, unloadDependencies, void, (), ,
|
|||
{
|
||||
return object->unloadDependencies();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GuiInspectorTypeAssetId
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiInspectorTypeLevelAssetPtr);
|
||||
|
||||
ConsoleDocClass(GuiInspectorTypeLevelAssetPtr,
|
||||
"@brief Inspector field type for Shapes\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
void GuiInspectorTypeLevelAssetPtr::consoleInit()
|
||||
{
|
||||
Parent::consoleInit();
|
||||
|
||||
ConsoleBaseType::getType(TypeLevelAssetPtr)->setInspectorFieldType("GuiInspectorTypeLevelAssetPtr");
|
||||
}
|
||||
|
||||
GuiControl* GuiInspectorTypeLevelAssetPtr::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(\"LevelAsset\", \"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(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
|
||||
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 GuiInspectorTypeLevelAssetPtr::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(GuiInspectorTypeLevelAssetId);
|
||||
|
||||
ConsoleDocClass(GuiInspectorTypeLevelAssetId,
|
||||
"@brief Inspector field type for Levels\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
void GuiInspectorTypeLevelAssetId::consoleInit()
|
||||
{
|
||||
Parent::consoleInit();
|
||||
|
||||
ConsoleBaseType::getType(TypeLevelAssetId)->setInspectorFieldType("GuiInspectorTypeLevelAssetId");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@
|
|||
#endif
|
||||
#include "T3D/assets/ImageAsset.h"
|
||||
|
||||
#ifndef _GUI_INSPECTOR_TYPES_H_
|
||||
#include "gui/editor/guiInspectorTypes.h"
|
||||
#endif
|
||||
#include <gui/controls/guiBitmapCtrl.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class LevelAsset : public AssetBase
|
||||
{
|
||||
|
|
@ -64,7 +69,7 @@ class LevelAsset : public AssetBase
|
|||
bool mIsSubLevel;
|
||||
StringTableEntry mMainLevelAsset;
|
||||
|
||||
StringTableEntry mGamemodeName;
|
||||
StringTableEntry mGameModesNames;
|
||||
|
||||
Vector<AssetBase*> mAssetDependencies;
|
||||
|
||||
|
|
@ -114,7 +119,7 @@ public:
|
|||
U32 load() override { return Ok; };
|
||||
|
||||
protected:
|
||||
static bool setLevelFile(void *obj, const char *index, const char *data) { static_cast<LevelAsset*>(obj)->setLevelFile(data); return false; }
|
||||
static bool setLevelFile(void* obj, const char* index, const char* data) { static_cast<LevelAsset*>(obj)->setLevelFile(data); return false; }
|
||||
static const char* getLevelFile(void* obj, const char* data) { return static_cast<LevelAsset*>(obj)->getLevelFile(); }
|
||||
|
||||
static bool setEditorFile(void* obj, const char* index, const char* data) { static_cast<LevelAsset*>(obj)->setEditorFile(data); return false; }
|
||||
|
|
@ -135,10 +140,96 @@ protected:
|
|||
|
||||
void initializeAsset(void) override;
|
||||
void onAssetRefresh(void) override;
|
||||
void loadAsset();
|
||||
void loadAsset();
|
||||
|
||||
typedef Signal<void()> LevelAssetChanged;
|
||||
LevelAssetChanged mChangeSignal;
|
||||
|
||||
public:
|
||||
LevelAssetChanged& getChangedSignal() { return mChangeSignal; }
|
||||
};
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
class GuiInspectorTypeLevelAssetPtr : public GuiInspectorTypeFileName
|
||||
{
|
||||
typedef GuiInspectorTypeFileName Parent;
|
||||
public:
|
||||
|
||||
GuiBitmapButtonCtrl* mEditButton;
|
||||
|
||||
DECLARE_CONOBJECT(GuiInspectorTypeLevelAssetPtr);
|
||||
static void consoleInit();
|
||||
|
||||
GuiControl* constructEditControl() override;
|
||||
bool updateRects() override;
|
||||
};
|
||||
|
||||
class GuiInspectorTypeLevelAssetId : public GuiInspectorTypeLevelAssetPtr
|
||||
{
|
||||
typedef GuiInspectorTypeLevelAssetPtr Parent;
|
||||
public:
|
||||
|
||||
DECLARE_CONOBJECT(GuiInspectorTypeLevelAssetId);
|
||||
static void consoleInit();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
DefineConsoleType(TypeLevelAssetPtr, LevelAsset)
|
||||
DefineConsoleType(TypeLevelAssetId, String)
|
||||
|
||||
#pragma region Singular Asset Macros
|
||||
|
||||
//Singular assets
|
||||
/// <Summary>
|
||||
/// Declares an level asset
|
||||
/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions
|
||||
/// </Summary>
|
||||
#define DECLARE_LEVELASSET(className, name, changeFunc) public: \
|
||||
StringTableEntry m##name##AssetId;\
|
||||
AssetPtr<LevelAsset> m##name##Asset;\
|
||||
public: \
|
||||
const AssetPtr<LevelAsset> & get##name##Asset() const { return m##name##Asset; }\
|
||||
void set##name##Asset(const AssetPtr<LevelAsset> &_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_LEVELASSET(name, consoleClass, docs) \
|
||||
addProtectedField(assetText(name, Asset), TypeLevelAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#endif // _ASSET_BASE_H_
|
||||
|
||||
|
|
|
|||
436
Engine/source/T3D/gameMode.cpp
Normal file
436
Engine/source/T3D/gameMode.cpp
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
#include "gameMode.h"
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
#include "gui/containers/guiDynamicCtrlArrayCtrl.h"
|
||||
#endif
|
||||
|
||||
IMPLEMENT_CONOBJECT(GameMode);
|
||||
|
||||
IMPLEMENT_CALLBACK(GameMode, onActivated, void, (), (),
|
||||
"@brief Called when a gamemode is activated.\n\n");
|
||||
IMPLEMENT_CALLBACK(GameMode, onDeactivated, void, (), (),
|
||||
"@brief Called when a gamemode is deactivated.\n\n");
|
||||
IMPLEMENT_CALLBACK(GameMode, onSceneLoaded, void, (), (),
|
||||
"@brief Called when a scene has been loaded and has game mode implications.\n\n");
|
||||
IMPLEMENT_CALLBACK(GameMode, onSceneUnloaded, void, (), (),
|
||||
"@brief Called when a scene has been unloaded and has game mode implications.\n\n");
|
||||
IMPLEMENT_CALLBACK(GameMode, onSubsceneLoaded, void, (), (),
|
||||
"@brief Called when a subScene has been loaded and has game mode implications.\n\n");
|
||||
IMPLEMENT_CALLBACK(GameMode, onSubsceneUnloaded, void, (), (),
|
||||
"@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
|
||||
|
||||
|
||||
ConsoleType(GameModeList, TypeGameModeList, const char*, "")
|
||||
|
||||
ConsoleGetType(TypeGameModeList)
|
||||
{
|
||||
// Fetch asset Id.
|
||||
return *((const char**)(dptr));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleSetType(TypeGameModeList)
|
||||
{
|
||||
// Was a single argument specified?
|
||||
if (argc == 1)
|
||||
{
|
||||
// Yes, so fetch field value.
|
||||
*((const char**)dptr) = StringTable->insert(argv[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Warn.
|
||||
//Con::warnf("(TypeGameModeList) - Cannot set multiple args to a single asset.");
|
||||
}
|
||||
|
||||
GameMode::GameMode() :
|
||||
mGameModeName(StringTable->EmptyString()),
|
||||
mGameModeDesc(StringTable->EmptyString())
|
||||
{
|
||||
INIT_ASSET(PreviewImage);
|
||||
}
|
||||
|
||||
void GameMode::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("gameModeName", TypeString, Offset(mGameModeName, GameMode), "Human-readable name of the gamemode");
|
||||
addField("description", TypeString, Offset(mGameModeDesc, GameMode), "Description of the gamemode");
|
||||
|
||||
INITPERSISTFIELD_IMAGEASSET(PreviewImage, GameMode, "Preview Image");
|
||||
|
||||
addField("active", TypeBool, Offset(mIsActive, GameMode), "Is the gamemode active");
|
||||
}
|
||||
|
||||
bool GameMode::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameMode::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void GameMode::findGameModes(const char* gameModeList, Vector<GameMode*> *outGameModes)
|
||||
{
|
||||
if (outGameModes == nullptr)
|
||||
return;
|
||||
|
||||
Vector<String> gameModeNames;
|
||||
U32 uCount = StringUnit::getUnitCount(gameModeList, ";");
|
||||
for (U32 i = 0; i < uCount; i++)
|
||||
{
|
||||
String name = StringUnit::getUnit(gameModeList, i, ";");
|
||||
if (!name.isEmpty())
|
||||
gameModeNames.push_back(name);
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < gameModeNames.size(); i++)
|
||||
{
|
||||
GameMode* gm;
|
||||
if (Sim::findObject(gameModeNames[i].c_str(), gm))
|
||||
{
|
||||
outGameModes->push_back(gm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameMode::setActive(const bool& active)
|
||||
{
|
||||
mIsActive = active;
|
||||
if (mIsActive)
|
||||
onActivated_callback();
|
||||
else
|
||||
onDeactivated_callback();
|
||||
}
|
||||
|
||||
DefineEngineMethod(GameMode, isActive, bool, (), ,
|
||||
"Returns if the GameMode is currently active.\n"
|
||||
"@return The active status of the GameMode")
|
||||
{
|
||||
return object->isActive();
|
||||
}
|
||||
|
||||
DefineEngineMethod(GameMode, setActive, void, (bool active), (true),
|
||||
"Sets the active state of the GameMode.\n"
|
||||
"@param active A bool of the state the GameMode should be set to")
|
||||
{
|
||||
object->setActive(active);
|
||||
}
|
||||
|
||||
DefineEngineFunction(getGameModesList, const char*, (), , "")
|
||||
{
|
||||
char* returnBuffer = Con::getReturnBuffer(1024);
|
||||
|
||||
String formattedList;
|
||||
|
||||
for (SimGroup::iterator itr = Sim::getRootGroup()->begin(); itr != Sim::getRootGroup()->end(); itr++)
|
||||
{
|
||||
GameMode* gm = dynamic_cast<GameMode*>(*itr);
|
||||
if (gm)
|
||||
{
|
||||
formattedList += String(gm->getName()) + ";";
|
||||
}
|
||||
}
|
||||
|
||||
dSprintf(returnBuffer, 1024, "%s", formattedList.c_str());
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GuiInspectorTypeAssetId
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef TORQUE_TOOLS
|
||||
|
||||
GuiInspectorTypeGameModeList::GuiInspectorTypeGameModeList()
|
||||
: mHelper(NULL),
|
||||
mRollout(NULL),
|
||||
mArrayCtrl(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
IMPLEMENT_CONOBJECT(GuiInspectorTypeGameModeList);
|
||||
|
||||
ConsoleDocClass(GuiInspectorTypeGameModeList,
|
||||
"@brief Inspector field type for selecting GameModes\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
bool GuiInspectorTypeGameModeList::onAdd()
|
||||
{
|
||||
// Skip our parent because we aren't using mEditCtrl
|
||||
// and according to our parent that would be cause to fail onAdd.
|
||||
if (!Parent::Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (!mInspector)
|
||||
return false;
|
||||
|
||||
//build out our list of gamemodes
|
||||
Vector<GameMode*> gameModesList;
|
||||
|
||||
for (SimGroup::iterator itr = Sim::getRootGroup()->begin(); itr != Sim::getRootGroup()->end(); itr++)
|
||||
{
|
||||
GameMode* gm = dynamic_cast<GameMode*>(*itr);
|
||||
if (gm)
|
||||
gameModesList.push_back(gm);
|
||||
}
|
||||
|
||||
static StringTableEntry sProfile = StringTable->insert("profile");
|
||||
setDataField(sProfile, NULL, "GuiInspectorFieldProfile");
|
||||
setBounds(0, 0, 100, 18);
|
||||
|
||||
// Allocate our children controls...
|
||||
|
||||
mRollout = new GuiRolloutCtrl();
|
||||
mRollout->setMargin(14, 0, 0, 0);
|
||||
mRollout->setCanCollapse(false);
|
||||
mRollout->registerObject();
|
||||
addObject(mRollout);
|
||||
|
||||
mArrayCtrl = new GuiDynamicCtrlArrayControl();
|
||||
mArrayCtrl->setDataField(sProfile, NULL, "GuiInspectorBitMaskArrayProfile");
|
||||
mArrayCtrl->setField("autoCellSize", "true");
|
||||
mArrayCtrl->setField("fillRowFirst", "true");
|
||||
mArrayCtrl->setField("dynamicSize", "true");
|
||||
mArrayCtrl->setField("rowSpacing", "4");
|
||||
mArrayCtrl->setField("colSpacing", "1");
|
||||
mArrayCtrl->setField("frozen", "true");
|
||||
mArrayCtrl->registerObject();
|
||||
|
||||
mRollout->addObject(mArrayCtrl);
|
||||
|
||||
GuiCheckBoxCtrl* pCheckBox = NULL;
|
||||
|
||||
for (S32 i = 0; i < gameModesList.size(); i++)
|
||||
{
|
||||
pCheckBox = new GuiCheckBoxCtrl();
|
||||
pCheckBox->setText(gameModesList[i]->getName());
|
||||
pCheckBox->registerObject();
|
||||
mArrayCtrl->addObject(pCheckBox);
|
||||
|
||||
pCheckBox->autoSize();
|
||||
|
||||
// Override the normal script callbacks for GuiInspectorTypeCheckBox
|
||||
char szBuffer[512];
|
||||
dSprintf(szBuffer, 512, "%d.applyValue();", getId());
|
||||
pCheckBox->setField("Command", szBuffer);
|
||||
}
|
||||
|
||||
mArrayCtrl->setField("frozen", "false");
|
||||
mArrayCtrl->refresh();
|
||||
|
||||
mHelper = new GuiInspectorTypeGameModeListHelper();
|
||||
mHelper->init(mInspector, mParent);
|
||||
mHelper->mParentRollout = mRollout;
|
||||
mHelper->mParentField = this;
|
||||
mHelper->setInspectorField(mField, mCaption, mFieldArrayIndex);
|
||||
mHelper->registerObject();
|
||||
mHelper->setExtent(pCheckBox->getExtent());
|
||||
mHelper->setPosition(0, 0);
|
||||
mRollout->addObject(mHelper);
|
||||
|
||||
mRollout->sizeToContents();
|
||||
mRollout->instantCollapse();
|
||||
|
||||
updateValue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiInspectorTypeGameModeList::consoleInit()
|
||||
{
|
||||
Parent::consoleInit();
|
||||
|
||||
ConsoleBaseType::getType(TypeGameModeList)->setInspectorFieldType("GuiInspectorTypeGameModeList");
|
||||
}
|
||||
|
||||
void GuiInspectorTypeGameModeList::childResized(GuiControl* child)
|
||||
{
|
||||
setExtent(mRollout->getExtent());
|
||||
}
|
||||
|
||||
bool GuiInspectorTypeGameModeList::resize(const Point2I& newPosition, const Point2I& newExtent)
|
||||
{
|
||||
if (!Parent::resize(newPosition, newExtent))
|
||||
return false;
|
||||
|
||||
// Hack... height of 18 is hardcoded
|
||||
return mHelper->resize(Point2I(0, 0), Point2I(newExtent.x, 18));
|
||||
}
|
||||
|
||||
bool GuiInspectorTypeGameModeList::updateRects()
|
||||
{
|
||||
if (!mRollout)
|
||||
return false;
|
||||
|
||||
bool result = mRollout->setExtent(getExtent());
|
||||
|
||||
for (U32 i = 0; i < mArrayCtrl->size(); i++)
|
||||
{
|
||||
GuiInspectorField* pField = dynamic_cast<GuiInspectorField*>(mArrayCtrl->at(i));
|
||||
if (pField)
|
||||
if (pField->updateRects())
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (mHelper && mHelper->updateRects())
|
||||
result = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringTableEntry GuiInspectorTypeGameModeList::getValue()
|
||||
{
|
||||
if (!mRollout)
|
||||
return StringTable->insert("");
|
||||
|
||||
String results = "";
|
||||
|
||||
for (U32 i = 0; i < mArrayCtrl->size(); i++)
|
||||
{
|
||||
GuiCheckBoxCtrl* pCheckBox = dynamic_cast<GuiCheckBoxCtrl*>(mArrayCtrl->at(i));
|
||||
|
||||
if (pCheckBox->getStateOn())
|
||||
results += pCheckBox->getText() + String(";");
|
||||
}
|
||||
|
||||
if (!results.isEmpty())
|
||||
return StringTable->insert(results.c_str());
|
||||
else
|
||||
return StringTable->EmptyString();
|
||||
}
|
||||
|
||||
void GuiInspectorTypeGameModeList::setValue(StringTableEntry value)
|
||||
{
|
||||
Vector<String> gameModeNames;
|
||||
U32 uCount = StringUnit::getUnitCount(value, ";");
|
||||
for (U32 i = 0; i < uCount; i++)
|
||||
{
|
||||
String name = StringUnit::getUnit(value, i, ";");
|
||||
if (!name.isEmpty())
|
||||
gameModeNames.push_back(name);
|
||||
}
|
||||
|
||||
for (U32 i = 0; i < mArrayCtrl->size(); i++)
|
||||
{
|
||||
GuiCheckBoxCtrl* pCheckBox = dynamic_cast<GuiCheckBoxCtrl*>(mArrayCtrl->at(i));
|
||||
|
||||
for (U32 m = 0; m < gameModeNames.size(); m++)
|
||||
{
|
||||
if (gameModeNames[m].equal(pCheckBox->getText()))
|
||||
{
|
||||
pCheckBox->setStateOn(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mHelper->setValue(value);
|
||||
}
|
||||
|
||||
void GuiInspectorTypeGameModeList::updateData()
|
||||
{
|
||||
StringTableEntry data = getValue();
|
||||
setData(data);
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiInspectorTypeGameModeList, applyValue, void, (), , "")
|
||||
{
|
||||
object->updateData();
|
||||
}
|
||||
|
||||
GuiInspectorTypeGameModeListHelper::GuiInspectorTypeGameModeListHelper()
|
||||
: mButton(NULL),
|
||||
mParentRollout(NULL),
|
||||
mParentField(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiInspectorTypeGameModeListHelper);
|
||||
|
||||
ConsoleDocClass(GuiInspectorTypeGameModeListHelper,
|
||||
"@brief Inspector field type support for GameModes lists.\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
GuiControl* GuiInspectorTypeGameModeListHelper::constructEditControl()
|
||||
{
|
||||
GuiControl* retCtrl = new GuiTextEditCtrl();
|
||||
retCtrl->setDataField(StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile");
|
||||
retCtrl->setField("hexDisplay", "true");
|
||||
|
||||
_registerEditControl(retCtrl);
|
||||
|
||||
char szBuffer[512];
|
||||
dSprintf(szBuffer, 512, "%d.apply(%d.getText());", mParentField->getId(), retCtrl->getId());
|
||||
retCtrl->setField("AltCommand", szBuffer);
|
||||
retCtrl->setField("Validate", szBuffer);
|
||||
|
||||
mButton = new GuiBitmapButtonCtrl();
|
||||
|
||||
RectI browseRect(Point2I((getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4));
|
||||
dSprintf(szBuffer, 512, "%d.toggleExpanded(false);", mParentRollout->getId());
|
||||
mButton->setField("Command", szBuffer);
|
||||
mButton->setField("buttonType", "ToggleButton");
|
||||
mButton->setDataField(StringTable->insert("Profile"), NULL, "GuiInspectorButtonProfile");
|
||||
mButton->setBitmap(StringTable->insert("ToolsModule:arrowBtn_N_image"));
|
||||
mButton->setStateOn(true);
|
||||
mButton->setExtent(16, 16);
|
||||
mButton->registerObject();
|
||||
addObject(mButton);
|
||||
|
||||
mButton->resize(browseRect.point, browseRect.extent);
|
||||
|
||||
return retCtrl;
|
||||
}
|
||||
|
||||
bool GuiInspectorTypeGameModeListHelper::resize(const Point2I& newPosition, const Point2I& newExtent)
|
||||
{
|
||||
if (!Parent::resize(newPosition, newExtent))
|
||||
return false;
|
||||
|
||||
if (mEdit != NULL)
|
||||
{
|
||||
return updateRects();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GuiInspectorTypeGameModeListHelper::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 - 32, fieldExtent.y);
|
||||
|
||||
bool editResize = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
|
||||
bool buttonResize = false;
|
||||
|
||||
if (mButton != NULL)
|
||||
{
|
||||
mButtonRect.set(fieldExtent.x - 26, 2, 16, 16);
|
||||
buttonResize = mButton->resize(mButtonRect.point, mButtonRect.extent);
|
||||
}
|
||||
|
||||
return (editResize || buttonResize);
|
||||
}
|
||||
|
||||
void GuiInspectorTypeGameModeListHelper::setValue(StringTableEntry newValue)
|
||||
{
|
||||
GuiTextEditCtrl* edit = dynamic_cast<GuiTextEditCtrl*>(mEdit);
|
||||
edit->setText(newValue);
|
||||
}
|
||||
#endif
|
||||
110
Engine/source/T3D/gameMode.h
Normal file
110
Engine/source/T3D/gameMode.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#pragma once
|
||||
#ifndef GAME_MODE_H
|
||||
#define GAME_MODE_H
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
#ifndef _GUI_INSPECTOR_TYPES_H_
|
||||
#include "gui/editor/guiInspectorTypes.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include "T3D/assets/ImageAsset.h"
|
||||
|
||||
class GameMode : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
private:
|
||||
StringTableEntry mGameModeName;
|
||||
StringTableEntry mGameModeDesc;
|
||||
|
||||
DECLARE_IMAGEASSET(GameMode, PreviewImage, previewChange, GFXStaticTextureSRGBProfile);
|
||||
DECLARE_ASSET_SETGET(GameMode, PreviewImage);
|
||||
|
||||
bool mIsActive;
|
||||
|
||||
public:
|
||||
|
||||
GameMode();
|
||||
~GameMode() = default;
|
||||
static void initPersistFields();
|
||||
bool onAdd() override;
|
||||
void onRemove() override;
|
||||
|
||||
bool isActive() { return mIsActive; }
|
||||
void setActive(const bool& active);
|
||||
|
||||
DECLARE_CONOBJECT(GameMode);
|
||||
|
||||
static void findGameModes(const char* gameModeList, Vector<GameMode*>* outGameModes);
|
||||
|
||||
void previewChange() {}
|
||||
|
||||
DECLARE_CALLBACK(void, onActivated, ());
|
||||
DECLARE_CALLBACK(void, onDeactivated, ());
|
||||
DECLARE_CALLBACK(void, onSceneLoaded, ());
|
||||
DECLARE_CALLBACK(void, onSceneUnloaded, ());
|
||||
DECLARE_CALLBACK(void, onSubsceneLoaded, ());
|
||||
DECLARE_CALLBACK(void, onSubsceneUnloaded, ());
|
||||
};
|
||||
|
||||
DefineConsoleType(TypeGameModeList, String)
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
class GuiInspectorTypeGameModeListHelper;
|
||||
|
||||
class GuiInspectorTypeGameModeList : public GuiInspectorField
|
||||
{
|
||||
typedef GuiInspectorField Parent;
|
||||
public:
|
||||
|
||||
GuiInspectorTypeGameModeListHelper* mHelper;
|
||||
GuiRolloutCtrl* mRollout;
|
||||
GuiDynamicCtrlArrayControl* mArrayCtrl;
|
||||
Vector<GuiInspectorField*> mChildren;
|
||||
|
||||
GuiBitmapButtonCtrl* mButton;
|
||||
RectI mButtonRect;
|
||||
|
||||
DECLARE_CONOBJECT(GuiInspectorTypeGameModeList);
|
||||
|
||||
GuiInspectorTypeGameModeList();
|
||||
|
||||
// ConsoleObject
|
||||
bool onAdd() override;
|
||||
static void consoleInit();
|
||||
|
||||
// GuiInspectorField
|
||||
bool resize(const Point2I& newPosition, const Point2I& newExtent) override;
|
||||
void childResized(GuiControl* child) override;
|
||||
bool updateRects() override;
|
||||
void updateData() override;
|
||||
StringTableEntry getValue() override;
|
||||
void setValue(StringTableEntry value) override;
|
||||
};
|
||||
|
||||
class GuiInspectorTypeGameModeListHelper : public GuiInspectorField
|
||||
{
|
||||
typedef GuiInspectorField Parent;
|
||||
|
||||
public:
|
||||
|
||||
GuiInspectorTypeGameModeListHelper();
|
||||
|
||||
DECLARE_CONOBJECT(GuiInspectorTypeGameModeListHelper);
|
||||
|
||||
GuiBitmapButtonCtrl* mButton;
|
||||
GuiRolloutCtrl* mParentRollout;
|
||||
GuiInspectorTypeGameModeList* mParentField;
|
||||
RectI mButtonRect;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Override able methods for custom edit fields
|
||||
//-----------------------------------------------------------------------------
|
||||
GuiControl* constructEditControl() override;
|
||||
bool resize(const Point2I& newPosition, const Point2I& newExtent) override;
|
||||
bool updateRects() override;
|
||||
void setValue(StringTableEntry value) override;
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -372,6 +372,13 @@ class GuiControl : public SimGroup
|
|||
|
||||
inline const S32 getHorizSizing() const { return mHorizSizing; }
|
||||
inline const S32 getVertSizing() const { return mVertSizing; }
|
||||
|
||||
void setHorizSizing(horizSizingOptions horizSizing) {
|
||||
mHorizSizing = horizSizing;
|
||||
}
|
||||
void setVertSizing(vertSizingOptions vertSizing) {
|
||||
mVertSizing = vertSizing;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
|
|
|||
|
|
@ -2013,3 +2013,8 @@ void SceneObject::onNewParent(SceneObject *newParent) { if (isServerObject()) on
|
|||
void SceneObject::onLostParent(SceneObject *oldParent) { if (isServerObject()) onLostParent_callback(oldParent); }
|
||||
void SceneObject::onNewChild(SceneObject *newKid) { if (isServerObject()) onNewChild_callback(newKid); }
|
||||
void SceneObject::onLostChild(SceneObject *lostKid) { if (isServerObject()) onLostChild_callback(lostKid); }
|
||||
|
||||
IMPLEMENT_CALLBACK(SceneObject, onSaving, void, (const char* fileName), (fileName),
|
||||
"@brief Called when a saving is occuring to allow objects to special-handle prepwork for saving if required.\n\n"
|
||||
|
||||
"@param fileName The level file being saved\n");
|
||||
|
|
|
|||
|
|
@ -913,6 +913,8 @@ class SceneObject : public NetObject, public ProcessObject
|
|||
DECLARE_CALLBACK(void, onLostChild, (SceneObject *subObject));
|
||||
// PATHSHAPE END
|
||||
|
||||
DECLARE_CALLBACK(void, onSaving, (const char* fileName));
|
||||
|
||||
virtual void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList) {}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue