mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-07 12:44:33 +00:00
- Updates the various classes using materials to comply to the change. - Also standardizes some getter names to follow the general convention image and shape used to keep things more consistent across the board to minimize usage friction. - Shifts handling of fallback asset lookups for the thus-far converted classes to have them be loaded during core initialization rather than being loaded as part of other asset loading as that sometimes lead to nesting execution/stability errors. Explicit call now ensures the fallbacks are loaded before any other asset tries to load, so there's no room for confounding or having no fallback to work with.
482 lines
14 KiB
C++
482 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;
|
|
|
|
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." );
|
|
|
|
ADD_FIELD( "materialAsset", TypeMaterialAssetRef, Offset( mMaterialAssetRef, DecalData ) ).doc( "Material asset 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, "materialAsset" ) == 0 )
|
|
{
|
|
mMaterialAssetRef = StringTable->insert( 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 );
|
|
|
|
AssetDatabase.packDataAsset( stream, mMaterialAssetRef.getAssetId() );
|
|
|
|
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 );
|
|
|
|
mMaterialAssetRef = AssetDatabase.unpackDataAsset( stream );
|
|
|
|
_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 );
|
|
|
|
if ( mMaterialAssetRef.notNull() && mMaterialAssetRef.assetPtr->getStatus() == MaterialAsset::Ok )
|
|
{
|
|
matInst = mMaterialAssetRef.assetPtr->getMaterial()->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'", mMaterialAssetRef.getAssetId() );
|
|
SAFE_DELETE( matInst );
|
|
matInst = MATMGR->createMatInstance( "WarningMaterial" );
|
|
matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() );
|
|
}
|
|
}
|
|
|
|
void DecalData::_updateMaterial()
|
|
{
|
|
U32 assetStatus = MaterialAsset::getAssetErrCode( mMaterialAssetRef.assetPtr );
|
|
if ( assetStatus != AssetBase::Ok && assetStatus != AssetBase::UsingFallback )
|
|
return;
|
|
|
|
// Only update material instance if we have one allocated.
|
|
if ( matInst )
|
|
_initMaterial();
|
|
}
|
|
|
|
Material* DecalData::getMaterial()
|
|
{
|
|
if ( !mMaterialAssetRef.notNull() )
|
|
{
|
|
_updateMaterial();
|
|
if ( !mMaterialAssetRef.notNull() )
|
|
return static_cast<Material*>( Sim::findObject( "WarningMaterial" ) );
|
|
}
|
|
|
|
return mMaterialAssetRef.assetPtr->getMaterial();
|
|
}
|
|
|
|
BaseMatInstance* DecalData::getMaterialInstance()
|
|
{
|
|
Material* matDef = mMaterialAssetRef.notNull() ? mMaterialAssetRef.assetPtr->getMaterial() : nullptr;
|
|
if ( !matDef || !matInst || matInst->getMaterial() != matDef )
|
|
_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;
|
|
|
|
|
|
if (texRows > 1 || texCols > 1)
|
|
{
|
|
texCoordCount = (texRows * texCols) - 1;
|
|
texRect[id].point.x = 0.f;
|
|
texRect[id].extent.x = 1.f;
|
|
texRect[id].point.y = 0.f;
|
|
texRect[id].extent.y = 1.f;
|
|
}
|
|
|
|
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();
|
|
}
|