decalData.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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 "T3D/decal/decalData.h"
  24. #include "console/consoleTypes.h"
  25. #include "core/stream/bitStream.h"
  26. #include "math/mathIO.h"
  27. #include "materials/materialManager.h"
  28. #include "materials/baseMatInstance.h"
  29. #include "T3D/objectTypes.h"
  30. #include "console/engineAPI.h"
  31. GFXImplementVertexFormat( DecalVertex )
  32. {
  33. addElement( "POSITION", GFXDeclType_Float3 );
  34. addElement( "NORMAL", GFXDeclType_Float3 );
  35. addElement( "TANGENT", GFXDeclType_Float3 );
  36. addElement( "COLOR", GFXDeclType_Color );
  37. addElement( "TEXCOORD", GFXDeclType_Float2, 0 );
  38. }
  39. IMPLEMENT_CO_DATABLOCK_V1( DecalData );
  40. ConsoleDocClass( DecalData,
  41. "@brief A datablock describing an individual decal.\n\n"
  42. "The textures defined by the decal Material can be divided into multiple "
  43. "rectangular sub-textures as shown below, with a different sub-texture "
  44. "selected by all decals using the same DecalData (via #frame) or each decal "
  45. "instance (via #randomize).\n"
  46. "@image html images/decal_example.png \"Example of a Decal imagemap\"\n"
  47. "@tsexample\n"
  48. "datablock DecalData(BulletHoleDecal)\n"
  49. "{\n"
  50. " material = \"DECAL_BulletHole\";\n"
  51. " size = \"5.0\";\n"
  52. " lifeSpan = \"50000\";\n"
  53. " randomize = \"1\";\n"
  54. " texRows = \"2\";\n"
  55. " texCols = \"2\";\n"
  56. " clippingAngle = \"60\";\n"
  57. "};\n"
  58. "@endtsexample\n\n"
  59. "@see Decals\n"
  60. "@ingroup Decals\n"
  61. "@ingroup FX\n"
  62. );
  63. //-------------------------------------------------------------------------
  64. // DecalData
  65. //-------------------------------------------------------------------------
  66. DecalData::DecalData()
  67. {
  68. size = 5;
  69. INIT_ASSET(Material);
  70. lifeSpan = 5000;
  71. fadeTime = 1000;
  72. frame = 0;
  73. randomize = false;
  74. texRows = 1;
  75. texCols = 1;
  76. fadeStartPixelSize = -1.0f;
  77. fadeEndPixelSize = 200.0f;
  78. matInst = NULL;
  79. mRenderPriority = 10;
  80. clippingMasks = STATIC_COLLISION_TYPEMASK;
  81. clippingAngle = 89.0f;
  82. texCoordCount = 1;
  83. // TODO: We could in theory calculate if we can skip
  84. // normals on the decal by checking the material features.
  85. skipVertexNormals = false;
  86. for ( S32 i = 0; i < 16; i++ )
  87. {
  88. texRect[i].point.set( 0.0f, 0.0f );
  89. texRect[i].extent.set( 1.0f, 1.0f );
  90. }
  91. }
  92. DecalData::~DecalData()
  93. {
  94. SAFE_DELETE( matInst );
  95. }
  96. bool DecalData::onAdd()
  97. {
  98. if ( !Parent::onAdd() )
  99. return false;
  100. if (size < 0.0) {
  101. Con::warnf("DecalData::onAdd: size < 0");
  102. size = 0;
  103. }
  104. getSet()->addObject( this );
  105. if( texRows > 1 || texCols > 1 )
  106. reloadRects();
  107. return true;
  108. }
  109. void DecalData::onRemove()
  110. {
  111. Parent::onRemove();
  112. }
  113. void DecalData::initPersistFields()
  114. {
  115. docsURL;
  116. addGroup( "Decal" );
  117. addFieldV( "size", TypeRangedF32, Offset( size, DecalData ), &CommonValidators::PositiveFloat,
  118. "Width and height of the decal in meters before scale is applied." );
  119. INITPERSISTFIELD_MATERIALASSET(Material, DecalData, "Material to use for this decal.");
  120. addFieldV( "lifeSpan", TypeRangedS32, Offset( lifeSpan, DecalData ), &CommonValidators::PositiveInt,
  121. "Time (in milliseconds) before this decal will be automatically deleted." );
  122. addFieldV( "fadeTime", TypeRangedS32, Offset( fadeTime, DecalData ), &CommonValidators::PositiveInt,
  123. "@brief Time (in milliseconds) over which to fade out the decal before "
  124. "deleting it at the end of its lifetime.\n\n"
  125. "@see lifeSpan" );
  126. endGroup( "Decal" );
  127. addGroup( "Rendering" );
  128. addFieldV( "fadeStartPixelSize", TypeRangedF32, Offset( fadeStartPixelSize, DecalData ), &CommonValidators::NegDefaultF32,
  129. "@brief LOD value - size in pixels at which decals of this type begin "
  130. "to fade out.\n\n"
  131. "This should be a larger value than #fadeEndPixelSize. However, you may "
  132. "also set this to a negative value to disable lod-based fading." );
  133. addFieldV( "fadeEndPixelSize", TypeRangedF32, Offset( fadeEndPixelSize, DecalData ), &CommonValidators::PositiveFloat,
  134. "@brief LOD value - size in pixels at which decals of this type are "
  135. "fully faded out.\n\n"
  136. "This should be a smaller value than #fadeStartPixelSize." );
  137. addField( "renderPriority", TypeS16, Offset( mRenderPriority, DecalData ),
  138. "Default renderPriority for decals of this type (determines draw "
  139. "order when decals overlap)." );
  140. addFieldV( "clippingAngle", TypeRangedF32, Offset( clippingAngle, DecalData ), &CommonValidators::PosDegreeRangeQuarter,
  141. "The angle in degrees used to clip geometry that faces away from the "
  142. "decal projection direction." );
  143. endGroup( "Rendering" );
  144. addGroup( "Texturing" );
  145. addFieldV( "frame", TypeRangedS32, Offset( frame, DecalData ), &CommonValidators::PositiveInt,
  146. "Index of the texture rectangle within the imagemap to use for this decal." );
  147. addField( "randomize", TypeBool, Offset( randomize, DecalData ),
  148. "If true, a random frame from the imagemap is selected for each "
  149. "instance of the decal." );
  150. addFieldV( "textureCoordCount", TypeRangedS32, Offset( texCoordCount, DecalData ), &CommonValidators::PositiveInt,
  151. "Number of individual frames in the imagemap (maximum 16)." );
  152. addFieldV( "texRows", TypeRangedS32, Offset( texRows, DecalData ), &CommonValidators::PositiveInt,
  153. "@brief Number of rows in the supplied imagemap.\n\n"
  154. "Use #texRows and #texCols if the imagemap frames are arranged in a "
  155. "grid; use #textureCoords to manually specify UV coordinates for "
  156. "irregular sized frames." );
  157. addFieldV( "texCols", TypeRangedS32, Offset( texCols, DecalData ), &CommonValidators::PositiveInt,
  158. "@brief Number of columns in the supplied imagemap.\n\n"
  159. "Use #texRows and #texCols if the imagemap frames are arranged in a "
  160. "grid; use #textureCoords to manually specify UV coordinates for "
  161. "irregular sized frames." );
  162. addField( "textureCoords", TypeRectUV, Offset( texRect, DecalData ), MAX_TEXCOORD_COUNT,
  163. "@brief An array of RectFs (topleft.x topleft.y extent.x extent.y) "
  164. "representing the UV coordinates for each frame in the imagemap.\n\n"
  165. "@note This field should only be set if the imagemap frames are "
  166. "irregular in size. Otherwise use the #texRows and #texCols fields "
  167. "and the UV coordinates will be calculated automatically." );
  168. endGroup( "Texturing" );
  169. Parent::initPersistFields();
  170. }
  171. void DecalData::onStaticModified( const char *slotName, const char *newValue )
  172. {
  173. Parent::onStaticModified( slotName, newValue );
  174. if ( !isProperlyAdded() )
  175. return;
  176. // To allow changing materials live.
  177. if ( dStricmp( slotName, "material" ) == 0 )
  178. {
  179. _setMaterial(newValue);
  180. _updateMaterial();
  181. }
  182. // To allow changing name live.
  183. else if ( dStricmp( slotName, "name" ) == 0 )
  184. {
  185. lookupName = getName();
  186. }
  187. else if ( dStricmp( slotName, "renderPriority" ) == 0 )
  188. {
  189. mRenderPriority = getMax(mRenderPriority, (S16)1 );
  190. }
  191. }
  192. bool DecalData::preload( bool server, String &errorStr )
  193. {
  194. if (Parent::preload(server, errorStr) == false)
  195. return false;
  196. // Server assigns name to lookupName,
  197. // client assigns lookupName in unpack.
  198. if ( server )
  199. lookupName = getName();
  200. return true;
  201. }
  202. void DecalData::packData( BitStream *stream )
  203. {
  204. Parent::packData( stream );
  205. stream->write( lookupName );
  206. stream->write( size );
  207. PACKDATA_ASSET(Material);
  208. stream->write( lifeSpan );
  209. stream->write( fadeTime );
  210. stream->write( texCoordCount );
  211. for (S32 i = 0; i < texCoordCount; i++)
  212. mathWrite( *stream, texRect[i] );
  213. stream->write( fadeStartPixelSize );
  214. stream->write( fadeEndPixelSize );
  215. stream->write( mRenderPriority );
  216. stream->write( clippingMasks );
  217. stream->write( clippingAngle );
  218. stream->write( texRows );
  219. stream->write( texCols );
  220. stream->write( frame );
  221. stream->write( randomize );
  222. }
  223. void DecalData::unpackData( BitStream *stream )
  224. {
  225. Parent::unpackData( stream );
  226. stream->read( &lookupName );
  227. assignName(lookupName);
  228. stream->read( &size );
  229. UNPACKDATA_ASSET(Material);
  230. _updateMaterial();
  231. stream->read( &lifeSpan );
  232. stream->read( &fadeTime );
  233. stream->read( &texCoordCount );
  234. for (S32 i = 0; i < texCoordCount; i++)
  235. mathRead(*stream, &texRect[i]);
  236. stream->read( &fadeStartPixelSize );
  237. stream->read( &fadeEndPixelSize );
  238. stream->read( &mRenderPriority);
  239. stream->read( &clippingMasks );
  240. stream->read( &clippingAngle );
  241. stream->read( &texRows );
  242. stream->read( &texCols );
  243. stream->read( &frame );
  244. stream->read( &randomize );
  245. }
  246. void DecalData::_initMaterial()
  247. {
  248. SAFE_DELETE( matInst );
  249. _setMaterial(getMaterial());
  250. if (mMaterialAsset.notNull() && mMaterialAsset->getStatus() == MaterialAsset::Ok)
  251. {
  252. matInst = getMaterialResource()->createMatInstance();
  253. }
  254. else
  255. matInst = MATMGR->createMatInstance( "WarningMaterial" );
  256. GFXStateBlockDesc desc;
  257. desc.setZReadWrite( true, false );
  258. //desc.zFunc = GFXCmpLess;
  259. matInst->addStateBlockDesc( desc );
  260. matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() );
  261. if( !matInst->isValid() )
  262. {
  263. Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", mMaterialAssetId );
  264. SAFE_DELETE( matInst );
  265. matInst = MATMGR->createMatInstance( "WarningMaterial" );
  266. matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() );
  267. }
  268. }
  269. void DecalData::_updateMaterial()
  270. {
  271. U32 assetStatus = MaterialAsset::getAssetErrCode(mMaterialAsset);
  272. if (assetStatus != AssetBase::Ok && assetStatus != AssetBase::UsingFallback)
  273. {
  274. return;
  275. }
  276. // Only update material instance if we have one allocated.
  277. if ( matInst )
  278. _initMaterial();
  279. }
  280. Material* DecalData::getMaterialDefinition()
  281. {
  282. if ( !getMaterialResource() )
  283. {
  284. _updateMaterial();
  285. if ( !mMaterial )
  286. mMaterial = static_cast<Material*>( Sim::findObject("WarningMaterial") );
  287. }
  288. return mMaterial;
  289. }
  290. BaseMatInstance* DecalData::getMaterialInstance()
  291. {
  292. if ( !mMaterial || !matInst || matInst->getMaterial() != mMaterial)
  293. _initMaterial();
  294. return matInst;
  295. }
  296. DecalData* DecalData::findDatablock( String searchName )
  297. {
  298. StringTableEntry className = DecalData::getStaticClassRep()->getClassName();
  299. DecalData *pData;
  300. SimSet *set = getSet();
  301. SimSetIterator iter( set );
  302. for ( ; *iter; ++iter )
  303. {
  304. if ( (*iter)->getClassName() != className )
  305. {
  306. Con::errorf( "DecalData::findDatablock - found a class %s object in DecalDataSet!", (*iter)->getClassName() );
  307. continue;
  308. }
  309. pData = static_cast<DecalData*>( *iter );
  310. if ( pData->lookupName.equal( searchName, String::NoCase ) )
  311. return pData;
  312. }
  313. return NULL;
  314. }
  315. void DecalData::inspectPostApply()
  316. {
  317. reloadRects();
  318. }
  319. void DecalData::reloadRects()
  320. {
  321. F32 rowsBase = 0;
  322. F32 colsBase = 0;
  323. bool canRenderRowsByFrame = false;
  324. bool canRenderColsByFrame = false;
  325. S32 id = 0;
  326. if (texRows > 1 || texCols > 1)
  327. {
  328. texCoordCount = (texRows * texCols) - 1;
  329. texRect[id].point.x = 0.f;
  330. texRect[id].extent.x = 1.f;
  331. texRect[id].point.y = 0.f;
  332. texRect[id].extent.y = 1.f;
  333. }
  334. if( texCoordCount > 16 )
  335. {
  336. Con::warnf("Coordinate max must be lower than 16 to be a valid decal !");
  337. texRows = 1;
  338. texCols = 1;
  339. texCoordCount = 1;
  340. }
  341. // use current datablock information in order to build a template to extract
  342. // coordinates from.
  343. if( texRows > 1 )
  344. {
  345. rowsBase = ( 1.f / texRows );
  346. canRenderRowsByFrame = true;
  347. }
  348. if( texCols > 1 )
  349. {
  350. colsBase = ( 1.f / texCols );
  351. canRenderColsByFrame = true;
  352. }
  353. // if were able, lets enter the loop
  354. if( frame >= 0 && (canRenderRowsByFrame || canRenderColsByFrame) )
  355. {
  356. // columns first then rows
  357. for ( S32 colId = 1; colId <= texCols; colId++ )
  358. {
  359. for ( S32 rowId = 1; rowId <= texRows; rowId++, id++ )
  360. {
  361. // if were over the coord count, lets go
  362. if(id > texCoordCount)
  363. return;
  364. // keep our dimensions correct
  365. if(rowId > texRows)
  366. rowId = 1;
  367. if(colId > texCols)
  368. colId = 1;
  369. // start setting our rect values per frame
  370. if( canRenderRowsByFrame )
  371. {
  372. texRect[id].point.x = rowsBase * ( rowId - 1 );
  373. texRect[id].extent.x = rowsBase;
  374. }
  375. if( canRenderColsByFrame )
  376. {
  377. texRect[id].point.y = colsBase * ( colId - 1 );
  378. texRect[id].extent.y = colsBase;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. DefineEngineMethod(DecalData, postApply, void, (),,
  385. "Recompute the imagemap sub-texture rectangles for this DecalData.\n"
  386. "@tsexample\n"
  387. "// Inform the decal object to reload its imagemap and frame data.\n"
  388. "%decalData.texRows = 4;\n"
  389. "%decalData.postApply();\n"
  390. "@endtsexample\n")
  391. {
  392. object->inspectPostApply();
  393. }