decalData.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. renderPriority = 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. addGroup( "Decal" );
  116. addField( "size", TypeF32, Offset( size, DecalData ),
  117. "Width and height of the decal in meters before scale is applied." );
  118. INITPERSISTFIELD_MATERIALASSET(Material, DecalData, "Material to use for this decal.");
  119. addField( "lifeSpan", TypeS32, Offset( lifeSpan, DecalData ),
  120. "Time (in milliseconds) before this decal will be automatically deleted." );
  121. addField( "fadeTime", TypeS32, Offset( fadeTime, DecalData ),
  122. "@brief Time (in milliseconds) over which to fade out the decal before "
  123. "deleting it at the end of its lifetime.\n\n"
  124. "@see lifeSpan" );
  125. endGroup( "Decal" );
  126. addGroup( "Rendering" );
  127. addField( "fadeStartPixelSize", TypeF32, Offset( fadeStartPixelSize, DecalData ),
  128. "@brief LOD value - size in pixels at which decals of this type begin "
  129. "to fade out.\n\n"
  130. "This should be a larger value than #fadeEndPixelSize. However, you may "
  131. "also set this to a negative value to disable lod-based fading." );
  132. addField( "fadeEndPixelSize", TypeF32, Offset( fadeEndPixelSize, DecalData ),
  133. "@brief LOD value - size in pixels at which decals of this type are "
  134. "fully faded out.\n\n"
  135. "This should be a smaller value than #fadeStartPixelSize." );
  136. addField( "renderPriority", TypeS8, Offset( renderPriority, DecalData ),
  137. "Default renderPriority for decals of this type (determines draw "
  138. "order when decals overlap)." );
  139. addField( "clippingAngle", TypeF32, Offset( clippingAngle, DecalData ),
  140. "The angle in degrees used to clip geometry that faces away from the "
  141. "decal projection direction." );
  142. endGroup( "Rendering" );
  143. addGroup( "Texturing" );
  144. addField( "frame", TypeS32, Offset( frame, DecalData ),
  145. "Index of the texture rectangle within the imagemap to use for this decal." );
  146. addField( "randomize", TypeBool, Offset( randomize, DecalData ),
  147. "If true, a random frame from the imagemap is selected for each "
  148. "instance of the decal." );
  149. addField( "textureCoordCount", TypeS32, Offset( texCoordCount, DecalData ),
  150. "Number of individual frames in the imagemap (maximum 16)." );
  151. addField( "texRows", TypeS32, Offset( texRows, DecalData ),
  152. "@brief Number of rows in the supplied imagemap.\n\n"
  153. "Use #texRows and #texCols if the imagemap frames are arranged in a "
  154. "grid; use #textureCoords to manually specify UV coordinates for "
  155. "irregular sized frames." );
  156. addField( "texCols", TypeS32, Offset( texCols, DecalData ),
  157. "@brief Number of columns in the supplied imagemap.\n\n"
  158. "Use #texRows and #texCols if the imagemap frames are arranged in a "
  159. "grid; use #textureCoords to manually specify UV coordinates for "
  160. "irregular sized frames." );
  161. addField( "textureCoords", TypeRectUV, Offset( texRect, DecalData ), MAX_TEXCOORD_COUNT,
  162. "@brief An array of RectFs (topleft.x topleft.y extent.x extent.y) "
  163. "representing the UV coordinates for each frame in the imagemap.\n\n"
  164. "@note This field should only be set if the imagemap frames are "
  165. "irregular in size. Otherwise use the #texRows and #texCols fields "
  166. "and the UV coordinates will be calculated automatically." );
  167. endGroup( "Texturing" );
  168. Parent::initPersistFields();
  169. }
  170. void DecalData::onStaticModified( const char *slotName, const char *newValue )
  171. {
  172. Parent::onStaticModified( slotName, newValue );
  173. if ( !isProperlyAdded() )
  174. return;
  175. // To allow changing materials live.
  176. if ( dStricmp( slotName, "material" ) == 0 )
  177. {
  178. _setMaterial(newValue);
  179. _updateMaterial();
  180. }
  181. // To allow changing name live.
  182. else if ( dStricmp( slotName, "name" ) == 0 )
  183. {
  184. lookupName = getName();
  185. }
  186. else if ( dStricmp( slotName, "renderPriority" ) == 0 )
  187. {
  188. renderPriority = getMax( renderPriority, (U8)1 );
  189. }
  190. }
  191. bool DecalData::preload( bool server, String &errorStr )
  192. {
  193. if (Parent::preload(server, errorStr) == false)
  194. return false;
  195. // Server assigns name to lookupName,
  196. // client assigns lookupName in unpack.
  197. if ( server )
  198. lookupName = getName();
  199. return true;
  200. }
  201. void DecalData::packData( BitStream *stream )
  202. {
  203. Parent::packData( stream );
  204. stream->write( lookupName );
  205. stream->write( size );
  206. PACKDATA_ASSET(Material);
  207. stream->write( lifeSpan );
  208. stream->write( fadeTime );
  209. stream->write( texCoordCount );
  210. for (S32 i = 0; i < texCoordCount; i++)
  211. mathWrite( *stream, texRect[i] );
  212. stream->write( fadeStartPixelSize );
  213. stream->write( fadeEndPixelSize );
  214. stream->write( renderPriority );
  215. stream->write( clippingMasks );
  216. stream->write( clippingAngle );
  217. stream->write( texRows );
  218. stream->write( texCols );
  219. stream->write( frame );
  220. stream->write( randomize );
  221. }
  222. void DecalData::unpackData( BitStream *stream )
  223. {
  224. Parent::unpackData( stream );
  225. stream->read( &lookupName );
  226. assignName(lookupName);
  227. stream->read( &size );
  228. UNPACKDATA_ASSET(Material);
  229. _updateMaterial();
  230. stream->read( &lifeSpan );
  231. stream->read( &fadeTime );
  232. stream->read( &texCoordCount );
  233. for (S32 i = 0; i < texCoordCount; i++)
  234. mathRead(*stream, &texRect[i]);
  235. stream->read( &fadeStartPixelSize );
  236. stream->read( &fadeEndPixelSize );
  237. stream->read( &renderPriority );
  238. stream->read( &clippingMasks );
  239. stream->read( &clippingAngle );
  240. stream->read( &texRows );
  241. stream->read( &texCols );
  242. stream->read( &frame );
  243. stream->read( &randomize );
  244. }
  245. void DecalData::_initMaterial()
  246. {
  247. SAFE_DELETE( matInst );
  248. _setMaterial(getMaterial());
  249. if (mMaterialAsset.notNull() && mMaterialAsset->getStatus() == MaterialAsset::Ok)
  250. {
  251. matInst = getMaterialResource()->createMatInstance();
  252. }
  253. else
  254. matInst = MATMGR->createMatInstance( "WarningMaterial" );
  255. GFXStateBlockDesc desc;
  256. desc.setZReadWrite( true, false );
  257. //desc.zFunc = GFXCmpLess;
  258. matInst->addStateBlockDesc( desc );
  259. matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() );
  260. if( !matInst->isValid() )
  261. {
  262. Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", mMaterialAssetId );
  263. SAFE_DELETE( matInst );
  264. matInst = MATMGR->createMatInstance( "WarningMaterial" );
  265. matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() );
  266. }
  267. }
  268. void DecalData::_updateMaterial()
  269. {
  270. if(mMaterialAsset.isNull())
  271. return;
  272. // Only update material instance if we have one allocated.
  273. if ( matInst )
  274. _initMaterial();
  275. }
  276. Material* DecalData::getMaterialDefinition()
  277. {
  278. if ( !getMaterialResource() )
  279. {
  280. _updateMaterial();
  281. if ( !mMaterial )
  282. mMaterial = static_cast<Material*>( Sim::findObject("WarningMaterial") );
  283. }
  284. return mMaterial;
  285. }
  286. BaseMatInstance* DecalData::getMaterialInstance()
  287. {
  288. if ( !mMaterial || !matInst || matInst->getMaterial() != mMaterial)
  289. _initMaterial();
  290. return matInst;
  291. }
  292. DecalData* DecalData::findDatablock( String searchName )
  293. {
  294. StringTableEntry className = DecalData::getStaticClassRep()->getClassName();
  295. DecalData *pData;
  296. SimSet *set = getSet();
  297. SimSetIterator iter( set );
  298. for ( ; *iter; ++iter )
  299. {
  300. if ( (*iter)->getClassName() != className )
  301. {
  302. Con::errorf( "DecalData::findDatablock - found a class %s object in DecalDataSet!", (*iter)->getClassName() );
  303. continue;
  304. }
  305. pData = static_cast<DecalData*>( *iter );
  306. if ( pData->lookupName.equal( searchName, String::NoCase ) )
  307. return pData;
  308. }
  309. return NULL;
  310. }
  311. void DecalData::inspectPostApply()
  312. {
  313. reloadRects();
  314. }
  315. void DecalData::reloadRects()
  316. {
  317. F32 rowsBase = 0;
  318. F32 colsBase = 0;
  319. bool canRenderRowsByFrame = false;
  320. bool canRenderColsByFrame = false;
  321. S32 id = 0;
  322. texRect[id].point.x = 0.f;
  323. texRect[id].extent.x = 1.f;
  324. texRect[id].point.y = 0.f;
  325. texRect[id].extent.y = 1.f;
  326. texCoordCount = (texRows * texCols) - 1;
  327. if( texCoordCount > 16 )
  328. {
  329. Con::warnf("Coordinate max must be lower than 16 to be a valid decal !");
  330. texRows = 1;
  331. texCols = 1;
  332. texCoordCount = 1;
  333. }
  334. // use current datablock information in order to build a template to extract
  335. // coordinates from.
  336. if( texRows > 1 )
  337. {
  338. rowsBase = ( 1.f / texRows );
  339. canRenderRowsByFrame = true;
  340. }
  341. if( texCols > 1 )
  342. {
  343. colsBase = ( 1.f / texCols );
  344. canRenderColsByFrame = true;
  345. }
  346. // if were able, lets enter the loop
  347. if( frame >= 0 && (canRenderRowsByFrame || canRenderColsByFrame) )
  348. {
  349. // columns first then rows
  350. for ( S32 colId = 1; colId <= texCols; colId++ )
  351. {
  352. for ( S32 rowId = 1; rowId <= texRows; rowId++, id++ )
  353. {
  354. // if were over the coord count, lets go
  355. if(id > texCoordCount)
  356. return;
  357. // keep our dimensions correct
  358. if(rowId > texRows)
  359. rowId = 1;
  360. if(colId > texCols)
  361. colId = 1;
  362. // start setting our rect values per frame
  363. if( canRenderRowsByFrame )
  364. {
  365. texRect[id].point.x = rowsBase * ( rowId - 1 );
  366. texRect[id].extent.x = rowsBase;
  367. }
  368. if( canRenderColsByFrame )
  369. {
  370. texRect[id].point.y = colsBase * ( colId - 1 );
  371. texRect[id].extent.y = colsBase;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. DefineEngineMethod(DecalData, postApply, void, (),,
  378. "Recompute the imagemap sub-texture rectangles for this DecalData.\n"
  379. "@tsexample\n"
  380. "// Inform the decal object to reload its imagemap and frame data.\n"
  381. "%decalData.texRows = 4;\n"
  382. "%decalData.postApply();\n"
  383. "@endtsexample\n")
  384. {
  385. object->inspectPostApply();
  386. }