2024-09-01 21:39:00 +00:00
# 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. " ) ;
2024-10-04 05:10:26 +00:00
addField ( " loadIf " , TypeCommand , Offset ( mLoadIf , SubScene ) , " evaluation condition (true/false) " ) ;
2024-09-01 21:39:00 +00:00
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 ;
2024-10-04 05:10:26 +00:00
bool passes = getWorldBox ( ) . isOverlapped ( testBox ) ;
if ( passes & & ! mLoadIf . isEmpty ( ) )
{
//test the mapper plugged in condition line
String resVar = getIdString ( ) + String ( " .result " ) ;
Con : : setBoolVariable ( resVar . c_str ( ) , false ) ;
String command = resVar + " = " + mLoadIf + " ; " ;
Con : : evaluatef ( command . c_str ( ) ) ;
passes = Con : : getBoolVariable ( resVar . c_str ( ) ) ;
}
return passes ;
2024-09-01 21:39:00 +00:00
}
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 + + )
{
2024-09-05 18:49:13 +00:00
//Inform our objects we're saving, so if they do any special stuff
//they can do it before the actual write-out
SimGroup * sg = dynamic_cast < SimGroup * > ( * itr ) ;
if ( sg )
2024-09-01 21:39:00 +00:00
{
2024-09-05 18:49:13 +00:00
ConsoleValue vars [ 3 ] ;
vars [ 2 ] . setString ( mLevelAssetId ) ;
sg - > callOnChildren ( " onSaving " , 3 , vars ) ;
2024-09-01 21:39:00 +00:00
}
2024-09-05 18:49:13 +00:00
SceneObject * scO = dynamic_cast < SceneObject * > ( * itr ) ;
if ( scO )
{
scO - > onSaving_callback ( mLevelAssetId ) ;
}
2024-09-01 21:39:00 +00:00
2024-09-05 18:49:13 +00:00
SimObject * sO = static_cast < SimObject * > ( * itr ) ;
2024-09-01 21:39:00 +00:00
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 ( ) ;
2024-09-01 22:14:08 +00:00
return saveSuccess ;
2024-09-01 21:39:00 +00:00
}
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 ( ) ;
}