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