SubScene.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. #include "SubScene.h"
  2. #include "gameMode.h"
  3. #include "console/persistenceManager.h"
  4. #include "console/script.h"
  5. #include "scene/sceneRenderState.h"
  6. #include "renderInstance/renderPassManager.h"
  7. #include "gfx/gfxDrawUtil.h"
  8. #include "gfx/gfxTransformSaver.h"
  9. #include "gui/editor/inspector/group.h"
  10. #include "T3D/gameBase/gameBase.h"
  11. bool SubScene::smTransformChildren = false;
  12. IMPLEMENT_CO_NETOBJECT_V1(SubScene);
  13. S32 SubScene::mUnloadTimeoutMs = 5000;
  14. IMPLEMENT_CALLBACK(SubScene, onLoaded, void, (), (),
  15. "@brief Called when a subScene has been loaded and has game mode implications.\n\n");
  16. IMPLEMENT_CALLBACK(SubScene, onUnloaded, void, (), (),
  17. "@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
  18. SubScene::SubScene() :
  19. mLevelAssetId(StringTable->EmptyString()),
  20. mGameModesNames(StringTable->EmptyString()),
  21. mScopeDistance(-1),
  22. mLoaded(false),
  23. mFreezeLoading(false),
  24. mTickPeriodMS(1000),
  25. mCurrTick(0),
  26. mGlobalLayer(false)
  27. {
  28. mNetFlags.set(Ghostable | ScopeAlways);
  29. mTypeMask |= StaticObjectType;
  30. }
  31. SubScene::~SubScene()
  32. {
  33. }
  34. bool SubScene::onAdd()
  35. {
  36. if (!Parent::onAdd())
  37. return false;
  38. setProcessTick(true);
  39. return true;
  40. }
  41. void SubScene::onRemove()
  42. {
  43. if (isClientObject())
  44. removeFromScene();
  45. unload();
  46. Parent::onRemove();
  47. }
  48. void SubScene::initPersistFields()
  49. {
  50. addGroup("SubScene");
  51. addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), "");
  52. INITPERSISTFIELD_LEVELASSET(Level, SubScene, "The level asset to load.");
  53. addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with.");
  54. endGroup("SubScene");
  55. addGroup("LoadingManagement");
  56. addField("freezeLoading", TypeBool, Offset(mFreezeLoading, SubScene), "If true, will prevent the zone from being changed from it's current loading state.");
  57. addField("loadIf", TypeCommand, Offset(mLoadIf, SubScene), "evaluation condition (true/false)");
  58. addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)");
  59. addField("onLoadCommand", TypeCommand, Offset(mOnLoadCommand, SubScene), "The command to execute when the subscene is loaded. Maximum 1023 characters.");
  60. addField("onUnloadCommand", TypeCommand, Offset(mOnUnloadCommand, SubScene), "The command to execute when subscene is unloaded. Maximum 1023 characters.");
  61. endGroup("LoadingManagement");
  62. Parent::initPersistFields();
  63. }
  64. void SubScene::consoleInit()
  65. {
  66. Parent::consoleInit();
  67. 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"
  68. "@ingroup Editors\n");
  69. Con::addVariable("$SubScene::transformChildren", TypeBool, &SubScene::smTransformChildren,
  70. "@brief If true, then transform manipulations modify child objects. If false, only triggering bounds is manipulated\n\n"
  71. "@ingroup Editors");
  72. }
  73. void SubScene::addObject(SimObject* object)
  74. {
  75. SceneObject::addObject(object);
  76. }
  77. void SubScene::removeObject(SimObject* object)
  78. {
  79. SceneObject::removeObject(object);
  80. }
  81. U32 SubScene::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
  82. {
  83. U32 retMask = Parent::packUpdate(conn, mask, stream);
  84. stream->writeFlag(mGlobalLayer);
  85. return retMask;
  86. }
  87. void SubScene::unpackUpdate(NetConnection* conn, BitStream* stream)
  88. {
  89. Parent::unpackUpdate(conn, stream);
  90. mGlobalLayer = stream->readFlag();
  91. }
  92. void SubScene::onInspect(GuiInspector* inspector)
  93. {
  94. Parent::onInspect(inspector);
  95. //Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
  96. GuiInspectorGroup* subsceneGrp = inspector->findExistentGroup(StringTable->insert("SubScene"));
  97. if (!subsceneGrp)
  98. return;
  99. GuiControl* stack = dynamic_cast<GuiControl*>(subsceneGrp->findObjectByInternalName(StringTable->insert("Stack")));
  100. //Save button
  101. GuiInspectorField* saveFieldGui = subsceneGrp->createInspectorField();
  102. saveFieldGui->init(inspector, subsceneGrp);
  103. saveFieldGui->setSpecialEditField(true);
  104. saveFieldGui->setTargetObject(this);
  105. StringTableEntry fldnm = StringTable->insert("SaveSubScene");
  106. saveFieldGui->setSpecialEditVariableName(fldnm);
  107. saveFieldGui->setInspectorField(NULL, fldnm);
  108. saveFieldGui->setDocs("");
  109. stack->addObject(saveFieldGui);
  110. GuiButtonCtrl* saveButton = new GuiButtonCtrl();
  111. saveButton->registerObject();
  112. saveButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
  113. saveButton->setText("Save SubScene");
  114. saveButton->resize(Point2I::Zero, saveFieldGui->getExtent());
  115. saveButton->setHorizSizing(GuiControl::horizResizeWidth);
  116. saveButton->setVertSizing(GuiControl::vertResizeHeight);
  117. char szBuffer[512];
  118. dSprintf(szBuffer, 512, "%d.save();", this->getId());
  119. saveButton->setConsoleCommand(szBuffer);
  120. saveFieldGui->addObject(saveButton);
  121. }
  122. void SubScene::inspectPostApply()
  123. {
  124. Parent::inspectPostApply();
  125. setMaskBits(-1);
  126. }
  127. void SubScene::setTransform(const MatrixF& mat)
  128. {
  129. if(SubScene::smTransformChildren)
  130. {
  131. Parent::setTransform(mat);
  132. }
  133. else
  134. {
  135. SceneObject::setTransform(mat);
  136. }
  137. }
  138. void SubScene::setRenderTransform(const MatrixF& mat)
  139. {
  140. if (SubScene::smTransformChildren)
  141. {
  142. Parent::setRenderTransform(mat);
  143. }
  144. else
  145. {
  146. SceneObject::setRenderTransform(mat);
  147. }
  148. }
  149. bool SubScene::evaluateCondition()
  150. {
  151. if (!mLoadIf.isEmpty())
  152. {
  153. //test the mapper plugged in condition line
  154. String resVar = getIdString() + String(".result");
  155. Con::setBoolVariable(resVar.c_str(), false);
  156. String command = resVar + "=" + mLoadIf + ";";
  157. Con::evaluatef(command.c_str());
  158. return Con::getBoolVariable(resVar.c_str());
  159. }
  160. return true;
  161. }
  162. bool SubScene::testBox(const Box3F& testBox)
  163. {
  164. if (mGlobalLayer)
  165. return true;
  166. bool passes = getWorldBox().isOverlapped(testBox);
  167. if (passes)
  168. passes = evaluateCondition();
  169. return passes;
  170. }
  171. void SubScene::write(Stream& stream, U32 tabStop, U32 flags)
  172. {
  173. MutexHandle handle;
  174. handle.lock(mMutex);
  175. // export selected only?
  176. if ((flags & SelectedOnly) && !isSelected())
  177. {
  178. for (U32 i = 0; i < size(); i++)
  179. (*this)[i]->write(stream, tabStop, flags);
  180. return;
  181. }
  182. stream.writeTabs(tabStop);
  183. char buffer[2048];
  184. const U32 bufferWriteLen = dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() && !(flags & NoName) ? getName() : "");
  185. stream.write(bufferWriteLen, buffer);
  186. writeFields(stream, tabStop + 1);
  187. //The only meaningful difference between this and simSet for writing is we skip the children, since they're just the levelAsset contents
  188. stream.writeTabs(tabStop);
  189. stream.write(4, "};\r\n");
  190. }
  191. void SubScene::processTick(const Move* move)
  192. {
  193. mCurrTick += TickMs;
  194. if (mCurrTick > mTickPeriodMS)
  195. {
  196. mCurrTick = 0;
  197. //re-evaluate
  198. if (!evaluateCondition())
  199. unload();
  200. }
  201. }
  202. void SubScene::_onFileChanged(const Torque::Path& path)
  203. {
  204. if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path)
  205. return;
  206. AssertFatal(path == mLevelAsset->getLevelPath(), "Prefab::_onFileChanged - path does not match filename.");
  207. _closeFile(false);
  208. _loadFile(false);
  209. setMaskBits(U32_MAX);
  210. }
  211. void SubScene::_removeContents(SimGroupIterator set)
  212. {
  213. for (SimGroupIterator itr(set); *itr; ++itr)
  214. {
  215. SimGroup* child = dynamic_cast<SimGroup*>(*itr);
  216. if (child)
  217. {
  218. _removeContents(SimGroupIterator(child));
  219. GameBase* asGameBase = dynamic_cast<GameBase*>(child);
  220. if (asGameBase)
  221. {
  222. asGameBase->scriptOnRemove();
  223. }
  224. Sim::cancelPendingEvents(child);
  225. child->safeDeleteObject();
  226. }
  227. }
  228. }
  229. void SubScene::_closeFile(bool removeFileNotify)
  230. {
  231. AssertFatal(isServerObject(), "Trying to close out a subscene file on the client is bad!");
  232. _removeContents(SimGroupIterator(this));
  233. if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString())
  234. {
  235. Torque::FS::RemoveChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
  236. }
  237. mGameModesList.clear();
  238. }
  239. void SubScene::_loadFile(bool addFileNotify)
  240. {
  241. AssertFatal(isServerObject(), "Trying to load a SubScene file on the client is bad!");
  242. if(mLevelAsset.isNull() || mLevelAsset->getLevelPath() == StringTable->EmptyString())
  243. return;
  244. String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath());
  245. String instantGroup = Con::getVariable("InstantGroup");
  246. Con::setIntVariable("InstantGroup", this->getId());
  247. Con::evaluate((const char*)evalCmd.c_str(), false, mLevelAsset->getLevelPath());
  248. Con::setVariable("InstantGroup", instantGroup.c_str());
  249. if (addFileNotify)
  250. Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
  251. }
  252. void SubScene::load()
  253. {
  254. mStartUnloadTimerMS = -1; //reset unload timers
  255. //no need to load multiple times
  256. if (mLoaded)
  257. return;
  258. if (mFreezeLoading)
  259. return;
  260. _loadFile(true);
  261. mLoaded = true;
  262. GameMode::findGameModes(mGameModesNames, &mGameModesList);
  263. onLoaded_callback();
  264. for (U32 i = 0; i < mGameModesList.size(); i++)
  265. {
  266. mGameModesList[i]->onSubsceneLoaded_callback(this);
  267. }
  268. if (!mOnLoadCommand.isEmpty())
  269. {
  270. String command = "%this = " + String(getIdString()) + "; " + mLoadIf + ";";
  271. Con::evaluatef(command.c_str());
  272. }
  273. }
  274. void SubScene::unload()
  275. {
  276. if (!mLoaded)
  277. return;
  278. if (mFreezeLoading)
  279. return;
  280. if (isSelected())
  281. {
  282. mStartUnloadTimerMS = Sim::getCurrentTime();
  283. return; //if a child is selected, then we don't want to unload
  284. }
  285. //scan down through our child objects, see if any are marked as selected,
  286. //if so, skip unloading and reset the timer
  287. for (SimGroupIterator itr(this); *itr; ++itr)
  288. {
  289. SimGroup* childGrp = dynamic_cast<SimGroup*>(*itr);
  290. if (childGrp)
  291. {
  292. if (childGrp->isSelected())
  293. {
  294. mStartUnloadTimerMS = Sim::getCurrentTime();
  295. return; //if a child is selected, then we don't want to unload
  296. }
  297. for (SimGroupIterator cldItr(childGrp); *cldItr; ++cldItr)
  298. {
  299. SimObject* chldChld = dynamic_cast<SimObject*>(*cldItr);
  300. if (chldChld && chldChld->isSelected())
  301. {
  302. mStartUnloadTimerMS = Sim::getCurrentTime();
  303. return; //if a child is selected, then we don't want to unload
  304. }
  305. }
  306. }
  307. }
  308. onUnloaded_callback();
  309. for (U32 i = 0; i < mGameModesList.size(); i++)
  310. {
  311. mGameModesList[i]->onSubsceneUnloaded_callback(this);
  312. }
  313. if (!mOnUnloadCommand.isEmpty())
  314. {
  315. String command = "%this = " + String(getIdString()) + "; " + mOnUnloadCommand + ";";
  316. Con::evaluatef(command.c_str());
  317. }
  318. _closeFile(true);
  319. mLoaded = false;
  320. }
  321. bool SubScene::save()
  322. {
  323. if (!isServerObject())
  324. return false;
  325. //if there's nothing TO save, don't bother
  326. if (size() == 0 || !isLoaded())
  327. return false;
  328. if (mLevelAsset.isNull())
  329. return false;
  330. //If we're flagged for unload, push back the unload timer so we can't accidentally trip be saving partway through an unload
  331. if (mStartUnloadTimerMS != -1)
  332. mStartUnloadTimerMS = Sim::getCurrentTime();
  333. PersistenceManager prMger;
  334. StringTableEntry levelPath = mLevelAsset->getLevelPath();
  335. FileStream fs;
  336. fs.open(levelPath, Torque::FS::File::Write);
  337. fs.close();
  338. for (SimGroupIterator itr(this); *itr; ++itr)
  339. {
  340. SimObject* childObj = (*itr);
  341. if (!prMger.isDirty(childObj))
  342. {
  343. if ((*itr)->isMethod("onSaving"))
  344. {
  345. Con::executef((*itr), "onSaving", mLevelAssetId);
  346. }
  347. if (childObj->getGroup() == this)
  348. {
  349. prMger.setDirty((*itr), levelPath);
  350. }
  351. }
  352. }
  353. prMger.saveDirty();
  354. //process our gameModeList and write it out to the levelAsset for metadata stashing
  355. bool saveSuccess = false;
  356. //Get the level asset
  357. if (mLevelAsset.isNull())
  358. return saveSuccess;
  359. //update the gamemode list as well
  360. mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
  361. //Finally, save
  362. saveSuccess = mLevelAsset->saveAsset();
  363. return saveSuccess;
  364. }
  365. void SubScene::_onSelected()
  366. {
  367. if (!isLoaded() && isServerObject())
  368. load();
  369. }
  370. void SubScene::_onUnselected()
  371. {
  372. }
  373. void SubScene::prepRenderImage(SceneRenderState* state)
  374. {
  375. // only render if selected or render flag is set
  376. if (/*!smRenderTriggers && */!isSelected())
  377. return;
  378. ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  379. ri->renderDelegate.bind(this, &SubScene::renderObject);
  380. ri->type = RenderPassManager::RIT_Editor;
  381. ri->translucentSort = true;
  382. ri->defaultKey = 1;
  383. state->getRenderPass()->addInst(ri);
  384. }
  385. void SubScene::renderObject(ObjectRenderInst* ri,
  386. SceneRenderState* state,
  387. BaseMatInstance* overrideMat)
  388. {
  389. if (overrideMat)
  390. return;
  391. GFXStateBlockDesc desc;
  392. desc.setZReadWrite(true, false);
  393. desc.setBlend(true);
  394. // Trigger polyhedrons are set up with outward facing normals and CCW ordering
  395. // so can't enable backface culling.
  396. desc.setCullMode(GFXCullNone);
  397. GFXTransformSaver saver;
  398. MatrixF mat = getRenderTransform();
  399. GFX->multWorld(mat);
  400. GFXDrawUtil* drawer = GFX->getDrawUtil();
  401. //Box3F scale = getScale()
  402. //Box3F bounds = Box3F(-m)
  403. Point3F scale = getScale();
  404. Box3F bounds = Box3F(-scale/2, scale/2);
  405. ColorI boundsColor = ColorI(135, 206, 235, 50);
  406. if (mGlobalLayer)
  407. boundsColor = ColorI(200, 100, 100, 25);
  408. else if (mLoaded)
  409. boundsColor = ColorI(50, 200, 50, 50);
  410. drawer->drawCube(desc, bounds, boundsColor);
  411. // Render wireframe.
  412. desc.setFillModeWireframe();
  413. drawer->drawCube(desc, bounds, ColorI::BLACK);
  414. }
  415. DefineEngineMethod(SubScene, save, bool, (),,
  416. "Save out the subScene.\n")
  417. {
  418. return object->save();
  419. }
  420. DefineEngineMethod(SubScene, load, void, (), ,
  421. "Loads the SubScene's level file.\n")
  422. {
  423. object->load();
  424. }
  425. DefineEngineMethod(SubScene, unload, void, (), ,
  426. "Unloads the SubScene's level file.\n")
  427. {
  428. object->unload();
  429. }