tsLastDetail.cpp 17 KB

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