tsLastDetail.cpp 17 KB

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