reflectionManager.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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 "platform/platform.h"
  23. #include "scene/reflectionManager.h"
  24. #include "platform/profiler.h"
  25. #include "platform/platformTimer.h"
  26. #include "console/consoleTypes.h"
  27. #include "core/tAlgorithm.h"
  28. #include "math/mMathFn.h"
  29. #include "math/mathUtils.h"
  30. #include "T3D/gameBase/gameConnection.h"
  31. #include "ts/tsShapeInstance.h"
  32. #include "gui/3d/guiTSControl.h"
  33. #include "scene/sceneManager.h"
  34. #include "gfx/gfxDebugEvent.h"
  35. #include "gfx/gfxStringEnumTranslate.h"
  36. #include "gfx/screenshot.h"
  37. #include "core/module.h"
  38. #include "scene/reflectionMatHook.h"
  39. #include "console/engineAPI.h"
  40. MODULE_BEGIN( ReflectionManager )
  41. MODULE_INIT
  42. {
  43. ManagedSingleton< ReflectionManager >::createSingleton();
  44. ReflectionManager::initConsole();
  45. }
  46. MODULE_SHUTDOWN
  47. {
  48. ManagedSingleton< ReflectionManager >::deleteSingleton();
  49. }
  50. MODULE_END;
  51. GFX_ImplementTextureProfile( ReflectRenderTargetProfile,
  52. GFXTextureProfile::DiffuseMap,
  53. GFXTextureProfile::PreserveSize | GFXTextureProfile::RenderTarget | GFXTextureProfile::Pooled,
  54. GFXTextureProfile::NONE );
  55. GFX_ImplementTextureProfile( RefractTextureProfile,
  56. GFXTextureProfile::DiffuseMap,
  57. GFXTextureProfile::PreserveSize |
  58. GFXTextureProfile::RenderTarget |
  59. GFXTextureProfile::Pooled,
  60. GFXTextureProfile::NONE );
  61. static S32 QSORT_CALLBACK compareReflectors( const void *a, const void *b )
  62. {
  63. const ReflectorBase *A = *((ReflectorBase**)a);
  64. const ReflectorBase *B = *((ReflectorBase**)b);
  65. F32 dif = B->score - A->score;
  66. return (S32)mFloor( dif );
  67. }
  68. U32 ReflectionManager::smFrameReflectionMS = 10;
  69. F32 ReflectionManager::smRefractTexScale = 0.5f;
  70. ReflectionManager::ReflectionManager()
  71. : mUpdateRefract( true ),
  72. mReflectFormat( GFXFormatR8G8B8A8 ),
  73. mLastUpdateMs( 0 )
  74. {
  75. mTimer = PlatformTimer::create();
  76. GFXDevice::getDeviceEventSignal().notify( this, &ReflectionManager::_handleDeviceEvent );
  77. }
  78. void ReflectionManager::initConsole()
  79. {
  80. Con::addVariable( "$pref::Reflect::refractTexScale", TypeF32, &ReflectionManager::smRefractTexScale, "RefractTex has dimensions equal to the active render target scaled in both x and y by this float.\n"
  81. "@ingroup Rendering");
  82. Con::addVariable( "$pref::Reflect::frameLimitMS", TypeS32, &ReflectionManager::smFrameReflectionMS, "ReflectionManager tries not to spend more than this amount of time updating reflections per frame.\n"
  83. "@ingroup Rendering");
  84. }
  85. ReflectionManager::~ReflectionManager()
  86. {
  87. SAFE_DELETE( mTimer );
  88. AssertFatal( mReflectors.size() == 0, "ReflectionManager, some reflectors were left nregistered!" );
  89. GFXDevice::getDeviceEventSignal().remove( this, &ReflectionManager::_handleDeviceEvent );
  90. }
  91. void ReflectionManager::registerReflector( ReflectorBase *reflector )
  92. {
  93. mReflectors.push_back_unique( reflector );
  94. }
  95. void ReflectionManager::unregisterReflector( ReflectorBase *reflector )
  96. {
  97. mReflectors.remove( reflector );
  98. }
  99. void ReflectionManager::update( F32 timeSlice,
  100. const Point2I &resolution,
  101. const CameraQuery &query )
  102. {
  103. GFXDEBUGEVENT_SCOPE( UpdateReflections, ColorI::WHITE );
  104. if ( mReflectors.empty() )
  105. return;
  106. PROFILE_SCOPE( ReflectionManager_Update );
  107. // Calculate our target time from the slice.
  108. U32 targetMs = timeSlice * smFrameReflectionMS;
  109. // Setup a culler for testing the
  110. // visibility of reflectors.
  111. Frustum culler;
  112. // jamesu - normally we just need a frustum which covers the current ports, however for SBS mode
  113. // we need something which covers both viewports.
  114. S32 stereoTarget = GFX->getCurrentStereoTarget();
  115. if (stereoTarget != -1)
  116. {
  117. // In this case we're rendering in stereo using a specific eye
  118. MathUtils::makeFovPortFrustum(&culler, false, query.nearPlane, query.farPlane, query.fovPort[stereoTarget], query.headMatrix);
  119. }
  120. else if (GFX->getCurrentRenderStyle() == GFXDevice::RS_StereoSideBySide)
  121. {
  122. // Calculate an ideal culling size here, we'll just assume double fov based on the first fovport based on
  123. // the head position.
  124. FovPort port = query.fovPort[0];
  125. F32 leftSize = query.nearPlane * port.leftTan;
  126. F32 rightSize = query.nearPlane * port.rightTan;
  127. F32 upSize = query.nearPlane * port.upTan;
  128. F32 downSize = query.nearPlane * port.downTan;
  129. F32 left = -leftSize;
  130. F32 right = rightSize;
  131. F32 top = upSize;
  132. F32 bottom = -downSize;
  133. F32 fovInRadians = mAtan2((top - bottom) / 2.0f, query.nearPlane) * 3.0f;
  134. culler.set(false,
  135. fovInRadians,
  136. (F32)(query.stereoViewports[0].extent.x + query.stereoViewports[1].extent.x) / (F32)query.stereoViewports[0].extent.y,
  137. query.nearPlane,
  138. query.farPlane,
  139. query.headMatrix);
  140. }
  141. else
  142. {
  143. // Normal culling
  144. culler.set(false,
  145. query.fov,
  146. (F32)resolution.x / (F32)resolution.y,
  147. query.nearPlane,
  148. query.farPlane,
  149. query.cameraMatrix);
  150. }
  151. // Manipulate the frustum for tiled screenshots
  152. const bool screenShotMode = gScreenShot && gScreenShot->isPending();
  153. if ( screenShotMode )
  154. gScreenShot->tileFrustum( culler );
  155. // We use the frame time and not real time
  156. // here as this may be called multiple times
  157. // within a frame.
  158. U32 startOfUpdateMs = Platform::getVirtualMilliseconds();
  159. // Save this for interested parties.
  160. mLastUpdateMs = startOfUpdateMs;
  161. ReflectParams refparams;
  162. refparams.query = &query;
  163. refparams.viewportExtent = resolution;
  164. refparams.culler = culler;
  165. refparams.startOfUpdateMs = startOfUpdateMs;
  166. refparams.eyeId = stereoTarget;
  167. // Update the reflection score.
  168. ReflectorList::iterator reflectorIter = mReflectors.begin();
  169. for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
  170. (*reflectorIter)->calcScore( refparams );
  171. // Sort them by the score.
  172. dQsort( mReflectors.address(), mReflectors.size(), sizeof(ReflectorBase*), compareReflectors );
  173. // Update as many reflections as we can
  174. // within the target time limit.
  175. mTimer->getElapsedMs();
  176. mTimer->reset();
  177. U32 numUpdated = 0;
  178. U32 currentTarget = stereoTarget >= 0 ? stereoTarget : 0;
  179. reflectorIter = mReflectors.begin();
  180. for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
  181. {
  182. // We're sorted by score... so once we reach
  183. // a zero score we have nothing more to update.
  184. if ( (*reflectorIter)->score <= 0.0f && !screenShotMode )
  185. break;
  186. (*reflectorIter)->updateReflection( refparams );
  187. if (stereoTarget != 0) // only update MS if we're not rendering the left eye in separate mode
  188. {
  189. (*reflectorIter)->lastUpdateMs = startOfUpdateMs;
  190. }
  191. numUpdated++;
  192. // If we run out of update time then stop.
  193. if ( mTimer->getElapsedMs() > targetMs && !screenShotMode && (*reflectorIter)->score < 1000.0f )
  194. break;
  195. }
  196. // Set metric/debug related script variables...
  197. U32 numVisible = 0;
  198. U32 numOccluded = 0;
  199. reflectorIter = mReflectors.begin();
  200. for ( ; reflectorIter != mReflectors.end(); reflectorIter++ )
  201. {
  202. ReflectorBase *pReflector = (*reflectorIter);
  203. if ( pReflector->isOccluded() )
  204. numOccluded++;
  205. else
  206. numVisible++;
  207. }
  208. #ifdef TORQUE_GATHER_METRICS
  209. U32 numEnabled = mReflectors.size();
  210. U32 totalElapsed = mTimer->getElapsedMs();
  211. const GFXTextureProfileStats &stats = ReflectRenderTargetProfile.getStats();
  212. F32 mb = ( stats.activeBytes / 1024.0f ) / 1024.0f;
  213. char temp[256];
  214. dSprintf( temp, 256, "%s %d %0.2f\n",
  215. ReflectRenderTargetProfile.getName().c_str(),
  216. stats.activeCount,
  217. mb );
  218. Con::setVariable( "$Reflect::textureStats", temp );
  219. Con::setIntVariable( "$Reflect::renderTargetsAllocated", stats.allocatedTextures );
  220. Con::setIntVariable( "$Reflect::poolSize", stats.activeCount );
  221. Con::setIntVariable( "$Reflect::numObjects", numEnabled );
  222. Con::setIntVariable( "$Reflect::numVisible", numVisible );
  223. Con::setIntVariable( "$Reflect::numOccluded", numOccluded );
  224. Con::setIntVariable( "$Reflect::numUpdated", numUpdated );
  225. Con::setIntVariable( "$Reflect::elapsed", totalElapsed );
  226. #endif
  227. }
  228. GFXTexHandle ReflectionManager::allocRenderTarget( const Point2I &size )
  229. {
  230. return GFXTexHandle( size.x, size.y, mReflectFormat,
  231. &ReflectRenderTargetProfile,
  232. avar("%s() - mReflectTex (line %d)", __FUNCTION__, __LINE__) );
  233. }
  234. GFXTextureObject* ReflectionManager::getRefractTex( bool forceUpdate )
  235. {
  236. GFXTarget *target = GFX->getActiveRenderTarget();
  237. GFXFormat targetFormat = target->getFormat();
  238. const Point2I &targetSize = target->getSize();
  239. #if defined(TORQUE_OS_XENON)
  240. // On the Xbox360, it needs to do a resolveTo from the active target, so this
  241. // may as well be the full size of the active target
  242. const U32 desWidth = targetSize.x;
  243. const U32 desHeight = targetSize.y;
  244. #else
  245. U32 desWidth, desHeight;
  246. // D3D11 needs to be the same size as the active target
  247. if (GFX->getAdapterType() == Direct3D11)
  248. {
  249. desWidth = targetSize.x;
  250. desHeight = targetSize.y;
  251. }
  252. else
  253. {
  254. desWidth = mFloor((F32)targetSize.x * smRefractTexScale);
  255. desHeight = mFloor((F32)targetSize.y * smRefractTexScale);
  256. }
  257. #endif
  258. if ( mRefractTex.isNull() ||
  259. mRefractTex->getWidth() != desWidth ||
  260. mRefractTex->getHeight() != desHeight ||
  261. mRefractTex->getFormat() != targetFormat )
  262. {
  263. mRefractTex.set( desWidth, desHeight, targetFormat, &RefractTextureProfile, "mRefractTex" );
  264. mUpdateRefract = true;
  265. }
  266. if ( forceUpdate || mUpdateRefract )
  267. {
  268. target->resolveTo( mRefractTex );
  269. mUpdateRefract = false;
  270. }
  271. return mRefractTex;
  272. }
  273. BaseMatInstance* ReflectionManager::getReflectionMaterial( BaseMatInstance *inMat ) const
  274. {
  275. // See if we have an existing material hook.
  276. ReflectionMaterialHook *hook = static_cast<ReflectionMaterialHook*>( inMat->getHook( ReflectionMaterialHook::Type ) );
  277. if ( !hook )
  278. {
  279. // Create a hook and initialize it using the incoming material.
  280. hook = new ReflectionMaterialHook;
  281. hook->init( inMat );
  282. inMat->addHook( hook );
  283. }
  284. return hook->getReflectMat();
  285. }
  286. bool ReflectionManager::_handleDeviceEvent( GFXDevice::GFXDeviceEventType evt )
  287. {
  288. switch( evt )
  289. {
  290. case GFXDevice::deStartOfFrame:
  291. case GFXDevice::deStartOfField:
  292. mUpdateRefract = true;
  293. break;
  294. case GFXDevice::deDestroy:
  295. mRefractTex = NULL;
  296. break;
  297. default:
  298. break;
  299. }
  300. return true;
  301. }
  302. DefineEngineFunction( setReflectFormat, void, ( GFXFormat format ),,
  303. "Set the reflection texture format.\n"
  304. "@ingroup GFX\n" )
  305. {
  306. REFLECTMGR->setReflectFormat( format );
  307. }