decalData.cpp
Engine/source/T3D/decal/decalData.cpp
Public Functions
ConsoleDocClass(DecalData , "@brief A datablock describing an individual <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n\n</a>" "The textures defined by the decal <a href="/coding/class/classmaterial/">Material</a> can be divided into multiple " "rectangular sub-textures as shown below, with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different sub-texture " "selected by all decals using the same <a href="/coding/class/classdecaldata/">DecalData</a>(via #frame) or each decal " "instance(via #randomize).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @image html images/decal_example.png \"Example of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Decal imagemap\"\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classdecaldata/">DecalData</a>(BulletHoleDecal)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " material = \"DECAL_BulletHole\";\n" " <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> = \"5.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifeSpan = \"50000\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " randomize = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " texRows = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " texCols = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " clippingAngle = \"60\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Decals\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Decals\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
DefineEngineMethod(DecalData , postApply , void , () )
GFXImplementVertexFormat(DecalVertex )
Detailed Description
Public Functions
ConsoleDocClass(DecalData , "@brief A datablock describing an individual <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n\n</a>" "The textures defined by the decal <a href="/coding/class/classmaterial/">Material</a> can be divided into multiple " "rectangular sub-textures as shown below, with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different sub-texture " "selected by all decals using the same <a href="/coding/class/classdecaldata/">DecalData</a>(via #frame) or each decal " "instance(via #randomize).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @image html images/decal_example.png \"Example of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Decal imagemap\"\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classdecaldata/">DecalData</a>(BulletHoleDecal)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " material = \"DECAL_BulletHole\";\n" " <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> = \"5.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifeSpan = \"50000\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " randomize = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " texRows = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " texCols = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " clippingAngle = \"60\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Decals\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Decals\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
DefineEngineMethod(DecalData , postApply , void , () )
GFXImplementVertexFormat(DecalVertex )
IMPLEMENT_CO_DATABLOCK_V1(DecalData )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "T3D/decal/decalData.h" 26 27#include "console/consoleTypes.h" 28#include "core/stream/bitStream.h" 29#include "math/mathIO.h" 30#include "materials/materialManager.h" 31#include "materials/baseMatInstance.h" 32#include "T3D/objectTypes.h" 33#include "console/engineAPI.h" 34 35GFXImplementVertexFormat( DecalVertex ) 36{ 37 addElement( "POSITION", GFXDeclType_Float3 ); 38 addElement( "NORMAL", GFXDeclType_Float3 ); 39 addElement( "TANGENT", GFXDeclType_Float3 ); 40 addElement( "COLOR", GFXDeclType_Color ); 41 addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); 42} 43 44IMPLEMENT_CO_DATABLOCK_V1( DecalData ); 45 46ConsoleDocClass( DecalData, 47 "@brief A datablock describing an individual decal.\n\n" 48 49 "The textures defined by the decal Material can be divided into multiple " 50 "rectangular sub-textures as shown below, with a different sub-texture " 51 "selected by all decals using the same DecalData (via #frame) or each decal " 52 "instance (via #randomize).\n" 53 54 "@image html images/decal_example.png \"Example of a Decal imagemap\"\n" 55 56 "@tsexample\n" 57 "datablock DecalData(BulletHoleDecal)\n" 58 "{\n" 59 " material = \"DECAL_BulletHole\";\n" 60 " size = \"5.0\";\n" 61 " lifeSpan = \"50000\";\n" 62 " randomize = \"1\";\n" 63 " texRows = \"2\";\n" 64 " texCols = \"2\";\n" 65 " clippingAngle = \"60\";\n" 66 "};\n" 67 "@endtsexample\n\n" 68 "@see Decals\n" 69 "@ingroup Decals\n" 70 "@ingroup FX\n" 71); 72 73//------------------------------------------------------------------------- 74// DecalData 75//------------------------------------------------------------------------- 76 77DecalData::DecalData() 78{ 79 size = 5; 80 materialName = ""; 81 82 lifeSpan = 5000; 83 fadeTime = 1000; 84 85 frame = 0; 86 randomize = false; 87 texRows = 1; 88 texCols = 1; 89 90 fadeStartPixelSize = -1.0f; 91 fadeEndPixelSize = 200.0f; 92 93 material = NULL; 94 matInst = NULL; 95 96 renderPriority = 10; 97 clippingMasks = STATIC_COLLISION_TYPEMASK; 98 clippingAngle = 89.0f; 99 100 texCoordCount = 1; 101 102 // TODO: We could in theory calculate if we can skip 103 // normals on the decal by checking the material features. 104 skipVertexNormals = false; 105 106 for ( S32 i = 0; i < 16; i++ ) 107 { 108 texRect[i].point.set( 0.0f, 0.0f ); 109 texRect[i].extent.set( 1.0f, 1.0f ); 110 } 111} 112 113DecalData::~DecalData() 114{ 115 SAFE_DELETE( matInst ); 116} 117 118bool DecalData::onAdd() 119{ 120 if ( !Parent::onAdd() ) 121 return false; 122 123 if (size < 0.0) { 124 Con::warnf("DecalData::onAdd: size < 0"); 125 size = 0; 126 } 127 128 getSet()->addObject( this ); 129 130 if( texRows > 1 || texCols > 1 ) 131 reloadRects(); 132 133 return true; 134} 135 136void DecalData::onRemove() 137{ 138 Parent::onRemove(); 139} 140 141void DecalData::initPersistFields() 142{ 143 addGroup( "Decal" ); 144 145 addField( "size", TypeF32, Offset( size, DecalData ), 146 "Width and height of the decal in meters before scale is applied." ); 147 148 addField( "material", TypeMaterialName, Offset( materialName, DecalData ), 149 "Material to use for this decal." ); 150 151 addField( "lifeSpan", TypeS32, Offset( lifeSpan, DecalData ), 152 "Time (in milliseconds) before this decal will be automatically deleted." ); 153 154 addField( "fadeTime", TypeS32, Offset( fadeTime, DecalData ), 155 "@brief Time (in milliseconds) over which to fade out the decal before " 156 "deleting it at the end of its lifetime.\n\n" 157 "@see lifeSpan" ); 158 159 endGroup( "Decal" ); 160 161 addGroup( "Rendering" ); 162 163 addField( "fadeStartPixelSize", TypeF32, Offset( fadeStartPixelSize, DecalData ), 164 "@brief LOD value - size in pixels at which decals of this type begin " 165 "to fade out.\n\n" 166 "This should be a larger value than #fadeEndPixelSize. However, you may " 167 "also set this to a negative value to disable lod-based fading." ); 168 169 addField( "fadeEndPixelSize", TypeF32, Offset( fadeEndPixelSize, DecalData ), 170 "@brief LOD value - size in pixels at which decals of this type are " 171 "fully faded out.\n\n" 172 "This should be a smaller value than #fadeStartPixelSize." ); 173 174 addField( "renderPriority", TypeS8, Offset( renderPriority, DecalData ), 175 "Default renderPriority for decals of this type (determines draw " 176 "order when decals overlap)." ); 177 178 addField( "clippingAngle", TypeF32, Offset( clippingAngle, DecalData ), 179 "The angle in degrees used to clip geometry that faces away from the " 180 "decal projection direction." ); 181 182 endGroup( "Rendering" ); 183 184 addGroup( "Texturing" ); 185 186 addField( "frame", TypeS32, Offset( frame, DecalData ), 187 "Index of the texture rectangle within the imagemap to use for this decal." ); 188 189 addField( "randomize", TypeBool, Offset( randomize, DecalData ), 190 "If true, a random frame from the imagemap is selected for each " 191 "instance of the decal." ); 192 193 addField( "textureCoordCount", TypeS32, Offset( texCoordCount, DecalData ), 194 "Number of individual frames in the imagemap (maximum 16)." ); 195 196 addField( "texRows", TypeS32, Offset( texRows, DecalData ), 197 "@brief Number of rows in the supplied imagemap.\n\n" 198 "Use #texRows and #texCols if the imagemap frames are arranged in a " 199 "grid; use #textureCoords to manually specify UV coordinates for " 200 "irregular sized frames." ); 201 202 addField( "texCols", TypeS32, Offset( texCols, DecalData ), 203 "@brief Number of columns in the supplied imagemap.\n\n" 204 "Use #texRows and #texCols if the imagemap frames are arranged in a " 205 "grid; use #textureCoords to manually specify UV coordinates for " 206 "irregular sized frames." ); 207 208 addField( "textureCoords", TypeRectF, Offset( texRect, DecalData ), MAX_TEXCOORD_COUNT, 209 "@brief An array of RectFs (topleft.x topleft.y extent.x extent.y) " 210 "representing the UV coordinates for each frame in the imagemap.\n\n" 211 "@note This field should only be set if the imagemap frames are " 212 "irregular in size. Otherwise use the #texRows and #texCols fields " 213 "and the UV coordinates will be calculated automatically." ); 214 215 endGroup( "Texturing" ); 216 217 Parent::initPersistFields(); 218} 219 220void DecalData::onStaticModified( const char *slotName, const char *newValue ) 221{ 222 Parent::onStaticModified( slotName, newValue ); 223 224 if ( !isProperlyAdded() ) 225 return; 226 227 // To allow changing materials live. 228 if ( dStricmp( slotName, "material" ) == 0 ) 229 { 230 materialName = newValue; 231 _updateMaterial(); 232 } 233 // To allow changing name live. 234 else if ( dStricmp( slotName, "name" ) == 0 ) 235 { 236 lookupName = getName(); 237 } 238 else if ( dStricmp( slotName, "renderPriority" ) == 0 ) 239 { 240 renderPriority = getMax( renderPriority, (U8)1 ); 241 } 242} 243 244bool DecalData::preload( bool server, String &errorStr ) 245{ 246 if (Parent::preload(server, errorStr) == false) 247 return false; 248 249 // Server assigns name to lookupName, 250 // client assigns lookupName in unpack. 251 if ( server ) 252 lookupName = getName(); 253 254 return true; 255} 256 257void DecalData::packData( BitStream *stream ) 258{ 259 Parent::packData( stream ); 260 261 stream->write( lookupName ); 262 stream->write( size ); 263 stream->write( materialName ); 264 stream->write( lifeSpan ); 265 stream->write( fadeTime ); 266 stream->write( texCoordCount ); 267 268 for (S32 i = 0; i < texCoordCount; i++) 269 mathWrite( *stream, texRect[i] ); 270 271 stream->write( fadeStartPixelSize ); 272 stream->write( fadeEndPixelSize ); 273 stream->write( renderPriority ); 274 stream->write( clippingMasks ); 275 stream->write( clippingAngle ); 276 277 stream->write( texRows ); 278 stream->write( texCols ); 279 stream->write( frame ); 280 stream->write( randomize ); 281} 282 283void DecalData::unpackData( BitStream *stream ) 284{ 285 Parent::unpackData( stream ); 286 287 stream->read( &lookupName ); 288 assignName(lookupName); 289 stream->read( &size ); 290 stream->read( &materialName ); 291 _updateMaterial(); 292 stream->read( &lifeSpan ); 293 stream->read( &fadeTime ); 294 stream->read( &texCoordCount ); 295 296 for (S32 i = 0; i < texCoordCount; i++) 297 mathRead(*stream, &texRect[i]); 298 299 stream->read( &fadeStartPixelSize ); 300 stream->read( &fadeEndPixelSize ); 301 stream->read( &renderPriority ); 302 stream->read( &clippingMasks ); 303 stream->read( &clippingAngle ); 304 305 stream->read( &texRows ); 306 stream->read( &texCols ); 307 stream->read( &frame ); 308 stream->read( &randomize ); 309} 310 311void DecalData::_initMaterial() 312{ 313 SAFE_DELETE( matInst ); 314 315 if ( material ) 316 matInst = material->createMatInstance(); 317 else 318 matInst = MATMGR->createMatInstance( "WarningMaterial" ); 319 320 GFXStateBlockDesc desc; 321 desc.setZReadWrite( true, false ); 322 //desc.zFunc = GFXCmpLess; 323 matInst->addStateBlockDesc( desc ); 324 325 matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() ); 326 if( !matInst->isValid() ) 327 { 328 Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", materialName.c_str() ); 329 SAFE_DELETE( matInst ); 330 matInst = MATMGR->createMatInstance( "WarningMaterial" ); 331 matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() ); 332 } 333} 334 335void DecalData::_updateMaterial() 336{ 337 if ( materialName.isEmpty() ) 338 return; 339 340 Material *pMat = NULL; 341 if ( !Sim::findObject( materialName, pMat ) ) 342 { 343 Con::printf( "DecalData::unpackUpdate, failed to find Material of name %s!", materialName.c_str() ); 344 return; 345 } 346 347 material = pMat; 348 349 // Only update material instance if we have one allocated. 350 if ( matInst ) 351 _initMaterial(); 352} 353 354Material* DecalData::getMaterial() 355{ 356 if ( !material ) 357 { 358 _updateMaterial(); 359 if ( !material ) 360 material = static_cast<Material*>( Sim::findObject("WarningMaterial") ); 361 } 362 363 return material; 364} 365 366BaseMatInstance* DecalData::getMaterialInstance() 367{ 368 if ( !material || !matInst || matInst->getMaterial() != material ) 369 _initMaterial(); 370 371 return matInst; 372} 373 374DecalData* DecalData::findDatablock( String searchName ) 375{ 376 StringTableEntry className = DecalData::getStaticClassRep()->getClassName(); 377 DecalData *pData; 378 SimSet *set = getSet(); 379 SimSetIterator iter( set ); 380 381 for ( ; *iter; ++iter ) 382 { 383 if ( (*iter)->getClassName() != className ) 384 { 385 Con::errorf( "DecalData::findDatablock - found a class %s object in DecalDataSet!", (*iter)->getClassName() ); 386 continue; 387 } 388 389 pData = static_cast<DecalData*>( *iter ); 390 if ( pData->lookupName.equal( searchName, String::NoCase ) ) 391 return pData; 392 } 393 394 return NULL; 395} 396 397void DecalData::inspectPostApply() 398{ 399 reloadRects(); 400} 401 402void DecalData::reloadRects() 403{ 404 F32 rowsBase = 0; 405 F32 colsBase = 0; 406 bool canRenderRowsByFrame = false; 407 bool canRenderColsByFrame = false; 408 S32 id = 0; 409 410 texRect[id].point.x = 0.f; 411 texRect[id].extent.x = 1.f; 412 texRect[id].point.y = 0.f; 413 texRect[id].extent.y = 1.f; 414 415 texCoordCount = (texRows * texCols) - 1; 416 417 if( texCoordCount > 16 ) 418 { 419 Con::warnf("Coordinate max must be lower than 16 to be a valid decal !"); 420 texRows = 1; 421 texCols = 1; 422 texCoordCount = 1; 423 } 424 425 // use current datablock information in order to build a template to extract 426 // coordinates from. 427 if( texRows > 1 ) 428 { 429 rowsBase = ( 1.f / texRows ); 430 canRenderRowsByFrame = true; 431 } 432 if( texCols > 1 ) 433 { 434 colsBase = ( 1.f / texCols ); 435 canRenderColsByFrame = true; 436 } 437 438 // if were able, lets enter the loop 439 if( frame >= 0 && (canRenderRowsByFrame || canRenderColsByFrame) ) 440 { 441 // columns first then rows 442 for ( S32 colId = 1; colId <= texCols; colId++ ) 443 { 444 for ( S32 rowId = 1; rowId <= texRows; rowId++, id++ ) 445 { 446 // if were over the coord count, lets go 447 if(id > texCoordCount) 448 return; 449 450 // keep our dimensions correct 451 if(rowId > texRows) 452 rowId = 1; 453 454 if(colId > texCols) 455 colId = 1; 456 457 // start setting our rect values per frame 458 if( canRenderRowsByFrame ) 459 { 460 texRect[id].point.x = rowsBase * ( rowId - 1 ); 461 texRect[id].extent.x = rowsBase; 462 } 463 464 if( canRenderColsByFrame ) 465 { 466 texRect[id].point.y = colsBase * ( colId - 1 ); 467 texRect[id].extent.y = colsBase; 468 } 469 } 470 } 471 } 472} 473 474DefineEngineMethod(DecalData, postApply, void, (),, 475 "Recompute the imagemap sub-texture rectangles for this DecalData.\n" 476 "@tsexample\n" 477 "// Inform the decal object to reload its imagemap and frame data.\n" 478 "%decalData.texRows = 4;\n" 479 "%decalData.postApply();\n" 480 "@endtsexample\n") 481{ 482 object->inspectPostApply(); 483} 484