decalData.cpp 14 KB

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