SubScene.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  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 there's nothing TO save, don't bother
  324. if (size() == 0 || !isLoaded())
  325. return false;
  326. if (mLevelAsset.isNull())
  327. return false;
  328. //If we're flagged for unload, push back the unload timer so we can't accidentally trip be saving partway through an unload
  329. if (mStartUnloadTimerMS != -1)
  330. mStartUnloadTimerMS = Sim::getCurrentTime();
  331. PersistenceManager prMger;
  332. StringTableEntry levelPath = mLevelAsset->getLevelPath();
  333. FileStream fs;
  334. fs.open(levelPath, Torque::FS::File::Write);
  335. fs.close();
  336. for (SimGroupIterator itr(this); *itr; ++itr)
  337. {
  338. if ((*itr)->isMethod("onSaving"))
  339. {
  340. ConsoleValue vars[3];
  341. vars[2].setString(mLevelAssetId);
  342. Con::execute((*itr), 3, vars);
  343. }
  344. prMger.setDirty((*itr), levelPath);
  345. }
  346. prMger.saveDirty();
  347. //process our gameModeList and write it out to the levelAsset for metadata stashing
  348. bool saveSuccess = false;
  349. //Get the level asset
  350. if (mLevelAsset.isNull())
  351. return saveSuccess;
  352. //update the gamemode list as well
  353. mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
  354. //Finally, save
  355. saveSuccess = mLevelAsset->saveAsset();
  356. return saveSuccess;
  357. }
  358. void SubScene::_onSelected()
  359. {
  360. if (!isLoaded() && isServerObject())
  361. load();
  362. }
  363. void SubScene::_onUnselected()
  364. {
  365. }
  366. void SubScene::prepRenderImage(SceneRenderState* state)
  367. {
  368. // only render if selected or render flag is set
  369. if (/*!smRenderTriggers && */!isSelected())
  370. return;
  371. ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  372. ri->renderDelegate.bind(this, &SubScene::renderObject);
  373. ri->type = RenderPassManager::RIT_Editor;
  374. ri->translucentSort = true;
  375. ri->defaultKey = 1;
  376. state->getRenderPass()->addInst(ri);
  377. }
  378. void SubScene::renderObject(ObjectRenderInst* ri,
  379. SceneRenderState* state,
  380. BaseMatInstance* overrideMat)
  381. {
  382. if (overrideMat)
  383. return;
  384. GFXStateBlockDesc desc;
  385. desc.setZReadWrite(true, false);
  386. desc.setBlend(true);
  387. // Trigger polyhedrons are set up with outward facing normals and CCW ordering
  388. // so can't enable backface culling.
  389. desc.setCullMode(GFXCullNone);
  390. GFXTransformSaver saver;
  391. MatrixF mat = getRenderTransform();
  392. GFX->multWorld(mat);
  393. GFXDrawUtil* drawer = GFX->getDrawUtil();
  394. //Box3F scale = getScale()
  395. //Box3F bounds = Box3F(-m)
  396. Point3F scale = getScale();
  397. Box3F bounds = Box3F(-scale/2, scale/2);
  398. ColorI boundsColor = ColorI(135, 206, 235, 50);
  399. if (mGlobalLayer)
  400. boundsColor = ColorI(200, 100, 100, 25);
  401. else if (mLoaded)
  402. boundsColor = ColorI(50, 200, 50, 50);
  403. drawer->drawCube(desc, bounds, boundsColor);
  404. // Render wireframe.
  405. desc.setFillModeWireframe();
  406. drawer->drawCube(desc, bounds, ColorI::BLACK);
  407. }
  408. DefineEngineMethod(SubScene, save, bool, (),,
  409. "Save out the subScene.\n")
  410. {
  411. return object->save();
  412. }
  413. DefineEngineMethod(SubScene, load, void, (), ,
  414. "Loads the SubScene's level file.\n")
  415. {
  416. object->load();
  417. }
  418. DefineEngineMethod(SubScene, unload, void, (), ,
  419. "Unloads the SubScene's level file.\n")
  420. {
  421. object->unload();
  422. }