tsLastDetail.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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 "ts/tsLastDetail.h"
  24. #include "renderInstance/renderPassManager.h"
  25. #include "ts/tsShapeInstance.h"
  26. #include "scene/sceneManager.h"
  27. #include "scene/sceneRenderState.h"
  28. #include "lighting/lightInfo.h"
  29. #include "renderInstance/renderImposterMgr.h"
  30. #include "gfx/gfxTransformSaver.h"
  31. #include "gfx/bitmap/ddsFile.h"
  32. #include "gfx/bitmap/imageUtils.h"
  33. #include "gfx/gfxTextureManager.h"
  34. #include "math/mRandom.h"
  35. #include "core/stream/fileStream.h"
  36. #include "util/imposterCapture.h"
  37. #include "materials/materialManager.h"
  38. #include "materials/materialFeatureTypes.h"
  39. #include "console/consoleTypes.h"
  40. #include "console/engineAPI.h"
  41. GFXImplementVertexFormat( ImposterState )
  42. {
  43. addElement( "POSITION", GFXDeclType_Float4 );
  44. addElement( "ImposterParams", GFXDeclType_Float2, 0 );
  45. addElement( "ImposterUpVec", GFXDeclType_Float3, 1 );
  46. addElement( "ImposterRightVec", GFXDeclType_Float3, 2 );
  47. };
  48. Vector<TSLastDetail*> TSLastDetail::smLastDetails;
  49. bool TSLastDetail::smCanShadow = true;
  50. AFTER_MODULE_INIT( Sim )
  51. {
  52. Con::addVariable( "$pref::imposter::canShadow", TypeBool, &TSLastDetail::smCanShadow,
  53. "User preference which toggles shadows from imposters. Defaults to true.\n"
  54. "@ingroup Rendering\n" );
  55. }
  56. TSLastDetail::TSLastDetail( TSShape *shape,
  57. const String &cachePath,
  58. U32 numEquatorSteps,
  59. U32 numPolarSteps,
  60. F32 polarAngle,
  61. bool includePoles,
  62. S32 dl, S32 dim )
  63. {
  64. mNumEquatorSteps = getMax( numEquatorSteps, (U32)1 );
  65. mNumPolarSteps = numPolarSteps;
  66. mPolarAngle = polarAngle;
  67. mIncludePoles = includePoles;
  68. mShape = shape;
  69. mDl = dl;
  70. mDim = getMax( dim, (S32)32 );
  71. mRadius = mShape->mRadius;
  72. mCenter = mShape->center;
  73. mCachePath = cachePath;
  74. mDiffusePath = mCachePath + "_imposter.dds";
  75. mNormalPath = mCachePath + "_imposter_normals.dds";
  76. mMaterial = NULL;
  77. mMatInstance = NULL;
  78. // Store this in the static list.
  79. smLastDetails.push_back( this );
  80. }
  81. TSLastDetail::TSLastDetail(TSShape* shape,
  82. const String& cachePath,
  83. const String& diffusePath,
  84. const String& normalPath,
  85. U32 numEquatorSteps,
  86. U32 numPolarSteps,
  87. F32 polarAngle,
  88. bool includePoles,
  89. S32 dl, S32 dim)
  90. {
  91. mNumEquatorSteps = getMax(numEquatorSteps, (U32)1);
  92. mNumPolarSteps = numPolarSteps;
  93. mPolarAngle = polarAngle;
  94. mIncludePoles = includePoles;
  95. mShape = shape;
  96. mDl = dl;
  97. mDim = getMax(dim, (S32)32);
  98. mRadius = mShape->mRadius;
  99. mCenter = mShape->center;
  100. mCachePath = cachePath;
  101. mDiffusePath = diffusePath;
  102. mNormalPath = normalPath;
  103. mMaterial = NULL;
  104. mMatInstance = NULL;
  105. // Store this in the static list.
  106. smLastDetails.push_back(this);
  107. }
  108. TSLastDetail::~TSLastDetail()
  109. {
  110. SAFE_DELETE( mMatInstance );
  111. if ( mMaterial )
  112. mMaterial->deleteObject();
  113. // Remove ourselves from the list.
  114. Vector<TSLastDetail*>::iterator iter = T3D::find( smLastDetails.begin(), smLastDetails.end(), this );
  115. smLastDetails.erase( iter );
  116. }
  117. void TSLastDetail::render( const TSRenderState &rdata, F32 alpha )
  118. {
  119. // Early out if we have nothing to render.
  120. if ( alpha < 0.01f ||
  121. !mMatInstance ||
  122. mMaterial->mImposterUVs.size() == 0 )
  123. return;
  124. const MatrixF &mat = GFX->getWorldMatrix();
  125. // Post a render instance for this imposter... the special
  126. // imposter render manager will do the magic!
  127. RenderPassManager *renderPass = rdata.getSceneState()->getRenderPass();
  128. ImposterRenderInst *ri = renderPass->allocInst<ImposterRenderInst>();
  129. ri->mat = rdata.getSceneState()->getOverrideMaterial( mMatInstance );
  130. ri->state.alpha = alpha;
  131. // Store the up and right vectors of the rotation
  132. // and we'll generate the up vector in the shader.
  133. //
  134. // This is faster than building a quat on the
  135. // CPU and then rebuilding the matrix on the GPU.
  136. //
  137. // NOTE: These vector include scale.
  138. //
  139. mat.getColumn( 2, &ri->state.upVec );
  140. mat.getColumn( 0, &ri->state.rightVec );
  141. // We send the unscaled size and the vertex shader
  142. // will use the orientation vectors above to scale it.
  143. ri->state.halfSize = mRadius;
  144. // We use the center of the object bounds for
  145. // the center of the billboard quad.
  146. mat.mulP( mCenter, &ri->state.center );
  147. // We sort by the imposter type first so that RIT_Imposter and s
  148. // RIT_ImposterBatches do not get mixed together.
  149. //
  150. // We then sort by material.
  151. //
  152. ri->defaultKey = 1;
  153. ri->defaultKey2 = ri->mat->getStateHint();
  154. renderPass->addInst( ri );
  155. }
  156. void TSLastDetail::update( bool forceUpdate )
  157. {
  158. // This should never be called on a dedicated server or
  159. // anywhere else where we don't have a GFX device!
  160. AssertFatal( GFXDevice::devicePresent(), "TSLastDetail::update() - Cannot update without a GFX device!" );
  161. // Clear the materialfirst.
  162. SAFE_DELETE( mMatInstance );
  163. if ( mMaterial )
  164. {
  165. mMaterial->deleteObject();
  166. mMaterial = NULL;
  167. }
  168. // Make sure imposter textures have been flushed (and not just queued for deletion)
  169. TEXMGR->cleanupCache();
  170. // Get the real path to the source shape for doing modified time
  171. // comparisons... this might be different if the DAEs have been
  172. // deleted from the install.
  173. String shapeFile( mCachePath );
  174. if ( !Torque::FS::IsFile( shapeFile ) )
  175. {
  176. Torque::Path path(shapeFile);
  177. path.setExtension("cached.dts");
  178. shapeFile = path.getFullPath();
  179. if ( !Torque::FS::IsFile( shapeFile ) )
  180. {
  181. Con::errorf( "TSLastDetail::update - '%s' could not be found!", mCachePath.c_str() );
  182. return;
  183. }
  184. }
  185. // Do we need to update the imposter?
  186. const String diffuseMapPath = _getDiffuseMapPath();
  187. bool isFile = Platform::isFile(diffuseMapPath.c_str());
  188. if ( forceUpdate || !isFile || Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) <= 0 )
  189. _update();
  190. // If the time check fails now then the update must have not worked.
  191. if ( Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) < 0 )
  192. {
  193. Con::errorf( "TSLastDetail::update - Failed to create imposters for '%s'!", mCachePath.c_str() );
  194. return;
  195. }
  196. // Figure out what our vertex format will be.
  197. //
  198. // If we're on SM 3.0 we can do multiple vertex streams
  199. // and the performance win is big as we send 3x less data
  200. // on each imposter instance.
  201. //
  202. // The problem is SM 2.0 won't do this, so we need to
  203. // support fallback to regular single stream imposters.
  204. //
  205. //mImposterVertDecl.copy( *getGFXVertexFormat<ImposterCorner>() );
  206. //mImposterVertDecl.append( *getGFXVertexFormat<ImposterState>(), 1 );
  207. //mImposterVertDecl.getDecl();
  208. mImposterVertDecl.clear();
  209. mImposterVertDecl.copy( *getGFXVertexFormat<ImposterState>() );
  210. // Setup the material for this imposter.
  211. mMaterial = MATMGR->allocateAndRegister( String::EmptyString );
  212. mMaterial->mAutoGenerated = true;
  213. mMaterial->_setDiffuseMap(diffuseMapPath, 0);
  214. mMaterial->_setNormalMap(_getNormalMapPath(), 0);
  215. mMaterial->mImposterLimits.set( (mNumPolarSteps * 2) + 1, mNumEquatorSteps, mPolarAngle, mIncludePoles );
  216. mMaterial->mTranslucent = true;
  217. mMaterial->mTranslucentBlendOp = Material::None;
  218. mMaterial->mTranslucentZWrite = true;
  219. mMaterial->mDoubleSided = true;
  220. mMaterial->mAlphaTest = true;
  221. mMaterial->mAlphaRef = 84;
  222. // Create the material instance.
  223. FeatureSet features = MATMGR->getDefaultFeatures();
  224. features.addFeature( MFT_ImposterVert );
  225. mMatInstance = mMaterial->createMatInstance();
  226. if ( !mMatInstance->init( features, &mImposterVertDecl ) )
  227. {
  228. delete mMatInstance;
  229. mMatInstance = NULL;
  230. }
  231. // Get the diffuse texture and from its size and
  232. // the imposter dimensions we can generate the UVs.
  233. GFXTexHandle diffuseTex( diffuseMapPath, &GFXStaticTextureSRGBProfile, String::EmptyString );
  234. Point2I texSize( diffuseTex->getWidth(), diffuseTex->getHeight() );
  235. _validateDim();
  236. S32 downscaledDim = mDim >> GFXTextureManager::getTextureDownscalePower(&GFXStaticTextureSRGBProfile);
  237. // Ok... pack in bitmaps till we run out.
  238. Vector<RectF> imposterUVs;
  239. for ( S32 y=0; y+downscaledDim <= texSize.y; )
  240. {
  241. for ( S32 x=0; x+downscaledDim <= texSize.x; )
  242. {
  243. // Store the uv for later lookup.
  244. RectF info;
  245. info.point.set( (F32)x / (F32)texSize.x, (F32)y / (F32)texSize.y );
  246. info.extent.set( (F32)downscaledDim / (F32)texSize.x, (F32)downscaledDim / (F32)texSize.y );
  247. imposterUVs.push_back( info );
  248. x += downscaledDim;
  249. }
  250. y += downscaledDim;
  251. }
  252. AssertFatal( imposterUVs.size() != 0, "hey" );
  253. mMaterial->mImposterUVs = imposterUVs;
  254. }
  255. void TSLastDetail::_validateDim()
  256. {
  257. // Loop till they fit.
  258. S32 newDim = mDim;
  259. while ( true )
  260. {
  261. S32 maxImposters = ( smMaxTexSize / newDim ) * ( smMaxTexSize / newDim );
  262. S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 );
  263. if ( imposterCount <= maxImposters )
  264. break;
  265. // There are too many imposters to fit a single
  266. // texture, so we fail. These imposters are for
  267. // rendering small distant objects. If you need
  268. // a really high resolution imposter or many images
  269. // around the equator and poles, maybe you need a
  270. // custom solution.
  271. newDim /= 2;
  272. }
  273. if ( newDim != mDim )
  274. {
  275. Con::printf( "TSLastDetail::_validateDim - '%s' detail dimensions too big! Reduced from %d to %d.",
  276. mCachePath.c_str(),
  277. mDim, newDim );
  278. mDim = newDim;
  279. }
  280. }
  281. void TSLastDetail::_update()
  282. {
  283. // We're gonna render... make sure we can.
  284. bool sceneBegun = GFX->canCurrentlyRender();
  285. if ( !sceneBegun )
  286. GFX->beginScene();
  287. _validateDim();
  288. Vector<GBitmap*> bitmaps;
  289. Vector<GBitmap*> normalmaps;
  290. // We need to create our own instance to render with.
  291. TSShapeInstance *shape = new TSShapeInstance( mShape, true );
  292. // Animate the shape once.
  293. shape->animate( mDl );
  294. // So we don't have to change it everywhere.
  295. const GFXFormat format = GFXFormatR8G8B8A8;
  296. S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 );
  297. // Figure out the optimal texture size.
  298. Point2I texSize( smMaxTexSize, smMaxTexSize );
  299. while ( true )
  300. {
  301. Point2I halfSize( texSize.x / 2, texSize.y / 2 );
  302. U32 count = ( halfSize.x / mDim ) * ( halfSize.y / mDim );
  303. if ( count < imposterCount )
  304. {
  305. // Try half of the height.
  306. count = ( texSize.x / mDim ) * ( halfSize.y / mDim );
  307. if ( count >= imposterCount )
  308. texSize.y = halfSize.y;
  309. break;
  310. }
  311. texSize = halfSize;
  312. }
  313. GBitmap *imposter = NULL;
  314. GBitmap *normalmap = NULL;
  315. GBitmap destBmp( texSize.x, texSize.y, true, format );
  316. GBitmap destNormal( texSize.x, texSize.y, true, format );
  317. U32 mipLevels = destBmp.getNumMipLevels();
  318. ImposterCapture *imposterCap = new ImposterCapture();
  319. F32 equatorStepSize = M_2PI_F / (F32)mNumEquatorSteps;
  320. static const MatrixF topXfm( EulerF( -M_PI_F / 2.0f, 0, 0 ) );
  321. static const MatrixF bottomXfm( EulerF( M_PI_F / 2.0f, 0, 0 ) );
  322. MatrixF angMat;
  323. F32 polarStepSize = 0.0f;
  324. if ( mNumPolarSteps > 0 )
  325. polarStepSize = -( 0.5f * M_PI_F - mDegToRad( mPolarAngle ) ) / (F32)mNumPolarSteps;
  326. PROFILE_START(TSLastDetail_snapshots);
  327. S32 currDim = mDim;
  328. for ( S32 mip = 0; mip < mipLevels; mip++ )
  329. {
  330. if ( currDim < 1 )
  331. currDim = 1;
  332. dMemset( destBmp.getWritableBits(mip), 0, destBmp.getWidth(mip) * destBmp.getHeight(mip) * GFXFormat_getByteSize( format ) );
  333. dMemset( destNormal.getWritableBits(mip), 0, destNormal.getWidth(mip) * destNormal.getHeight(mip) * GFXFormat_getByteSize( format ) );
  334. bitmaps.clear();
  335. normalmaps.clear();
  336. F32 rotX = 0.0f;
  337. if ( mNumPolarSteps > 0 )
  338. rotX = -( mDegToRad( mPolarAngle ) - 0.5f * M_PI_F );
  339. // We capture the images in a particular order which must
  340. // match the order expected by the imposter renderer.
  341. imposterCap->begin( shape, mDl, currDim, mRadius, mCenter );
  342. for ( U32 j=0; j < (2 * mNumPolarSteps + 1); j++ )
  343. {
  344. F32 rotZ = -M_PI_F / 2.0f;
  345. for ( U32 k=0; k < mNumEquatorSteps; k++ )
  346. {
  347. angMat.mul( MatrixF( EulerF( rotX, 0, 0 ) ),
  348. MatrixF( EulerF( 0, 0, rotZ ) ) );
  349. imposterCap->capture( angMat, &imposter, &normalmap );
  350. bitmaps.push_back( imposter );
  351. normalmaps.push_back( normalmap );
  352. rotZ += equatorStepSize;
  353. }
  354. rotX += polarStepSize;
  355. if ( mIncludePoles )
  356. {
  357. imposterCap->capture( topXfm, &imposter, &normalmap );
  358. bitmaps.push_back(imposter);
  359. normalmaps.push_back( normalmap );
  360. imposterCap->capture( bottomXfm, &imposter, &normalmap );
  361. bitmaps.push_back( imposter );
  362. normalmaps.push_back( normalmap );
  363. }
  364. }
  365. imposterCap->end();
  366. Point2I atlasSize( destBmp.getWidth(mip), destBmp.getHeight(mip) );
  367. // Ok... pack in bitmaps till we run out.
  368. for ( S32 y=0; y+currDim <= atlasSize.y; )
  369. {
  370. for ( S32 x=0; x+currDim <= atlasSize.x; )
  371. {
  372. // Copy the next bitmap to the dest texture.
  373. GBitmap* cell = bitmaps.first();
  374. bitmaps.pop_front();
  375. destBmp.copyRect(cell, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip );
  376. delete cell;
  377. // Copy the next normal to the dest texture.
  378. GBitmap* cellNormalmap = normalmaps.first();
  379. normalmaps.pop_front();
  380. destNormal.copyRect(cellNormalmap, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip );
  381. delete cellNormalmap;
  382. // Did we finish?
  383. if ( bitmaps.empty() )
  384. break;
  385. x += currDim;
  386. }
  387. // Did we finish?
  388. if ( bitmaps.empty() )
  389. break;
  390. y += currDim;
  391. }
  392. // Next mip...
  393. currDim /= 2;
  394. }
  395. PROFILE_END(); // TSLastDetail_snapshots
  396. delete imposterCap;
  397. delete shape;
  398. // Should we dump the images?
  399. if ( Con::getBoolVariable( "$TSLastDetail::dumpImposters", false ) )
  400. {
  401. String imposterPath = _getDiffuseMapPath();
  402. String normalsPath = _getNormalMapPath();
  403. if (!destBmp.writeBitmap("png", imposterPath))
  404. Con::errorf("TSLastDetail::_update() - failed to write imposter %s", imposterPath.c_str());
  405. if (!destNormal.writeBitmap("png", normalsPath))
  406. Con::errorf("TSLastDetail::_update() - failed to write normal %s", normalsPath.c_str());
  407. }
  408. // DEBUG: Some code to force usage of a test image.
  409. //GBitmap* tempMap = GBitmap::load( "./forest/data/test1234.png" );
  410. //tempMap->extrudeMipLevels();
  411. //mTexture.set( tempMap, &GFXStaticTextureSRGBProfile, false );
  412. //delete tempMap;
  413. DDSFile *ddsDest = DDSFile::createDDSFileFromGBitmap( &destBmp );
  414. ImageUtil::ddsCompress( ddsDest, GFXFormatBC2 );
  415. DDSFile *ddsNormals = DDSFile::createDDSFileFromGBitmap( &destNormal );
  416. ImageUtil::ddsCompress( ddsNormals, GFXFormatBC3 );
  417. // Finally save the imposters to disk.
  418. FileStream fs;
  419. if ( fs.open( _getDiffuseMapPath(), Torque::FS::File::Write ) )
  420. {
  421. ddsDest->write( fs );
  422. fs.close();
  423. }
  424. if ( fs.open( _getNormalMapPath(), Torque::FS::File::Write ) )
  425. {
  426. ddsNormals->write( fs );
  427. fs.close();
  428. }
  429. delete ddsDest;
  430. delete ddsNormals;
  431. // If we did a begin then end it now.
  432. if ( !sceneBegun )
  433. GFX->endScene();
  434. }
  435. void TSLastDetail::deleteImposterCacheTextures()
  436. {
  437. const String diffuseMap = _getDiffuseMapPath();
  438. if ( diffuseMap.length() )
  439. dFileDelete( diffuseMap );
  440. const String normalMap = _getNormalMapPath();
  441. if ( normalMap.length() )
  442. dFileDelete( normalMap );
  443. }
  444. void TSLastDetail::updateImposterImages( bool forceUpdate )
  445. {
  446. // Can't do it without GFX!
  447. if ( !GFXDevice::devicePresent() )
  448. return;
  449. //D3DPERF_SetMarker( D3DCOLOR_RGBA( 0, 255, 0, 255 ), L"TSLastDetail::makeImposter" );
  450. bool sceneBegun = GFX->canCurrentlyRender();
  451. if ( !sceneBegun )
  452. GFX->beginScene();
  453. Vector<TSLastDetail*>::iterator iter = smLastDetails.begin();
  454. for ( ; iter != smLastDetails.end(); iter++ )
  455. (*iter)->update( forceUpdate );
  456. if ( !sceneBegun )
  457. GFX->endScene();
  458. }
  459. DefineEngineFunction( tsUpdateImposterImages, void, (bool forceUpdate), (false), "tsUpdateImposterImages( bool forceupdate )")
  460. {
  461. TSLastDetail::updateImposterImages(forceUpdate);
  462. }