reflectionProbe.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "T3D/lighting/reflectionProbe.h"
  23. #include "math/mathIO.h"
  24. #include "scene/sceneRenderState.h"
  25. #include "console/consoleTypes.h"
  26. #include "core/stream/bitStream.h"
  27. #include "materials/baseMatInstance.h"
  28. #include "console/engineAPI.h"
  29. #include "gfx/gfxDrawUtil.h"
  30. #include "gfx/gfxDebugEvent.h"
  31. #include "gfx/gfxTransformSaver.h"
  32. #include "math/mathUtils.h"
  33. #include "gfx/bitmap/gBitmap.h"
  34. #include "core/stream/fileStream.h"
  35. #include "core/fileObject.h"
  36. #include "core/resourceManager.h"
  37. #include "console/simPersistID.h"
  38. #include "T3D/gameFunctions.h"
  39. #include "postFx/postEffect.h"
  40. #include "renderInstance/renderProbeMgr.h"
  41. #include "renderInstance/renderProbeMgr.h"
  42. #include "math/util/sphereMesh.h"
  43. #include "materials/materialManager.h"
  44. #include "math/util/matrixSet.h"
  45. #include "gfx/bitmap/cubemapSaver.h"
  46. #include "materials/materialFeatureTypes.h"
  47. #include "gfx/gfxTextureManager.h"
  48. #include "T3D/lighting/IBLUtilities.h"
  49. #include "scene/reflector.h"
  50. extern bool gEditingMission;
  51. extern ColorI gCanvasClearColor;
  52. bool ReflectionProbe::smRenderPreviewProbes = true;
  53. static MatrixF sEditingTransformMat;
  54. IMPLEMENT_CO_NETOBJECT_V1(ReflectionProbe);
  55. ConsoleDocClass(ReflectionProbe,
  56. "@brief An example scene object which renders a mesh.\n\n"
  57. "This class implements a basic SceneObject that can exist in the world at a "
  58. "3D position and render itself. There are several valid ways to render an "
  59. "object in Torque. This class implements the preferred rendering method which "
  60. "is to submit a MeshRenderInst along with a Material, vertex buffer, "
  61. "primitive buffer, and transform and allow the RenderMeshMgr handle the "
  62. "actual setup and rendering for you.\n\n"
  63. "See the C++ code for implementation details.\n\n"
  64. "@ingroup Examples\n");
  65. ImplementEnumType(ReflectProbeType,
  66. "Type of mesh data available in a shape.\n"
  67. "@ingroup gameObjects")
  68. { ReflectionProbe::ProbeInfo::Sphere, "Sphere", "Sphere shaped" },
  69. { ReflectionProbe::ProbeInfo::Box, "Box", "Box shape" }
  70. EndImplementEnumType;
  71. ImplementEnumType(ReflectionModeEnum,
  72. "Type of mesh data available in a shape.\n"
  73. "@ingroup gameObjects")
  74. { ReflectionProbe::NoReflection, "No Reflections", "This probe does not provide any local reflection data"},
  75. { ReflectionProbe::StaticCubemap, "Static Cubemap", "Uses a static CubemapData" },
  76. { ReflectionProbe::BakedCubemap, "Baked Cubemap", "Uses a cubemap baked from the probe's current position" },
  77. //{ ReflectionProbe::DynamicCubemap, "Dynamic Cubemap", "Uses a cubemap baked from the probe's current position, updated at a set rate" },
  78. EndImplementEnumType;
  79. void ReflectionProbe::ProbeInfo::clear()
  80. {
  81. mPrefilterCubemap.free();
  82. mIrradianceCubemap.free();
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Object setup and teardown
  86. //-----------------------------------------------------------------------------
  87. ReflectionProbe::ReflectionProbe()
  88. {
  89. // Flag this object so that it will always
  90. // be sent across the network to clients
  91. mNetFlags.set(Ghostable | ScopeAlways);
  92. mTypeMask = LightObjectType | MarkerObjectType;
  93. mProbeShapeType = ProbeInfo::Box;
  94. mReflectionModeType = BakedCubemap;
  95. mEnabled = true;
  96. mBakeReflections = false;
  97. mCubemapDirty = false;
  98. mRadius = 10;
  99. mObjScale = Point3F::One * 10;
  100. mProbeRefScale = Point3F::One*10;
  101. mUseHDRCaptures = true;
  102. mCubemapName = StringTable->EmptyString();
  103. mStaticCubemap = NULL;
  104. mProbeUniqueID = "";
  105. #ifdef TORQUE_TOOLS
  106. mEditorShapeInst = NULL;
  107. mEditorShape = NULL;
  108. #endif
  109. mRefreshRateMS = 200;
  110. mDynamicLastBakeMS = 0;
  111. mResourcesCreated = false;
  112. mPrefilterSize = 64;
  113. mPrefilterMipLevels = mLog2(F32(mPrefilterSize));
  114. mPrefilterMap = nullptr;
  115. mIrridianceMap = nullptr;
  116. mProbeRefOffset = Point3F::Zero;
  117. mEditPosOffset = false;
  118. mCaptureMask = REFLECTION_PROBE_CAPTURE_TYPEMASK;
  119. mCanDamp = false;
  120. mAtten = 0.0f;
  121. }
  122. ReflectionProbe::~ReflectionProbe()
  123. {
  124. #ifdef TORQUE_TOOLS
  125. if (mEditorShapeInst)
  126. SAFE_DELETE(mEditorShapeInst);
  127. #endif
  128. if (mReflectionModeType == StaticCubemap && mStaticCubemap)
  129. mStaticCubemap->deleteObject();
  130. mProbeInfo.clear();
  131. if (mIrridianceMap) {
  132. if (mIrridianceMap->isProperlyAdded() && !mIrridianceMap->isRemoved())
  133. mIrridianceMap->deleteObject();
  134. }
  135. if (mPrefilterMap)
  136. {
  137. if (mPrefilterMap->isProperlyAdded() && !mPrefilterMap->isRemoved())
  138. mPrefilterMap->deleteObject();
  139. }
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Object Editing
  143. //-----------------------------------------------------------------------------
  144. void ReflectionProbe::initPersistFields()
  145. {
  146. docsURL;
  147. addGroup("Rendering");
  148. addProtectedField("enabled", TypeBool, Offset(mEnabled, ReflectionProbe),
  149. &_setEnabled, &defaultProtectedGetFn, "Is the probe enabled or not");
  150. addField("canDamp", TypeBool, Offset(mCanDamp, ReflectionProbe),"wetness allowed");
  151. addFieldV("attenuation", TypeRangedF32, Offset(mAtten, ReflectionProbe), &CommonValidators::NormalizedFloat, "falloff percent");
  152. endGroup("Rendering");
  153. addGroup("Reflection");
  154. addProtectedFieldV("radius", TypeRangedF32, Offset(mRadius, ReflectionProbe), &_setRadius, &defaultProtectedGetFn, &CommonValidators::PositiveFloat,
  155. "The name of the material used to render the mesh.");
  156. addProtectedField("EditPosOffset", TypeBool, Offset(mEditPosOffset, ReflectionProbe),
  157. &_toggleEditPosOffset, &defaultProtectedGetFn, "Toggle Edit Pos Offset Mode", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
  158. addField("refOffset", TypePoint3F, Offset(mProbeRefOffset, ReflectionProbe), "The reference positional offset for the probe. This is used for adjusting the perceived center and area of influence.\nHelpful in adjusting parallax issues");
  159. addField("refScale", TypePoint3F, Offset(mProbeRefScale, ReflectionProbe), "The reference scale for the probe. This is used for adjusting the perceived center and area of influence.\nHelpful in adjusting parallax issues");
  160. addProtectedField("ReflectionMode", TypeReflectionModeEnum, Offset(mReflectionModeType, ReflectionProbe), &_setReflectionMode, &defaultProtectedGetFn,
  161. "Used to dictate what sort of cubemap the probes use when using IBL.");
  162. addField("StaticCubemap", TypeCubemapName, Offset(mCubemapName, ReflectionProbe), "This is used when a static cubemap is used. The name of the cubemap is looked up and loaded for the IBL calculations.");
  163. //addField("DynamicReflectionRefreshMS", TypeS32, Offset(mRefreshRateMS, ReflectionProbe), "How often the dynamic cubemap is refreshed in milliseconds. Only works when the ReflectionMode is set to DynamicCubemap.");
  164. addProtectedField("Bake", TypeBool, Offset(mBakeReflections, ReflectionProbe),
  165. &_doBake, &defaultProtectedGetFn, "Bake Probe Reflections", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
  166. endGroup("Reflection");
  167. Con::addVariable("$Light::renderReflectionProbes", TypeBool, &RenderProbeMgr::smRenderReflectionProbes,
  168. "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
  169. "@note Only works for shadow mapped lights.\n\n"
  170. "@ingroup Lighting");
  171. Con::addVariable("$Probes::Capturing", TypeBool, &RenderProbeMgr::smBakeReflectionProbes,
  172. "Toggles probe rendering capture state.\n\n"
  173. "@ingroup Lighting");
  174. Con::addVariable("$Light::renderPreviewProbes", TypeBool, &ReflectionProbe::smRenderPreviewProbes,
  175. "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
  176. "@note Only works for shadow mapped lights.\n\n"
  177. "@ingroup Lighting");
  178. // SceneObject already handles exposing the transform
  179. Parent::initPersistFields();
  180. }
  181. void ReflectionProbe::inspectPostApply()
  182. {
  183. Parent::inspectPostApply();
  184. mDirty = true;
  185. bool liveUpdates = Con::getBoolVariable("$Probes::liveUpdates", false);
  186. if (liveUpdates)
  187. {
  188. bake();
  189. }
  190. // Flag the network mask to send the updates
  191. // to the client object
  192. setMaskBits(-1);
  193. }
  194. bool ReflectionProbe::_setEnabled(void *object, const char *index, const char *data)
  195. {
  196. ReflectionProbe* probe = reinterpret_cast< ReflectionProbe* >(object);
  197. probe->mEnabled = dAtob(data);
  198. probe->setMaskBits(EnabledMask);
  199. return true;
  200. }
  201. bool ReflectionProbe::_doBake(void *object, const char *index, const char *data)
  202. {
  203. ReflectionProbe* probe = reinterpret_cast< ReflectionProbe* >(object);
  204. probe->bake();
  205. probe->setMaskBits(StaticDataMask);
  206. return false;
  207. }
  208. bool ReflectionProbe::_toggleEditPosOffset(void *object, const char *index, const char *data)
  209. {
  210. ReflectionProbe* probe = reinterpret_cast< ReflectionProbe* >(object);
  211. probe->mEditPosOffset = !probe->mEditPosOffset;
  212. return false;
  213. }
  214. bool ReflectionProbe::_setRadius(void *object, const char *index, const char *data)
  215. {
  216. ReflectionProbe* probe = reinterpret_cast<ReflectionProbe*>(object);
  217. if (probe->mProbeShapeType != ProbeInfo::Sphere)
  218. return false;
  219. probe->mObjScale = Point3F(probe->mRadius, probe->mRadius, probe->mRadius);
  220. probe->setMaskBits(StaticDataMask);
  221. return true;
  222. }
  223. bool ReflectionProbe::_setReflectionMode(void *object, const char *index, const char *data)
  224. {
  225. ReflectionProbe* probe = reinterpret_cast<ReflectionProbe*>(object);
  226. if (!String::compare(data,"Static Cubemap"))
  227. {
  228. probe->mReflectionModeType = StaticCubemap;
  229. }
  230. else if (!String::compare(data, "Baked Cubemap"))
  231. {
  232. //Clear our cubemap if we changed it to be baked, just for cleanliness
  233. probe->mReflectionModeType = BakedCubemap;
  234. probe->mCubemapName = StringTable->EmptyString();
  235. }
  236. probe->setMaskBits(StaticDataMask);
  237. return true;
  238. }
  239. bool ReflectionProbe::onAdd()
  240. {
  241. if (!Parent::onAdd())
  242. return false;
  243. mEditPosOffset = false;
  244. mObjBox.minExtents.set(-0.5, -0.5, -0.5);
  245. mObjBox.maxExtents.set(0.5, 0.5, 0.5);
  246. // Skip our transform... it just dirties mask bits.
  247. Parent::setTransform(mObjToWorld);
  248. resetWorldBox();
  249. // Add this object to the scene
  250. addToScene();
  251. if (isServerObject())
  252. {
  253. if (!mPersistentId)
  254. mPersistentId = getOrCreatePersistentId();
  255. mProbeUniqueID = mPersistentId->getUUID().toString();
  256. }
  257. // Refresh this object's material (if any)
  258. if (isClientObject())
  259. {
  260. if (!mResourcesCreated && !createClientResources())
  261. return false;
  262. updateProbeParams();
  263. }
  264. setMaskBits(-1);
  265. return true;
  266. }
  267. void ReflectionProbe::onRemove()
  268. {
  269. if (isClientObject())
  270. {
  271. PROBEMGR->unregisterProbe(&mProbeInfo);
  272. }
  273. // Remove this object from the scene
  274. removeFromScene();
  275. Parent::onRemove();
  276. }
  277. void ReflectionProbe::handleDeleteAction()
  278. {
  279. //we're deleting it?
  280. //Then we need to clear out the processed cubemaps(if we have them)
  281. if (mReflectionModeType != StaticCubemap)
  282. {
  283. String prefilPath = getPrefilterMapPath();
  284. if (Torque::FS::IsFile(prefilPath))
  285. {
  286. Torque::FS::Remove(prefilPath);
  287. }
  288. String irrPath = getIrradianceMapPath();
  289. if (Torque::FS::IsFile(irrPath))
  290. {
  291. Torque::FS::Remove(irrPath);
  292. }
  293. }
  294. Parent::handleDeleteAction();
  295. }
  296. void ReflectionProbe::setTransform(const MatrixF & mat)
  297. {
  298. // Let SceneObject handle all of the matrix manipulation
  299. if (!mEditPosOffset)
  300. {
  301. Parent::setTransform(mat);
  302. setMaskBits(TransformMask);
  303. }
  304. else
  305. {
  306. mProbeRefOffset = mat.getPosition();
  307. setMaskBits(StaticDataMask);
  308. }
  309. mDirty = true;
  310. }
  311. const MatrixF& ReflectionProbe::getTransform() const
  312. {
  313. if (!mEditPosOffset)
  314. return mObjToWorld;
  315. else
  316. {
  317. sEditingTransformMat = MatrixF::Identity;
  318. sEditingTransformMat.setPosition(mProbeRefOffset);
  319. return sEditingTransformMat;
  320. }
  321. }
  322. void ReflectionProbe::setScale(const VectorF &scale)
  323. {
  324. if (!mEditPosOffset)
  325. {
  326. Parent::setScale(scale);
  327. setMaskBits(TransformMask);
  328. }
  329. else
  330. {
  331. mProbeRefScale = scale;
  332. setMaskBits(StaticDataMask);
  333. }
  334. mDirty = true;
  335. }
  336. const VectorF& ReflectionProbe::getScale() const
  337. {
  338. if (!mEditPosOffset)
  339. return mObjScale;
  340. else
  341. return mProbeRefScale;
  342. }
  343. bool ReflectionProbe::writeField(StringTableEntry fieldname, const char *value)
  344. {
  345. if (fieldname == StringTable->insert("Bake") || fieldname == StringTable->insert("EditPosOffset"))
  346. return false;
  347. return Parent::writeField(fieldname, value);
  348. }
  349. U32 ReflectionProbe::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
  350. {
  351. // Allow the Parent to get a crack at writing its info
  352. U32 retMask = Parent::packUpdate(conn, mask, stream);
  353. // Write our transform information
  354. if (stream->writeFlag(mask & TransformMask))
  355. {
  356. stream->writeFlag(mEditPosOffset);
  357. mathWrite(*stream, mObjToWorld);
  358. mathWrite(*stream, mObjScale);
  359. mathWrite(*stream, mProbeRefOffset);
  360. mathWrite(*stream, mProbeRefScale);
  361. }
  362. if (stream->writeFlag(mask & StaticDataMask))
  363. {
  364. stream->write((U32)mProbeShapeType);
  365. stream->write(mRadius);
  366. stream->write(mProbeUniqueID);
  367. stream->write((U32)mReflectionModeType);
  368. stream->writeString(mCubemapName);
  369. stream->write(mAtten);
  370. }
  371. if (stream->writeFlag(mask & EnabledMask))
  372. {
  373. stream->writeFlag(mEnabled);
  374. }
  375. stream->writeFlag(mCanDamp);
  376. return retMask;
  377. }
  378. void ReflectionProbe::unpackUpdate(NetConnection *conn, BitStream *stream)
  379. {
  380. // Let the Parent read any info it sent
  381. Parent::unpackUpdate(conn, stream);
  382. if (stream->readFlag()) // TransformMask
  383. {
  384. mEditPosOffset = stream->readFlag();
  385. mathRead(*stream, &mObjToWorld);
  386. mathRead(*stream, &mObjScale);
  387. Parent::setTransform(mObjToWorld);
  388. resetWorldBox();
  389. mathRead(*stream, &mProbeRefOffset);
  390. mathRead(*stream, &mProbeRefScale);
  391. mDirty = true;
  392. }
  393. if (stream->readFlag()) // StaticDataMask
  394. {
  395. U32 shapeType = ProbeInfo::Sphere;
  396. stream->read(&shapeType);
  397. mProbeShapeType = (ProbeInfo::ProbeShapeType)shapeType;
  398. stream->read(&mRadius);
  399. stream->read(&mProbeUniqueID);
  400. U32 oldReflectModeType = mReflectionModeType;
  401. U32 reflectModeType = BakedCubemap;
  402. stream->read(&reflectModeType);
  403. mReflectionModeType = (ReflectionModeType)reflectModeType;
  404. StringTableEntry oldCubemapName = mCubemapName;
  405. mCubemapName = stream->readSTString();
  406. if(oldReflectModeType != mReflectionModeType || oldCubemapName != mCubemapName)
  407. mCubemapDirty = true;
  408. stream->read(&mAtten);
  409. mDirty = true;
  410. }
  411. if (stream->readFlag()) // EnabledMask
  412. {
  413. mEnabled = stream->readFlag();
  414. mDirty = true;
  415. }
  416. mCanDamp = stream->readFlag();
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Object Rendering
  420. //-----------------------------------------------------------------------------
  421. void ReflectionProbe::updateProbeParams()
  422. {
  423. mProbeInfo.mObject = this;
  424. if (!mResourcesCreated)
  425. {
  426. if (!createClientResources())
  427. return;
  428. }
  429. mProbeInfo.mIsEnabled = mEnabled;
  430. mProbeInfo.mProbeShapeType = mProbeShapeType;
  431. if (mProbeShapeType == ProbeInfo::Sphere)
  432. mObjScale.set(mRadius, mRadius, mRadius);
  433. Box3F bounds;
  434. if (mProbeShapeType == ProbeInfo::Skylight)
  435. {
  436. mProbeInfo.mPosition = Point3F::Zero;
  437. mProbeInfo.mTransform = MatrixF::Identity;
  438. F32 visDist = gClientSceneGraph->getVisibleDistance();
  439. Box3F skylightBounds = Box3F(visDist * 2);
  440. skylightBounds.setCenter(Point3F::Zero);
  441. bounds = skylightBounds;
  442. setGlobalBounds();
  443. mProbeInfo.mScore = 10000.0f;
  444. }
  445. else
  446. {
  447. MatrixF transform = getTransform();
  448. mProbeInfo.mPosition = getPosition();
  449. transform.scale(getScale());
  450. mProbeInfo.mTransform = transform.inverse();
  451. bounds = mWorldBox;
  452. mProbeInfo.mScore = 1;
  453. }
  454. // Skip our transform... it just dirties mask bits.
  455. Parent::setTransform(mObjToWorld);
  456. resetWorldBox();
  457. mProbeInfo.mBounds = bounds;
  458. mProbeInfo.mExtents = getScale();
  459. mProbeInfo.mRadius = mRadius;
  460. mProbeInfo.mProbeRefOffset = mProbeRefOffset;
  461. mProbeInfo.mProbeRefScale = mProbeRefScale;
  462. mProbeInfo.mCanDamp = mCanDamp;
  463. mProbeInfo.mAtten = mAtten;
  464. mProbeInfo.mDirty = true;
  465. if (mCubemapDirty)
  466. {
  467. if (mReflectionModeType == StaticCubemap)
  468. processStaticCubemap();
  469. else if (mReflectionModeType == BakedCubemap)
  470. processBakedCubemap();
  471. else
  472. processDynamicCubemap();
  473. }
  474. }
  475. void ReflectionProbe::processDynamicCubemap()
  476. {
  477. }
  478. void ReflectionProbe::processBakedCubemap()
  479. {
  480. //mProbeInfo.mIsEnabled = false;
  481. if ((mReflectionModeType != BakedCubemap) || mProbeUniqueID.isEmpty())
  482. return;
  483. String irrPath = getIrradianceMapPath();
  484. if ((mIrridianceMap == nullptr || mIrridianceMap->mCubemap.isNull()) && Platform::isFile(irrPath))
  485. {
  486. mIrridianceMap->setCubemapFile(FileName(irrPath));
  487. mIrridianceMap->updateFaces();
  488. }
  489. if (mIrridianceMap == nullptr || mIrridianceMap->mCubemap.isNull())
  490. {
  491. Con::errorf("ReflectionProbe::processBakedCubemap() - Unable to load baked irradiance map at %s", getIrradianceMapPath().c_str());
  492. return;
  493. }
  494. String prefilPath = getPrefilterMapPath();
  495. if ((mPrefilterMap == nullptr || mPrefilterMap->mCubemap.isNull()) && Platform::isFile(prefilPath))
  496. {
  497. mPrefilterMap->setCubemapFile(FileName(prefilPath));
  498. mPrefilterMap->updateFaces();
  499. }
  500. if (mPrefilterMap == nullptr || mPrefilterMap->mCubemap.isNull())
  501. {
  502. Con::errorf("ReflectionProbe::processBakedCubemap() - Unable to load baked prefilter map at %s", getPrefilterMapPath().c_str());
  503. return;
  504. }
  505. mProbeInfo.mPrefilterCubemap = mPrefilterMap->mCubemap;
  506. mProbeInfo.mIrradianceCubemap = mIrridianceMap->mCubemap;
  507. if (mEnabled && !mProbeInfo.mPrefilterCubemap.isNull() && !mProbeInfo.mIrradianceCubemap.isNull())
  508. {
  509. //mProbeInfo.mIsEnabled = true;
  510. mCubemapDirty = false;
  511. //Update the probe manager with our new texture!
  512. PROBEMGR->updateProbeTexture(&mProbeInfo);
  513. //now, cleanup
  514. mProbeInfo.mPrefilterCubemap.free();
  515. mProbeInfo.mIrradianceCubemap.free();
  516. }
  517. else
  518. {
  519. //if we failed, disable
  520. mProbeInfo.mIsEnabled = false;
  521. }
  522. }
  523. void ReflectionProbe::processStaticCubemap()
  524. {
  525. mProbeInfo.mIsEnabled = false;
  526. String path = Con::getVariable("$pref::ReflectionProbes::CurrentLevelPath", "levels/");
  527. char irradFileName[256];
  528. dSprintf(irradFileName, 256, "%s%s_Irradiance.dds", path.c_str(), mCubemapName);
  529. if (Platform::isFile(irradFileName))
  530. {
  531. mIrridianceMap->setCubemapFile(FileName(irradFileName));
  532. mIrridianceMap->updateFaces();
  533. }
  534. if (mIrridianceMap == nullptr || mIrridianceMap->mCubemap.isNull())
  535. {
  536. Con::errorf("ReflectionProbe::processStaticCubemap() - Unable to load baked irradiance map at %s", irradFileName);
  537. return;
  538. }
  539. char prefilterFileName[256];
  540. dSprintf(prefilterFileName, 256, "%s%s_Prefilter.dds", path.c_str(), mCubemapName);
  541. if (Platform::isFile(prefilterFileName))
  542. {
  543. mPrefilterMap->setCubemapFile(FileName(prefilterFileName));
  544. mPrefilterMap->updateFaces();
  545. }
  546. if (mPrefilterMap == nullptr || mPrefilterMap->mCubemap.isNull())
  547. {
  548. Con::errorf("ReflectionProbe::processStaticCubemap() - Unable to load baked prefilter map at %s", prefilterFileName);
  549. return;
  550. }
  551. if (!Platform::isFile(prefilterFileName) || !Platform::isFile(irradFileName))
  552. {
  553. //If we are missing either of the files, just re-run the bake
  554. Sim::findObject(mCubemapName, mStaticCubemap);
  555. if (!mStaticCubemap)
  556. {
  557. Con::errorf("ReflectionProbe::updateMaterial() - unable to find static cubemap file!");
  558. return;
  559. }
  560. if (mStaticCubemap->mCubemap.isNull())
  561. {
  562. mStaticCubemap->createMap();
  563. mStaticCubemap->updateFaces();
  564. }
  565. if (mUseHDRCaptures)
  566. {
  567. mIrridianceMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mIrridianceMap_HDR");
  568. mPrefilterMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mPrefilterMap_HDR");
  569. }
  570. else
  571. {
  572. mIrridianceMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR8G8B8A8, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mIrridianceMap");
  573. mPrefilterMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR8G8B8A8, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mPrefilterMap");
  574. }
  575. GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
  576. IBLUtilities::GenerateIrradianceMap(renderTarget, mStaticCubemap->mCubemap, mIrridianceMap->mCubemap);
  577. IBLUtilities::GeneratePrefilterMap(renderTarget, mStaticCubemap->mCubemap, mPrefilterMipLevels, mPrefilterMap->mCubemap);
  578. IBLUtilities::SaveCubeMap(irradFileName, mIrridianceMap->mCubemap);
  579. IBLUtilities::SaveCubeMap(prefilterFileName, mPrefilterMap->mCubemap);
  580. }
  581. if ((mIrridianceMap != nullptr && !mIrridianceMap->mCubemap.isNull()) && (mPrefilterMap != nullptr && !mPrefilterMap->mCubemap.isNull()))
  582. {
  583. mProbeInfo.mPrefilterCubemap = mPrefilterMap->mCubemap;
  584. mProbeInfo.mIrradianceCubemap = mIrridianceMap->mCubemap;
  585. }
  586. if (mEnabled && mProbeInfo.mPrefilterCubemap.isValid() && mProbeInfo.mIrradianceCubemap.isValid())
  587. {
  588. mProbeInfo.mIsEnabled = true;
  589. mCubemapDirty = false;
  590. //Update the probe manager with our new texture!
  591. PROBEMGR->updateProbeTexture(&mProbeInfo);
  592. }
  593. }
  594. bool ReflectionProbe::createClientResources()
  595. {
  596. PROBEMGR->registerProbe(&mProbeInfo);
  597. mProbeInfo.mIsEnabled = false;
  598. //irridiance resources
  599. if (!mIrridianceMap)
  600. {
  601. mIrridianceMap = new CubemapData();
  602. mIrridianceMap->registerObject();
  603. mIrridianceMap->createMap();
  604. }
  605. //
  606. if (!mPrefilterMap)
  607. {
  608. mPrefilterMap = new CubemapData();
  609. mPrefilterMap->registerObject();
  610. mPrefilterMap->createMap();
  611. }
  612. mResourcesCreated = true;
  613. mCubemapDirty = true;
  614. return true;
  615. }
  616. String ReflectionProbe::getPrefilterMapPath()
  617. {
  618. if (mProbeUniqueID.isEmpty())
  619. {
  620. Con::errorf("ReflectionProbe::getPrefilterMapPath() - We don't have a set output path or persistant id, so no valid path can be provided!");
  621. return "";
  622. }
  623. String path = Con::getVariable("$pref::ReflectionProbes::CurrentLevelPath", "levels/");
  624. char fileName[256];
  625. dSprintf(fileName, 256, "%s%s_Prefilter.dds", path.c_str(), mProbeUniqueID.c_str());
  626. return fileName;
  627. }
  628. String ReflectionProbe::getIrradianceMapPath()
  629. {
  630. if (mProbeUniqueID.isEmpty())
  631. {
  632. Con::errorf("ReflectionProbe::getIrradianceMapPath() - We don't have a set output path or persistant id, so no valid path can be provided!");
  633. return "";
  634. }
  635. String path = Con::getVariable("$pref::ReflectionProbes::CurrentLevelPath", "levels/");
  636. char fileName[256];
  637. dSprintf(fileName, 256, "%s%s_Irradiance.dds", path.c_str(), mProbeUniqueID.c_str());
  638. return fileName;
  639. }
  640. void ReflectionProbe::bake()
  641. {
  642. if (mReflectionModeType != BakedCubemap)
  643. return;
  644. PROBEMGR->preBake();
  645. PROBEMGR->bakeProbe(this);
  646. PROBEMGR->postBake();
  647. setMaskBits(-1);
  648. }
  649. //-----------------------------------------------------------------------------
  650. //Rendering of editing/debug stuff
  651. //-----------------------------------------------------------------------------
  652. void ReflectionProbe::createEditorResources()
  653. {
  654. #ifdef TORQUE_TOOLS
  655. // Clean up our previous shape
  656. if (mEditorShapeInst)
  657. SAFE_DELETE(mEditorShapeInst);
  658. mEditorShape = NULL;
  659. String shapeFile = "tools/resources/previewSphereShape.dae";
  660. // Attempt to get the resource from the ResourceManager
  661. mEditorShape = ResourceManager::get().load(shapeFile);
  662. if (mEditorShape)
  663. {
  664. mEditorShapeInst = new TSShapeInstance(mEditorShape, isClientObject());
  665. }
  666. #endif
  667. }
  668. void ReflectionProbe::prepRenderImage(SceneRenderState *state)
  669. {
  670. if (!mEnabled || (!RenderProbeMgr::smRenderReflectionProbes || RenderProbeMgr::smBakeReflectionProbes))
  671. return;
  672. Point3F distVec = getRenderPosition() - state->getCameraPosition();
  673. F32 dist = distVec.len();
  674. //Culling distance. Can be adjusted for performance options considerations via the scalar
  675. if (dist > RenderProbeMgr::smMaxProbeDrawDistance * Con::getFloatVariable("$pref::GI::ProbeDrawDistScale", 1.0))
  676. {
  677. mProbeInfo.mScore = RenderProbeMgr::smMaxProbeDrawDistance;
  678. return;
  679. }
  680. if (mReflectionModeType == DynamicCubemap && mRefreshRateMS < (Platform::getRealMilliseconds() - mDynamicLastBakeMS))
  681. {
  682. bake();
  683. mDynamicLastBakeMS = Platform::getRealMilliseconds();
  684. }
  685. //Submit our probe to actually do the probe action
  686. // Get a handy pointer to our RenderPassmanager
  687. //RenderPassManager *renderPass = state->getRenderPass();
  688. //Update our score based on our radius, distance
  689. mProbeInfo.mScore = mMax(dist, 1.0f);
  690. Point3F vect = distVec;
  691. vect.normalizeSafe();
  692. //mProbeInfo.mScore *= mMax(mAbs(mDot(vect, state->getCameraTransform().getForwardVector())),0.001f);
  693. PROBEMGR->submitProbe(&mProbeInfo);
  694. #ifdef TORQUE_TOOLS
  695. if (ReflectionProbe::smRenderPreviewProbes && gEditingMission && mPrefilterMap != nullptr)
  696. {
  697. if(!mEditorShapeInst)
  698. createEditorResources();
  699. GFXTransformSaver saver;
  700. // Calculate the distance of this object from the camera
  701. Point3F cameraOffset;
  702. getRenderTransform().getColumn(3, &cameraOffset);
  703. cameraOffset -= state->getDiffuseCameraPosition();
  704. dist = cameraOffset.len();
  705. if (dist < 0.01f)
  706. dist = 0.01f;
  707. // Set up the LOD for the shape
  708. F32 invScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z));
  709. mEditorShapeInst->setDetailFromDistance(state, dist * invScale);
  710. // Make sure we have a valid level of detail
  711. if (mEditorShapeInst->getCurrentDetail() < 0)
  712. return;
  713. BaseMatInstance* probePrevMat = mEditorShapeInst->getMaterialList()->getMaterialInst(0);
  714. if (probePrevMat == nullptr)
  715. return;
  716. setPreviewMatParameters(state, probePrevMat);
  717. // GFXTransformSaver is a handy helper class that restores
  718. // the current GFX matrices to their original values when
  719. // it goes out of scope at the end of the function
  720. // Set up our TS render state
  721. TSRenderState rdata;
  722. rdata.setSceneState(state);
  723. rdata.setFadeOverride(1.0f);
  724. if(mReflectionModeType != DynamicCubemap)
  725. rdata.setCubemap(mPrefilterMap->mCubemap);
  726. else
  727. rdata.setCubemap(mDynamicCubemap);
  728. // We might have some forward lit materials
  729. // so pass down a query to gather lights.
  730. LightQuery query;
  731. query.init(getWorldSphere());
  732. rdata.setLightQuery(&query);
  733. // Set the world matrix to the objects render transform
  734. MatrixF mat = getRenderTransform();
  735. GFX->setWorldMatrix(mat);
  736. // Animate the the shape
  737. mEditorShapeInst->animate();
  738. // Allow the shape to submit the RenderInst(s) for itself
  739. mEditorShapeInst->render(rdata);
  740. saver.restore();
  741. }
  742. // If the probe is selected or probe visualization
  743. // is enabled then register the callback.
  744. const bool isSelectedInEditor = (gEditingMission && isSelected());
  745. if (isSelectedInEditor)
  746. {
  747. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  748. ri->renderDelegate.bind(this, &ReflectionProbe::_onRenderViz);
  749. ri->type = RenderPassManager::RIT_Editor;
  750. state->getRenderPass()->addInst(ri);
  751. }
  752. #endif
  753. }
  754. void ReflectionProbe::_onRenderViz(ObjectRenderInst *ri,
  755. SceneRenderState *state,
  756. BaseMatInstance *overrideMat)
  757. {
  758. if (!RenderProbeMgr::smRenderReflectionProbes)
  759. return;
  760. GFXDrawUtil *draw = GFX->getDrawUtil();
  761. GFXStateBlockDesc desc;
  762. desc.setZReadWrite(true, false);
  763. desc.setCullMode(GFXCullNone);
  764. desc.setBlend(true);
  765. //desc.fillMode = GFXFillWireframe;
  766. // Base the sphere color on the light color.
  767. ColorI color = ColorI(255, 0, 255, 63);
  768. const MatrixF worldToObjectXfm = mObjToWorld;
  769. if (mProbeShapeType == ProbeInfo::Sphere)
  770. {
  771. draw->drawSphere(desc, mRadius, getPosition(), color);
  772. }
  773. else
  774. {
  775. Point3F tscl = worldToObjectXfm.getScale();
  776. Box3F projCube(-mObjScale/2, mObjScale / 2);
  777. projCube.setCenter(getPosition());
  778. draw->drawCube(desc, projCube, color, &worldToObjectXfm);
  779. }
  780. Point3F renderPos = getRenderTransform().getPosition();
  781. Box3F refCube = Box3F(-mProbeRefScale / 2, mProbeRefScale / 2);
  782. refCube.setCenter(renderPos + mProbeRefOffset);
  783. color = ColorI(0, 255, 255, 63);
  784. draw->drawCube(desc, refCube, color, &worldToObjectXfm);
  785. }
  786. void ReflectionProbe::setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat)
  787. {
  788. if (!mat->getFeatures().hasFeature(MFT_isDeferred))
  789. return;
  790. //Set up the params
  791. MaterialParameters *matParams = mat->getMaterialParameters();
  792. //Get the deferred render target
  793. NamedTexTarget* deferredTexTarget = NamedTexTarget::find("deferred");
  794. GFXTextureObject *deferredTexObject = deferredTexTarget->getTexture();
  795. if (!deferredTexObject)
  796. return;
  797. GFX->setTexture(0, deferredTexObject);
  798. //Set the cubemap
  799. GFX->setTexture(1, mPrefilterMap->mCubemap);
  800. //Set the invViewMat
  801. MatrixSet &matrixSet = renderState->getRenderPass()->getMatrixSet();
  802. const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
  803. MaterialParameterHandle *invViewMat = mat->getMaterialParameterHandle("$invViewMat");
  804. matParams->setSafe(invViewMat, worldToCameraXfm);
  805. }
  806. DefineEngineMethod(ReflectionProbe, postApply, void, (), ,
  807. "A utility method for forcing a network update.\n")
  808. {
  809. object->inspectPostApply();
  810. }
  811. DefineEngineMethod(ReflectionProbe, Bake, void, (), ,
  812. "@brief Bakes the cubemaps for a reflection probe\n\n.")
  813. {
  814. ReflectionProbe *clientProbe = (ReflectionProbe*)object->getClientObject();
  815. if (clientProbe)
  816. {
  817. clientProbe->bake();
  818. }
  819. }