decalData.cpp

Engine/source/T3D/decal/decalData.cpp

More...

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 , () )

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