mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
483 lines
14 KiB
C++
483 lines
14 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platform/platform.h"
|
|
#include "T3D/decal/decalData.h"
|
|
|
|
#include "console/consoleTypes.h"
|
|
#include "core/stream/bitStream.h"
|
|
#include "math/mathIO.h"
|
|
#include "materials/materialManager.h"
|
|
#include "materials/baseMatInstance.h"
|
|
#include "T3D/objectTypes.h"
|
|
#include "console/engineAPI.h"
|
|
|
|
GFXImplementVertexFormat( DecalVertex )
|
|
{
|
|
addElement( "POSITION", GFXDeclType_Float3 );
|
|
addElement( "NORMAL", GFXDeclType_Float3 );
|
|
addElement( "TANGENT", GFXDeclType_Float3 );
|
|
addElement( "COLOR", GFXDeclType_Color );
|
|
addElement( "TEXCOORD", GFXDeclType_Float2, 0 );
|
|
}
|
|
|
|
IMPLEMENT_CO_DATABLOCK_V1( DecalData );
|
|
|
|
ConsoleDocClass( DecalData,
|
|
"@brief A datablock describing an individual decal.\n\n"
|
|
|
|
"The textures defined by the decal Material can be divided into multiple "
|
|
"rectangular sub-textures as shown below, with a different sub-texture "
|
|
"selected by all decals using the same DecalData (via #frame) or each decal "
|
|
"instance (via #randomize).\n"
|
|
|
|
"@image html images/decal_example.png \"Example of a Decal imagemap\"\n"
|
|
|
|
"@tsexample\n"
|
|
"datablock DecalData(BulletHoleDecal)\n"
|
|
"{\n"
|
|
" material = \"DECAL_BulletHole\";\n"
|
|
" size = \"5.0\";\n"
|
|
" lifeSpan = \"50000\";\n"
|
|
" randomize = \"1\";\n"
|
|
" texRows = \"2\";\n"
|
|
" texCols = \"2\";\n"
|
|
" clippingAngle = \"60\";\n"
|
|
"};\n"
|
|
"@endtsexample\n\n"
|
|
"@see Decals\n"
|
|
"@ingroup Decals\n"
|
|
"@ingroup FX\n"
|
|
);
|
|
|
|
//-------------------------------------------------------------------------
|
|
// DecalData
|
|
//-------------------------------------------------------------------------
|
|
|
|
DecalData::DecalData()
|
|
{
|
|
size = 5;
|
|
INIT_ASSET(Material);
|
|
|
|
lifeSpan = 5000;
|
|
fadeTime = 1000;
|
|
|
|
frame = 0;
|
|
randomize = false;
|
|
texRows = 1;
|
|
texCols = 1;
|
|
|
|
fadeStartPixelSize = -1.0f;
|
|
fadeEndPixelSize = 200.0f;
|
|
|
|
matInst = NULL;
|
|
|
|
mRenderPriority = 10;
|
|
clippingMasks = STATIC_COLLISION_TYPEMASK;
|
|
clippingAngle = 89.0f;
|
|
|
|
texCoordCount = 1;
|
|
|
|
// TODO: We could in theory calculate if we can skip
|
|
// normals on the decal by checking the material features.
|
|
skipVertexNormals = false;
|
|
|
|
for ( S32 i = 0; i < 16; i++ )
|
|
{
|
|
texRect[i].point.set( 0.0f, 0.0f );
|
|
texRect[i].extent.set( 1.0f, 1.0f );
|
|
}
|
|
}
|
|
|
|
DecalData::~DecalData()
|
|
{
|
|
SAFE_DELETE( matInst );
|
|
}
|
|
|
|
bool DecalData::onAdd()
|
|
{
|
|
if ( !Parent::onAdd() )
|
|
return false;
|
|
|
|
if (size < 0.0) {
|
|
Con::warnf("DecalData::onAdd: size < 0");
|
|
size = 0;
|
|
}
|
|
|
|
getSet()->addObject( this );
|
|
|
|
if( texRows > 1 || texCols > 1 )
|
|
reloadRects();
|
|
|
|
return true;
|
|
}
|
|
|
|
void DecalData::onRemove()
|
|
{
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void DecalData::initPersistFields()
|
|
{
|
|
docsURL;
|
|
addGroup( "Decal" );
|
|
|
|
addFieldV( "size", TypeRangedF32, Offset( size, DecalData ), &CommonValidators::PositiveFloat,
|
|
"Width and height of the decal in meters before scale is applied." );
|
|
|
|
INITPERSISTFIELD_MATERIALASSET(Material, DecalData, "Material to use for this decal.");
|
|
|
|
addFieldV( "lifeSpan", TypeRangedS32, Offset( lifeSpan, DecalData ), &CommonValidators::PositiveInt,
|
|
"Time (in milliseconds) before this decal will be automatically deleted." );
|
|
|
|
addFieldV( "fadeTime", TypeRangedS32, Offset( fadeTime, DecalData ), &CommonValidators::PositiveInt,
|
|
"@brief Time (in milliseconds) over which to fade out the decal before "
|
|
"deleting it at the end of its lifetime.\n\n"
|
|
"@see lifeSpan" );
|
|
|
|
endGroup( "Decal" );
|
|
|
|
addGroup( "Rendering" );
|
|
|
|
addFieldV( "fadeStartPixelSize", TypeRangedF32, Offset( fadeStartPixelSize, DecalData ), &CommonValidators::NegDefaultF32,
|
|
"@brief LOD value - size in pixels at which decals of this type begin "
|
|
"to fade out.\n\n"
|
|
"This should be a larger value than #fadeEndPixelSize. However, you may "
|
|
"also set this to a negative value to disable lod-based fading." );
|
|
|
|
addFieldV( "fadeEndPixelSize", TypeRangedF32, Offset( fadeEndPixelSize, DecalData ), &CommonValidators::PositiveFloat,
|
|
"@brief LOD value - size in pixels at which decals of this type are "
|
|
"fully faded out.\n\n"
|
|
"This should be a smaller value than #fadeStartPixelSize." );
|
|
|
|
addField( "renderPriority", TypeS16, Offset( mRenderPriority, DecalData ),
|
|
"Default renderPriority for decals of this type (determines draw "
|
|
"order when decals overlap)." );
|
|
|
|
addFieldV( "clippingAngle", TypeRangedF32, Offset( clippingAngle, DecalData ), &CommonValidators::PosDegreeRangeQuarter,
|
|
"The angle in degrees used to clip geometry that faces away from the "
|
|
"decal projection direction." );
|
|
|
|
endGroup( "Rendering" );
|
|
|
|
addGroup( "Texturing" );
|
|
|
|
addFieldV( "frame", TypeRangedS32, Offset( frame, DecalData ), &CommonValidators::PositiveInt,
|
|
"Index of the texture rectangle within the imagemap to use for this decal." );
|
|
|
|
addField( "randomize", TypeBool, Offset( randomize, DecalData ),
|
|
"If true, a random frame from the imagemap is selected for each "
|
|
"instance of the decal." );
|
|
|
|
addFieldV( "textureCoordCount", TypeRangedS32, Offset( texCoordCount, DecalData ), &CommonValidators::PositiveInt,
|
|
"Number of individual frames in the imagemap (maximum 16)." );
|
|
|
|
addFieldV( "texRows", TypeRangedS32, Offset( texRows, DecalData ), &CommonValidators::PositiveInt,
|
|
"@brief Number of rows in the supplied imagemap.\n\n"
|
|
"Use #texRows and #texCols if the imagemap frames are arranged in a "
|
|
"grid; use #textureCoords to manually specify UV coordinates for "
|
|
"irregular sized frames." );
|
|
|
|
addFieldV( "texCols", TypeRangedS32, Offset( texCols, DecalData ), &CommonValidators::PositiveInt,
|
|
"@brief Number of columns in the supplied imagemap.\n\n"
|
|
"Use #texRows and #texCols if the imagemap frames are arranged in a "
|
|
"grid; use #textureCoords to manually specify UV coordinates for "
|
|
"irregular sized frames." );
|
|
|
|
addField( "textureCoords", TypeRectUV, Offset( texRect, DecalData ), MAX_TEXCOORD_COUNT,
|
|
"@brief An array of RectFs (topleft.x topleft.y extent.x extent.y) "
|
|
"representing the UV coordinates for each frame in the imagemap.\n\n"
|
|
"@note This field should only be set if the imagemap frames are "
|
|
"irregular in size. Otherwise use the #texRows and #texCols fields "
|
|
"and the UV coordinates will be calculated automatically." );
|
|
|
|
endGroup( "Texturing" );
|
|
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
void DecalData::onStaticModified( const char *slotName, const char *newValue )
|
|
{
|
|
Parent::onStaticModified( slotName, newValue );
|
|
|
|
if ( !isProperlyAdded() )
|
|
return;
|
|
|
|
// To allow changing materials live.
|
|
if ( dStricmp( slotName, "material" ) == 0 )
|
|
{
|
|
_setMaterial(newValue);
|
|
_updateMaterial();
|
|
}
|
|
// To allow changing name live.
|
|
else if ( dStricmp( slotName, "name" ) == 0 )
|
|
{
|
|
lookupName = getName();
|
|
}
|
|
else if ( dStricmp( slotName, "renderPriority" ) == 0 )
|
|
{
|
|
mRenderPriority = getMax(mRenderPriority, (S16)1 );
|
|
}
|
|
}
|
|
|
|
bool DecalData::preload( bool server, String &errorStr )
|
|
{
|
|
if (Parent::preload(server, errorStr) == false)
|
|
return false;
|
|
|
|
// Server assigns name to lookupName,
|
|
// client assigns lookupName in unpack.
|
|
if ( server )
|
|
lookupName = getName();
|
|
|
|
return true;
|
|
}
|
|
|
|
void DecalData::packData( BitStream *stream )
|
|
{
|
|
Parent::packData( stream );
|
|
|
|
stream->write( lookupName );
|
|
stream->write( size );
|
|
|
|
PACKDATA_ASSET(Material);
|
|
|
|
stream->write( lifeSpan );
|
|
stream->write( fadeTime );
|
|
stream->write( texCoordCount );
|
|
|
|
for (S32 i = 0; i < texCoordCount; i++)
|
|
mathWrite( *stream, texRect[i] );
|
|
|
|
stream->write( fadeStartPixelSize );
|
|
stream->write( fadeEndPixelSize );
|
|
stream->write( mRenderPriority );
|
|
stream->write( clippingMasks );
|
|
stream->write( clippingAngle );
|
|
|
|
stream->write( texRows );
|
|
stream->write( texCols );
|
|
stream->write( frame );
|
|
stream->write( randomize );
|
|
}
|
|
|
|
void DecalData::unpackData( BitStream *stream )
|
|
{
|
|
Parent::unpackData( stream );
|
|
|
|
stream->read( &lookupName );
|
|
assignName(lookupName);
|
|
stream->read( &size );
|
|
|
|
UNPACKDATA_ASSET(Material);
|
|
|
|
_updateMaterial();
|
|
stream->read( &lifeSpan );
|
|
stream->read( &fadeTime );
|
|
stream->read( &texCoordCount );
|
|
|
|
for (S32 i = 0; i < texCoordCount; i++)
|
|
mathRead(*stream, &texRect[i]);
|
|
|
|
stream->read( &fadeStartPixelSize );
|
|
stream->read( &fadeEndPixelSize );
|
|
stream->read( &mRenderPriority);
|
|
stream->read( &clippingMasks );
|
|
stream->read( &clippingAngle );
|
|
|
|
stream->read( &texRows );
|
|
stream->read( &texCols );
|
|
stream->read( &frame );
|
|
stream->read( &randomize );
|
|
}
|
|
|
|
void DecalData::_initMaterial()
|
|
{
|
|
SAFE_DELETE( matInst );
|
|
|
|
_setMaterial(getMaterial());
|
|
|
|
if (mMaterialAsset.notNull() && mMaterialAsset->getStatus() == MaterialAsset::Ok)
|
|
{
|
|
matInst = getMaterialResource()->createMatInstance();
|
|
}
|
|
else
|
|
matInst = MATMGR->createMatInstance( "WarningMaterial" );
|
|
|
|
GFXStateBlockDesc desc;
|
|
desc.setZReadWrite( true, false );
|
|
//desc.zFunc = GFXCmpLess;
|
|
matInst->addStateBlockDesc( desc );
|
|
|
|
matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() );
|
|
if( !matInst->isValid() )
|
|
{
|
|
Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", mMaterialAssetId );
|
|
SAFE_DELETE( matInst );
|
|
matInst = MATMGR->createMatInstance( "WarningMaterial" );
|
|
matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() );
|
|
}
|
|
}
|
|
|
|
void DecalData::_updateMaterial()
|
|
{
|
|
U32 assetStatus = MaterialAsset::getAssetErrCode(mMaterialAsset);
|
|
if (assetStatus != AssetBase::Ok && assetStatus != AssetBase::UsingFallback)
|
|
{
|
|
return;
|
|
}
|
|
// Only update material instance if we have one allocated.
|
|
if ( matInst )
|
|
_initMaterial();
|
|
}
|
|
|
|
Material* DecalData::getMaterialDefinition()
|
|
{
|
|
if ( !getMaterialResource() )
|
|
{
|
|
_updateMaterial();
|
|
if ( !mMaterial )
|
|
mMaterial = static_cast<Material*>( Sim::findObject("WarningMaterial") );
|
|
}
|
|
|
|
return mMaterial;
|
|
}
|
|
|
|
BaseMatInstance* DecalData::getMaterialInstance()
|
|
{
|
|
if ( !mMaterial || !matInst || matInst->getMaterial() != mMaterial)
|
|
_initMaterial();
|
|
|
|
return matInst;
|
|
}
|
|
|
|
DecalData* DecalData::findDatablock( String searchName )
|
|
{
|
|
StringTableEntry className = DecalData::getStaticClassRep()->getClassName();
|
|
DecalData *pData;
|
|
SimSet *set = getSet();
|
|
SimSetIterator iter( set );
|
|
|
|
for ( ; *iter; ++iter )
|
|
{
|
|
if ( (*iter)->getClassName() != className )
|
|
{
|
|
Con::errorf( "DecalData::findDatablock - found a class %s object in DecalDataSet!", (*iter)->getClassName() );
|
|
continue;
|
|
}
|
|
|
|
pData = static_cast<DecalData*>( *iter );
|
|
if ( pData->lookupName.equal( searchName, String::NoCase ) )
|
|
return pData;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void DecalData::inspectPostApply()
|
|
{
|
|
reloadRects();
|
|
}
|
|
|
|
void DecalData::reloadRects()
|
|
{
|
|
F32 rowsBase = 0;
|
|
F32 colsBase = 0;
|
|
bool canRenderRowsByFrame = false;
|
|
bool canRenderColsByFrame = false;
|
|
S32 id = 0;
|
|
|
|
texRect[id].point.x = 0.f;
|
|
texRect[id].extent.x = 1.f;
|
|
texRect[id].point.y = 0.f;
|
|
texRect[id].extent.y = 1.f;
|
|
|
|
texCoordCount = (texRows * texCols) - 1;
|
|
|
|
if( texCoordCount > 16 )
|
|
{
|
|
Con::warnf("Coordinate max must be lower than 16 to be a valid decal !");
|
|
texRows = 1;
|
|
texCols = 1;
|
|
texCoordCount = 1;
|
|
}
|
|
|
|
// use current datablock information in order to build a template to extract
|
|
// coordinates from.
|
|
if( texRows > 1 )
|
|
{
|
|
rowsBase = ( 1.f / texRows );
|
|
canRenderRowsByFrame = true;
|
|
}
|
|
if( texCols > 1 )
|
|
{
|
|
colsBase = ( 1.f / texCols );
|
|
canRenderColsByFrame = true;
|
|
}
|
|
|
|
// if were able, lets enter the loop
|
|
if( frame >= 0 && (canRenderRowsByFrame || canRenderColsByFrame) )
|
|
{
|
|
// columns first then rows
|
|
for ( S32 colId = 1; colId <= texCols; colId++ )
|
|
{
|
|
for ( S32 rowId = 1; rowId <= texRows; rowId++, id++ )
|
|
{
|
|
// if were over the coord count, lets go
|
|
if(id > texCoordCount)
|
|
return;
|
|
|
|
// keep our dimensions correct
|
|
if(rowId > texRows)
|
|
rowId = 1;
|
|
|
|
if(colId > texCols)
|
|
colId = 1;
|
|
|
|
// start setting our rect values per frame
|
|
if( canRenderRowsByFrame )
|
|
{
|
|
texRect[id].point.x = rowsBase * ( rowId - 1 );
|
|
texRect[id].extent.x = rowsBase;
|
|
}
|
|
|
|
if( canRenderColsByFrame )
|
|
{
|
|
texRect[id].point.y = colsBase * ( colId - 1 );
|
|
texRect[id].extent.y = colsBase;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DefineEngineMethod(DecalData, postApply, void, (),,
|
|
"Recompute the imagemap sub-texture rectangles for this DecalData.\n"
|
|
"@tsexample\n"
|
|
"// Inform the decal object to reload its imagemap and frame data.\n"
|
|
"%decalData.texRows = 4;\n"
|
|
"%decalData.postApply();\n"
|
|
"@endtsexample\n")
|
|
{
|
|
object->inspectPostApply();
|
|
}
|