decalManager.cpp
Engine/source/T3D/decal/decalManager.cpp
Public Enumerations
enum
_Anonymous_ { SIZE_CLASS_0 = 256 SIZE_CLASS_1 = 512 SIZE_CLASS_2 = 1024 NUM_SIZE_CLASSES = 3 }
Public Variables
A bias applied to the nearPlane for Decal and DecalRoad rendering.
bool
For frame signal.
Public Functions
ConsoleDoc("@defgroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Decals\n</a>" "@brief Decals are non-<a href="/coding/class/classsimobject/">SimObject</a> derived objects that are stored and loaded " "separately from the normal mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n\n</a>" "The <a href="/coding/class/classdecalmanager/">DecalManager</a> handles all aspects of decal management including loading, " " creation, saving , and automatically deleting decals that have exceeded their " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">lifeSpan.\n\n</a>" "The static decals associated with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> mission are normally loaded immediately " "after the mission itself has loaded as shown <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">below.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Load the static mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decals.\n</a>" "decalManagerLoad(%missionName @ \".decals\" );\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
ConsoleDocClass(DecalManager , "@brief The object that manages all of the decals in the active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mission.\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>" )
DefineEngineFunction(decalManagerAddDecal , S32 , (Point3F position, Point3F normal, F32 rot, F32 scale, DecalData *decalData, bool isImmortal) , (false) , "Adds <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> decal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">manager.\n</a>" "@param position <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param normal Decal normal vector (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal was <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tire lying flat on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "surface, this is the vector pointing in the direction of the axle).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param rot Angle (in radians) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> rotate this decal around its normal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "@param scale Scale factor applied <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param decalData <a href="/coding/class/classdecaldata/">DecalData</a> datablock <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param isImmortal Whether or not this decal is immortal. If immortal, it " "does not expire automatically and must be removed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explicitly.\n</a>" " @return Returns the ID of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> Decal object or -1 on <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failure.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Specify the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" "% position)
DefineEngineFunction(decalManagerClear , void , () , "Removes all decals currently loaded in the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">manager.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Tell the decal manager <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> remove all existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decals.\n</a>" "decalManagerClear();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup Decals" )
DefineEngineFunction(decalManagerDirty , bool , () , "Returns whether the decal manager has unsaved <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">modifications.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal manager has unsaved modifications)
DefineEngineFunction(decalManagerEditDecal , bool , (S32 decalID, Point3F pos, Point3F normal, F32 rotAroundNormal, F32 decalScale) , "Edit specified decal of the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">manager.\n</a>" "@param decalID ID of the decal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">edit.\n</a>" "@param pos <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param normal Decal normal vector (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal was <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tire lying flat on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "surface, this is the vector pointing in the direction of the axle).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param rotAroundNormal Angle (in radians) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> rotate this decal around its normal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "@param decalScale Scale factor applied <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> decalID not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">found.\n</a>" "" )
DefineEngineFunction(decalManagerLoad , bool , (const char *fileName) , "Clears existing decals and replaces them with decals loaded from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n</a>" "@param fileName Filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal manager was able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load the requested file, " "false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it could <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Set the filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "% fileName)
DefineEngineFunction(decalManagerRemoveDecal , bool , (S32 decalID) , "Remove specified decal from the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scene.\n</a>" "@param decalID ID of the decal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">remove.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful)
DefineEngineFunction(decalManagerSave , void , (String decalSaveFile) , ("") , "Saves the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the active mission in the entered <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">filename.\n</a>" "@param decalSaveFile Filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Set the filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the decals in. If no filename is set, then <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">the\n</a>" "//decals will default <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a>< activeMissionName >.<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mis.decals\n</a>" "% fileName)
Detailed Description
Public Enumerations
@182
Enumerator
- SIZE_CLASS_0 = 256
- SIZE_CLASS_1 = 512
- SIZE_CLASS_2 = 1024
- NUM_SIZE_CLASSES = 3
Public Variables
F32 gDecalBias
A bias applied to the nearPlane for Decal and DecalRoad rendering.
Is set by by LevelInfo.
DecalManager * gDecalManager
bool gEditingMission
For frame signal.
MODULE_END
MODULE_INIT
MODULE_SHUTDOWN
Public Functions
addObjectToScene(gDecalManager )
ConsoleDoc("@defgroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Decals\n</a>" "@brief Decals are non-<a href="/coding/class/classsimobject/">SimObject</a> derived objects that are stored and loaded " "separately from the normal mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n\n</a>" "The <a href="/coding/class/classdecalmanager/">DecalManager</a> handles all aspects of decal management including loading, " " creation, saving , and automatically deleting decals that have exceeded their " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">lifeSpan.\n\n</a>" "The static decals associated with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> mission are normally loaded immediately " "after the mission itself has loaded as shown <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">below.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Load the static mission <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decals.\n</a>" "decalManagerLoad(%missionName @ \".decals\" );\n" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
ConsoleDocClass(DecalManager , "@brief The object that manages all of the decals in the active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mission.\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>" )
DefineEngineFunction(decalManagerAddDecal , S32 , (Point3F position, Point3F normal, F32 rot, F32 scale, DecalData *decalData, bool isImmortal) , (false) , "Adds <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> decal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">manager.\n</a>" "@param position <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param normal Decal normal vector (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal was <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tire lying flat on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "surface, this is the vector pointing in the direction of the axle).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param rot Angle (in radians) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> rotate this decal around its normal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "@param scale Scale factor applied <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param decalData <a href="/coding/class/classdecaldata/">DecalData</a> datablock <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param isImmortal Whether or not this decal is immortal. If immortal, it " "does not expire automatically and must be removed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explicitly.\n</a>" " @return Returns the ID of the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> Decal object or -1 on <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failure.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Specify the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" "% position)
DefineEngineFunction(decalManagerClear , void , () , "Removes all decals currently loaded in the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">manager.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Tell the decal manager <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> remove all existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decals.\n</a>" "decalManagerClear();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup Decals" )
DefineEngineFunction(decalManagerDirty , bool , () , "Returns whether the decal manager has unsaved <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">modifications.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal manager has unsaved modifications)
DefineEngineFunction(decalManagerEditDecal , bool , (S32 decalID, Point3F pos, Point3F normal, F32 rotAroundNormal, F32 decalScale) , "Edit specified decal of the decal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">manager.\n</a>" "@param decalID ID of the decal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">edit.\n</a>" "@param pos <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@param normal Decal normal vector (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal was <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tire lying flat on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "surface, this is the vector pointing in the direction of the axle).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param rotAroundNormal Angle (in radians) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> rotate this decal around its normal <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" "@param decalScale Scale factor applied <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">decal.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> decalID not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">found.\n</a>" "" )
DefineEngineFunction(decalManagerLoad , bool , (const char *fileName) , "Clears existing decals and replaces them with decals loaded from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n</a>" "@param fileName Filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the decal manager was able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load the requested file, " "false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it could <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Set the filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">from.\n</a>" "% fileName)
DefineEngineFunction(decalManagerRemoveDecal , bool , (S32 decalID) , "Remove specified decal from the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scene.\n</a>" "@param decalID ID of the decal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">remove.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful)
DefineEngineFunction(decalManagerSave , void , (String decalSaveFile) , ("") , "Saves the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the active mission in the entered <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">filename.\n</a>" "@param decalSaveFile Filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the decals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Set the filename <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the decals in. If no filename is set, then <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">the\n</a>" "//decals will default <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a>< activeMissionName >.<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mis.decals\n</a>" "% fileName)
IMPLEMENT_CONOBJECT(DecalManager )
SAFE_DELETE(gDecalManager )
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/decalManager.h" 26 27#include "scene/sceneManager.h" 28#include "scene/sceneRenderState.h" 29#include "ts/tsShapeInstance.h" 30#include "console/console.h" 31#include "console/dynamicTypes.h" 32#include "gfx/primBuilder.h" 33#include "console/consoleTypes.h" 34#include "platform/profiler.h" 35#include "gfx/gfxTransformSaver.h" 36#include "lighting/lightManager.h" 37#include "lighting/lightInfo.h" 38#include "gfx/gfxDrawUtil.h" 39#include "gfx/sim/gfxStateBlockData.h" 40#include "materials/shaderData.h" 41#include "materials/matInstance.h" 42#include "renderInstance/renderPassManager.h" 43#include "core/resourceManager.h" 44#include "core/stream/fileStream.h" 45#include "gfx/gfxDebugEvent.h" 46#include "math/util/quadTransforms.h" 47#include "math/mathUtils.h" 48#include "core/volume.h" 49#include "core/module.h" 50#include "T3D/decal/decalData.h" 51#include "console/engineAPI.h" 52 53 54extern bool gEditingMission; 55 56 57MODULE_BEGIN( DecalManager ) 58 59 MODULE_INIT_AFTER( Scene ) 60 MODULE_SHUTDOWN_BEFORE( Scene ) 61 62 MODULE_INIT 63 { 64 gDecalManager = new DecalManager; 65 gClientSceneGraph->addObjectToScene( gDecalManager ); 66 } 67 68 MODULE_SHUTDOWN 69 { 70 gClientSceneGraph->removeObjectFromScene( gDecalManager ); 71 SAFE_DELETE( gDecalManager ); 72 } 73 74MODULE_END; 75 76 77/// A bias applied to the nearPlane for Decal and DecalRoad rendering. 78/// Is set by by LevelInfo. 79F32 gDecalBias = 0.0015f; 80 81bool DecalManager::smDecalsOn = true; 82bool DecalManager::smDebugRender = false; 83F32 DecalManager::smDecalLifeTimeScale = 1.0f; 84bool DecalManager::smPoolBuffers = true; 85const U32 DecalManager::smMaxVerts = 6000; 86const U32 DecalManager::smMaxIndices = 10000; 87 88DecalManager *gDecalManager = NULL; 89 90IMPLEMENT_CONOBJECT(DecalManager); 91 92ConsoleDoc( 93 "@defgroup Decals\n" 94 "@brief Decals are non-SimObject derived objects that are stored and loaded " 95 "separately from the normal mission file.\n\n" 96 97 "The DecalManager handles all aspects of decal management including loading, " 98 "creation, saving, and automatically deleting decals that have exceeded their " 99 "lifeSpan.\n\n" 100 101 "The static decals associated with a mission are normally loaded immediately " 102 "after the mission itself has loaded as shown below.\n" 103 104 "@tsexample\n" 105 "// Load the static mission decals.\n" 106 "decalManagerLoad( %missionName @ \".decals\" );\n" 107 "@endtsexample\n" 108 109 "@ingroup FX\n" 110); 111 112ConsoleDocClass( DecalManager, 113 "@brief The object that manages all of the decals in the active mission.\n\n" 114 "@see Decals\n" 115 "@ingroup Decals\n" 116 "@ingroup FX\n" 117); 118 119namespace { 120 121S32 QSORT_CALLBACK cmpDecalInstance(const void* p1, const void* p2) 122{ 123 const DecalInstance** pd1 = (const DecalInstance**)p1; 124 const DecalInstance** pd2 = (const DecalInstance**)p2; 125 126 return int(((char *)(*pd1)->mDataBlock) - ((char *)(*pd2)->mDataBlock)); 127} 128 129S32 QSORT_CALLBACK cmpPointsXY( const void *p1, const void *p2 ) 130{ 131 const Point3F *pnt1 = (const Point3F*)p1; 132 const Point3F *pnt2 = (const Point3F*)p2; 133 134 if ( pnt1->x < pnt2->x ) 135 return -1; 136 else if ( pnt1->x > pnt2->x ) 137 return 1; 138 else if ( pnt1->y < pnt2->y ) 139 return -1; 140 else if ( pnt1->y > pnt2->y ) 141 return 1; 142 else 143 return 0; 144} 145 146S32 QSORT_CALLBACK cmpQuadPointTheta( const void *p1, const void *p2 ) 147{ 148 const Point4F *pnt1 = (const Point4F*)p1; 149 const Point4F *pnt2 = (const Point4F*)p2; 150 151 if ( mFabs( pnt1->w ) > mFabs( pnt2->w ) ) 152 return 1; 153 else if ( mFabs( pnt1->w ) < mFabs( pnt2->w ) ) 154 return -1; 155 else 156 return 0; 157} 158 159static Point3F gSortPoint; 160 161S32 QSORT_CALLBACK cmpDecalDistance( const void *p1, const void *p2 ) 162{ 163 const DecalInstance** pd1 = (const DecalInstance**)p1; 164 const DecalInstance** pd2 = (const DecalInstance**)p2; 165 166 F32 dist1 = ( (*pd1)->mPosition - gSortPoint ).lenSquared(); 167 F32 dist2 = ( (*pd2)->mPosition - gSortPoint ).lenSquared(); 168 169 return mSign( dist1 - dist2 ); 170} 171 172S32 QSORT_CALLBACK cmpDecalRenderOrder( const void *p1, const void *p2 ) 173{ 174 const DecalInstance** pd1 = (const DecalInstance**)p1; 175 const DecalInstance** pd2 = (const DecalInstance**)p2; 176 177 if ( ( (*pd2)->mFlags & SaveDecal ) && !( (*pd1)->mFlags & SaveDecal ) ) 178 return -1; 179 else if ( !( (*pd2)->mFlags & SaveDecal ) && ( (*pd1)->mFlags & SaveDecal ) ) 180 return 1; 181 else 182 { 183 S32 priority = (*pd1)->getRenderPriority() - (*pd2)->getRenderPriority(); 184 185 if ( priority != 0 ) 186 return priority; 187 188 if ( (*pd2)->mFlags & SaveDecal ) 189 { 190 S32 id = ( (*pd1)->mDataBlock->getMaterial()->getId() - (*pd2)->mDataBlock->getMaterial()->getId() ); 191 if ( id != 0 ) 192 return id; 193 194 return (*pd1)->mCreateTime - (*pd2)->mCreateTime; 195 } 196 else 197 return (*pd1)->mCreateTime - (*pd2)->mCreateTime; 198 } 199} 200 201} // namespace {} 202 203// These numbers should be tweaked to get as many dynamically placed decals 204// as possible to allocate buffer arrays with the FreeListChunker. 205enum 206{ 207 SIZE_CLASS_0 = 256, 208 SIZE_CLASS_1 = 512, 209 SIZE_CLASS_2 = 1024, 210 211 NUM_SIZE_CLASSES = 3 212}; 213 214//------------------------------------------------------------------------- 215// DecalManager 216//------------------------------------------------------------------------- 217DecalManager::DecalManager() 218{ 219 #ifdef DECALMANAGER_DEBUG 220 VECTOR_SET_ASSOCIATION( mDebugPlanes ); 221 #endif 222 223 setGlobalBounds(); 224 225 mDataFileName = NULL; 226 227 mTypeMask |= EnvironmentObjectType; 228 229 mDirty = false; 230 231 mChunkers[0] = new FreeListChunkerUntyped( SIZE_CLASS_0 * sizeof( U8 ) ); 232 mChunkers[1] = new FreeListChunkerUntyped( SIZE_CLASS_1 * sizeof( U8 ) ); 233 mChunkers[2] = new FreeListChunkerUntyped( SIZE_CLASS_2 * sizeof( U8 ) ); 234 235 GFXDevice::getDeviceEventSignal().notify(this, &DecalManager::_handleGFXEvent); 236} 237 238DecalManager::~DecalManager() 239{ 240 GFXDevice::getDeviceEventSignal().remove(this, &DecalManager::_handleGFXEvent); 241 242 clearData(); 243 244 for( U32 i = 0; i < NUM_SIZE_CLASSES; ++ i ) 245 delete mChunkers[ i ]; 246} 247 248void DecalManager::consoleInit() 249{ 250 Con::addVariable( "$pref::Decals::enabled", TypeBool, &smDecalsOn, 251 "Controls whether decals are rendered.\n" 252 "@ingroup Decals" ); 253 254 Con::addVariable( "$pref::Decals::lifeTimeScale", TypeF32, &smDecalLifeTimeScale, 255 "@brief Lifetime that decals will last after being created in the world.\n" 256 "Deprecated. Use DecalData::lifeSpan instead.\n" 257 "@ingroup Decals" ); 258 259 Con::addVariable( "$Decals::poolBuffers", TypeBool, &smPoolBuffers, 260 "If true, will merge all PrimitiveBuffers and VertexBuffers into a pair " 261 "of pools before clearing them at the end of a frame.\n" 262 "If false, will just clear them at the end of a frame.\n" 263 "@ingroup Decals" ); 264 265 Con::addVariable( "$Decals::debugRender", TypeBool, &smDebugRender, 266 "If true, the decal spheres will be visualized when in the editor.\n\n" 267 "@ingroup Decals" ); 268 269 Con::addVariable( "$Decals::sphereDistanceTolerance", TypeF32, &DecalSphere::smDistanceTolerance, 270 "The distance at which the decal system will start breaking up decal " 271 "spheres when adding new decals.\n\n" 272 "@ingroup Decals" ); 273 274 Con::addVariable( "$Decals::sphereRadiusTolerance", TypeF32, &DecalSphere::smRadiusTolerance, 275 "The radius beyond which the decal system will start breaking up decal " 276 "spheres when adding new decals.\n\n" 277 "@ingroup Decals" ); 278} 279 280bool DecalManager::_handleGFXEvent(GFXDevice::GFXDeviceEventType event) 281{ 282 switch(event) 283 { 284 case GFXDevice::deEndOfFrame: 285 286 // Return PrimitiveBuffers and VertexBuffers used this frame to the pool. 287 288 if ( smPoolBuffers ) 289 { 290 mPBPool.merge( mPBs ); 291 mPBs.clear(); 292 293 mVBPool.merge( mVBs ); 294 mVBs.clear(); 295 } 296 else 297 { 298 _freePools(); 299 } 300 301 break; 302 303 default: ; 304 } 305 306 return true; 307} 308 309bool DecalManager::clipDecal( DecalInstance *decal, Vector<Point3F> *edgeVerts, const Point2F *clipDepth ) 310{ 311 PROFILE_SCOPE( DecalManager_clipDecal ); 312 313 // Free old verts and indices. 314 _freeBuffers( decal ); 315 316 F32 halfSize = decal->mSize * 0.5f; 317 318 // Ugly hack for ProjectedShadow! 319 F32 halfSizeZ = clipDepth ? clipDepth->x : halfSize; 320 F32 negHalfSize = clipDepth ? clipDepth->y : halfSize; 321 Point3F decalHalfSize( halfSize, halfSize, halfSize ); 322 Point3F decalHalfSizeZ( halfSizeZ, halfSizeZ, halfSizeZ ); 323 324 MatrixF projMat( true ); 325 decal->getWorldMatrix( &projMat ); 326 327 const VectorF &crossVec = decal->mNormal; 328 const Point3F &decalPos = decal->mPosition; 329 330 VectorF newFwd, newRight; 331 projMat.getColumn( 0, &newRight ); 332 projMat.getColumn( 1, &newFwd ); 333 334 VectorF objRight( 1.0f, 0, 0 ); 335 VectorF objFwd( 0, 1.0f, 0 ); 336 VectorF objUp( 0, 0, 1.0f ); 337 338 // See above re: decalHalfSizeZ hack. 339 mClipper.clear(); 340 mClipper.mPlaneList.setSize(6); 341 mClipper.mPlaneList[0].set( ( decalPos + ( -newRight * halfSize ) ), -newRight ); 342 mClipper.mPlaneList[1].set( ( decalPos + ( -newFwd * halfSize ) ), -newFwd ); 343 mClipper.mPlaneList[2].set( ( decalPos + ( -crossVec * decalHalfSizeZ ) ), -crossVec ); 344 mClipper.mPlaneList[3].set( ( decalPos + ( newRight * halfSize ) ), newRight ); 345 mClipper.mPlaneList[4].set( ( decalPos + ( newFwd * halfSize ) ), newFwd ); 346 mClipper.mPlaneList[5].set( ( decalPos + ( crossVec * negHalfSize ) ), crossVec ); 347 348 mClipper.mNormal = decal->mNormal; 349 350 const DecalData *decalData = decal->mDataBlock; 351 352 mClipper.mNormalTolCosineRadians = mCos( mDegToRad( decalData->clippingAngle ) ); 353 354 Box3F box( -decalHalfSizeZ, decalHalfSizeZ ); 355 356 projMat.mul( box ); 357 358 PROFILE_START( DecalManager_clipDecal_buildPolyList ); 359 getContainer()->buildPolyList( PLC_Decal, box, decalData->clippingMasks, &mClipper ); 360 PROFILE_END(); 361 362 mClipper.cullUnusedVerts(); 363 mClipper.triangulate(); 364 365 const U32 numVerts = mClipper.mVertexList.size(); 366 const U32 numIndices = mClipper.mIndexList.size(); 367 368 if ( !numVerts || !numIndices ) 369 return false; 370 371 // Fail if either of the buffer metrics exceeds our limits 372 // on dynamic geometry buffers. 373 if ( numVerts > smMaxVerts || 374 numIndices > smMaxIndices ) 375 return false; 376 377 if ( !decalData->skipVertexNormals ) 378 mClipper.generateNormals(); 379 380#ifdef DECALMANAGER_DEBUG 381 mDebugPlanes.clear(); 382 mDebugPlanes.merge( mClipper.mPlaneList ); 383#endif 384 385 decal->mVertCount = numVerts; 386 decal->mIndxCount = numIndices; 387 388 Vector<Point3F> tmpPoints; 389 390 tmpPoints.push_back(( objFwd * decalHalfSize ) + ( objRight * decalHalfSize )); 391 tmpPoints.push_back(( objFwd * decalHalfSize ) + ( -objRight * decalHalfSize )); 392 tmpPoints.push_back(( -objFwd * decalHalfSize ) + ( -objRight * decalHalfSize )); 393 394 Point3F lowerLeft(( -objFwd * decalHalfSize ) + ( objRight * decalHalfSize )); 395 396 projMat.inverse(); 397 398 _generateWindingOrder( lowerLeft, &tmpPoints ); 399 400 BiQuadToSqr quadToSquare( Point2F( lowerLeft.x, lowerLeft.y ), 401 Point2F( tmpPoints[0].x, tmpPoints[0].y ), 402 Point2F( tmpPoints[1].x, tmpPoints[1].y ), 403 Point2F( tmpPoints[2].x, tmpPoints[2].y ) ); 404 405 Point2F uv( 0, 0 ); 406 Point3F vecX(0.0f, 0.0f, 0.0f); 407 408 // Allocate memory for vert and index arrays 409 _allocBuffers( decal ); 410 411 // Mark this so that the color will be assigned on these verts the next 412 // time it renders, since we just threw away the previous verts. 413 decal->mLastAlpha = -1; 414 415 Point3F vertPoint( 0, 0, 0 ); 416 417 for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ ) 418 { 419 const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i]; 420 vertPoint = vert.point; 421 422 // Transform this point to 423 // object space to look up the 424 // UV coordinate for this vertex. 425 projMat.mulP( vertPoint ); 426 427 // Clamp the point to be within the quad. 428 vertPoint.x = mClampF( vertPoint.x, -decalHalfSize.x, decalHalfSize.x ); 429 vertPoint.y = mClampF( vertPoint.y, -decalHalfSize.y, decalHalfSize.y ); 430 431 // Get our UV. 432 uv = quadToSquare.transform( Point2F( vertPoint.x, vertPoint.y ) ); 433 434 const RectF &rect = decal->mDataBlock->texRect[decal->mTextureRectIdx]; 435 436 uv *= rect.extent; 437 uv += rect.point; 438 439 // Set the world space vertex position. 440 decal->mVerts[i].point = vert.point; 441 442 decal->mVerts[i].texCoord.set( uv.x, uv.y ); 443 444 if ( mClipper.mNormalList.empty() ) 445 continue; 446 447 decal->mVerts[i].normal = mClipper.mNormalList[i]; 448 decal->mVerts[i].normal.normalize(); 449 450 if( mFabs( decal->mVerts[i].normal.z ) > 0.8f ) 451 mCross( decal->mVerts[i].normal, Point3F( 1.0f, 0.0f, 0.0f ), &vecX ); 452 else if ( mFabs( decal->mVerts[i].normal.x ) > 0.8f ) 453 mCross( decal->mVerts[i].normal, Point3F( 0.0f, 1.0f, 0.0f ), &vecX ); 454 else if ( mFabs( decal->mVerts[i].normal.y ) > 0.8f ) 455 mCross( decal->mVerts[i].normal, Point3F( 0.0f, 0.0f, 1.0f ), &vecX ); 456 457 decal->mVerts[i].tangent = mCross( decal->mVerts[i].normal, vecX ); 458 } 459 460 U32 curIdx = 0; 461 for ( U32 j = 0; j < mClipper.mPolyList.size(); j++ ) 462 { 463 // Write indices for each Poly 464 ClippedPolyList::Poly *poly = &mClipper.mPolyList[j]; 465 466 AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" ); 467 468 decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart]; 469 curIdx++; 470 decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 1]; 471 curIdx++; 472 decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 2]; 473 curIdx++; 474 } 475 476 if ( !edgeVerts ) 477 return true; 478 479 Point3F tmpHullPt( 0, 0, 0 ); 480 Vector<Point3F> tmpHullPts; 481 482 for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ ) 483 { 484 const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i]; 485 tmpHullPt = vert.point; 486 projMat.mulP( tmpHullPt ); 487 tmpHullPts.push_back( tmpHullPt ); 488 } 489 490 edgeVerts->clear(); 491 U32 verts = _generateConvexHull( tmpHullPts, edgeVerts ); 492 edgeVerts->setSize( verts ); 493 494 projMat.inverse(); 495 for ( U32 i = 0; i < edgeVerts->size(); i++ ) 496 projMat.mulP( (*edgeVerts)[i] ); 497 498 return true; 499} 500 501DecalInstance* DecalManager::addDecal( const Point3F &pos, 502 const Point3F &normal, 503 F32 rotAroundNormal, 504 DecalData *decalData, 505 F32 decalScale, 506 S32 decalTexIndex, 507 U8 flags ) 508{ 509 MatrixF mat( true ); 510 MathUtils::getMatrixFromUpVector( normal, &mat ); 511 512 AngAxisF rot( normal, rotAroundNormal ); 513 MatrixF rotmat; 514 rot.setMatrix( &rotmat ); 515 mat.mul( rotmat ); 516 517 Point3F tangent; 518 mat.getColumn( 1, &tangent ); 519 520 return addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags ); 521} 522 523DecalInstance* DecalManager::addDecal( const Point3F& pos, 524 const Point3F& normal, 525 const Point3F& tangent, 526 DecalData* decalData, 527 F32 decalScale, 528 S32 decalTexIndex, 529 U8 flags ) 530{ 531 if ( !mData && !_createDataFile() ) 532 return NULL; 533 534 // only dirty the manager if this decal should be saved 535 if ( flags & SaveDecal ) 536 mDirty = true; 537 538 return mData->addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags ); 539} 540 541void DecalManager::removeDecal( DecalInstance *inst ) 542{ 543 // If this is a decal we save then we need 544 // to set the dirty flag. 545 if ( inst->mFlags & SaveDecal ) 546 mDirty = true; 547 548 // Remove the decal from the instance vector. 549 550 if( inst->mId != -1 && inst->mId < mDecalInstanceVec.size() ) 551 mDecalInstanceVec[ inst->mId ] = NULL; 552 553 // Release its geometry (if it has any). 554 555 _freeBuffers( inst ); 556 557 // Remove it from the decal file. 558 559 if ( mData ) 560 mData->removeDecal( inst ); 561} 562 563DecalInstance* DecalManager::getDecal( S32 id ) 564{ 565 if( id < 0 || id >= mDecalInstanceVec.size() ) 566 return NULL; 567 568 return mDecalInstanceVec[id]; 569} 570 571void DecalManager::notifyDecalModified( DecalInstance *inst ) 572{ 573 // If this is a decal we save then we need 574 // to set the dirty flag. 575 if ( inst->mFlags & SaveDecal ) 576 mDirty = true; 577 578 if ( mData ) 579 mData->notifyDecalModified( inst ); 580} 581 582DecalInstance* DecalManager::getClosestDecal( const Point3F &pos ) 583{ 584 if ( !mData ) 585 return NULL; 586 587 const Vector<DecalSphere*> &grid = mData->getSphereList(); 588 589 DecalInstance *inst = NULL; 590 SphereF worldPickSphere( pos, 0.5f ); 591 SphereF worldInstSphere( Point3F( 0, 0, 0 ), 1.0f ); 592 593 Vector<DecalInstance*> collectedInsts; 594 595 for ( U32 i = 0; i < grid.size(); i++ ) 596 { 597 DecalSphere *decalSphere = grid[i]; 598 const SphereF &worldSphere = decalSphere->mWorldSphere; 599 if ( !worldSphere.isIntersecting( worldPickSphere ) && 600 !worldSphere.isContained( pos ) ) 601 continue; 602 603 const Vector<DecalInstance*> &items = decalSphere->mItems; 604 for ( U32 n = 0; n < items.size(); n++ ) 605 { 606 inst = items[n]; 607 if ( !inst ) 608 continue; 609 610 worldInstSphere.center = inst->mPosition; 611 worldInstSphere.radius = inst->mSize; 612 613 if ( !worldInstSphere.isContained( inst->mPosition ) ) 614 continue; 615 616 collectedInsts.push_back( inst ); 617 } 618 } 619 620 F32 closestDistance = F32_MAX; 621 F32 currentDist = 0; 622 U32 closestIndex = 0; 623 for ( U32 i = 0; i < collectedInsts.size(); i++ ) 624 { 625 inst = collectedInsts[i]; 626 currentDist = (inst->mPosition - pos).len(); 627 if ( currentDist < closestDistance ) 628 { 629 closestIndex = i; 630 closestDistance = currentDist; 631 worldInstSphere.center = inst->mPosition; 632 worldInstSphere.radius = inst->mSize; 633 } 634 } 635 636 if ( (!collectedInsts.empty() && 637 collectedInsts[closestIndex] && 638 closestDistance < 1.0f) || 639 worldInstSphere.isContained( pos ) ) 640 return collectedInsts[closestIndex]; 641 else 642 return NULL; 643} 644 645DecalInstance* DecalManager::raycast( const Point3F &start, const Point3F &end, bool savedDecalsOnly ) 646{ 647 if ( !mData ) 648 return NULL; 649 650 const Vector<DecalSphere*> &grid = mData->getSphereList(); 651 652 DecalInstance *inst = NULL; 653 SphereF worldSphere( Point3F( 0, 0, 0 ), 1.0f ); 654 655 Vector<DecalInstance*> hitDecals; 656 657 for ( U32 i = 0; i < grid.size(); i++ ) 658 { 659 DecalSphere *decalSphere = grid[i]; 660 if ( !decalSphere->mWorldSphere.intersectsRay( start, end ) ) 661 continue; 662 663 const Vector<DecalInstance*> &items = decalSphere->mItems; 664 for ( U32 n = 0; n < items.size(); n++ ) 665 { 666 inst = items[n]; 667 if ( !inst ) 668 continue; 669 670 if ( savedDecalsOnly && !(inst->mFlags & SaveDecal) ) 671 continue; 672 673 worldSphere.center = inst->mPosition; 674 worldSphere.radius = inst->mSize; 675 676 if ( !worldSphere.intersectsRay( start, end ) ) 677 continue; 678 679 RayInfo ri; 680 bool containsPoint = false; 681 if ( gServerContainer.castRayRendered( start, end, STATIC_COLLISION_TYPEMASK, &ri ) ) 682 { 683 Point2F poly[4]; 684 poly[0].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2)); 685 poly[1].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2)); 686 poly[2].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2)); 687 poly[3].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2)); 688 689 if ( MathUtils::pointInPolygon( poly, 4, Point2F(ri.point.x, ri.point.y) ) ) 690 containsPoint = true; 691 } 692 693 if( !containsPoint ) 694 continue; 695 696 hitDecals.push_back( inst ); 697 } 698 } 699 700 if ( hitDecals.empty() ) 701 return NULL; 702 703 gSortPoint = start; 704 dQsort( hitDecals.address(), hitDecals.size(), sizeof(DecalInstance*), cmpDecalDistance ); 705 return hitDecals[0]; 706} 707 708U32 DecalManager::_generateConvexHull( const Vector<Point3F> &points, Vector<Point3F> *outPoints ) 709{ 710 PROFILE_SCOPE( DecalManager_generateConvexHull ); 711 712 // chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm 713 // Input: P[] = an array of 2D points 714 // presorted by increasing x- and y-coordinates 715 // n = the number of points in P[] 716 // Output: H[] = an array of the convex hull vertices (max is n) 717 // Return: the number of points in H[] 718 //int 719 720 if ( points.size() < 3 ) 721 { 722 outPoints->merge( points ); 723 return outPoints->size(); 724 } 725 726 // Sort our input points. 727 dQsort( points.address(), points.size(), sizeof( Point3F ), cmpPointsXY ); 728 729 U32 n = points.size(); 730 731 Vector<Point3F> tmpPoints; 732 tmpPoints.setSize( n ); 733 734 // the output array H[] will be used as the stack 735 S32 bot=0, top=(-1); // indices for bottom and top of the stack 736 S32 i; // array scan index 737 S32 toptmp = 0; 738 739 // Get the indices of points with min x-coord and min|max y-coord 740 S32 minmin = 0, minmax; 741 F32 xmin = points[0].x; 742 for ( i = 1; i < n; i++ ) 743 if (points[i].x != xmin) 744 break; 745 746 minmax = i - 1; 747 if ( minmax == n - 1 ) 748 { 749 // degenerate case: all x-coords == xmin 750 toptmp = top + 1; 751 if ( toptmp < n ) 752 tmpPoints[++top] = points[minmin]; 753 754 if ( points[minmax].y != points[minmin].y ) // a nontrivial segment 755 { 756 toptmp = top + 1; 757 if ( toptmp < n ) 758 tmpPoints[++top] = points[minmax]; 759 } 760 761 toptmp = top + 1; 762 if ( toptmp < n ) 763 tmpPoints[++top] = points[minmin]; // add polygon endpoint 764 765 return top+1; 766 } 767 768 // Get the indices of points with max x-coord and min|max y-coord 769 S32 maxmin, maxmax = n-1; 770 F32 xmax = points[n-1].x; 771 772 for ( i = n - 2; i >= 0; i-- ) 773 if ( points[i].x != xmax ) 774 break; 775 776 maxmin = i + 1; 777 778 // Compute the lower hull on the stack H 779 toptmp = top + 1; 780 if ( toptmp < n ) 781 tmpPoints[++top] = points[minmin]; // push minmin point onto stack 782 783 i = minmax; 784 while ( ++i <= maxmin ) 785 { 786 // the lower line joins P[minmin] with P[maxmin] 787 if (i < maxmin && isLeft(points[minmin], points[maxmin], points[i]) >= 0) 788 continue; // ignore P[i] above or on the lower line 789 790 while (top > 0) // there are at least 2 points on the stack 791 { 792 // test if P[i] is left of the line at the stack top 793 if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i]) > 0) 794 break; // P[i] is a new hull vertex 795 else 796 top--; // pop top point off stack 797 } 798 799 toptmp = top + 1; 800 if ( toptmp < n ) 801 tmpPoints[++top] = points[i]; // push P[i] onto stack 802 } 803 804 // Next, compute the upper hull on the stack H above the bottom hull 805 if (maxmax != maxmin) // if distinct xmax points 806 { 807 toptmp = top + 1; 808 if ( toptmp < n ) 809 tmpPoints[++top] = points[maxmax]; // push maxmax point onto stack 810 } 811 812 bot = top; // the bottom point of the upper hull stack 813 i = maxmin; 814 while (--i >= minmax) 815 { 816 // the upper line joins P[maxmax] with P[minmax] 817 if ( isLeft( points[maxmax], points[minmax], points[i] ) >= 0 && i > minmax ) 818 continue; // ignore P[i] below or on the upper line 819 820 while ( top > bot ) // at least 2 points on the upper stack 821 { 822 // test if P[i] is left of the line at the stack top 823 if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i] ) > 0 ) 824 break; // P[i] is a new hull vertex 825 else 826 top--; // pop top point off stack 827 } 828 829 toptmp = top + 1; 830 if ( toptmp < n ) 831 tmpPoints[++top] = points[i]; // push P[i] onto stack 832 } 833 834 if (minmax != minmin) 835 { 836 toptmp = top + 1; 837 if ( toptmp < n ) 838 tmpPoints[++top] = points[minmin]; // push joining endpoint onto stack 839 } 840 841 outPoints->merge( tmpPoints ); 842 843 return top + 1; 844} 845 846void DecalManager::_generateWindingOrder( const Point3F &cornerPoint, Vector<Point3F> *sortPoints ) 847{ 848 // This block of code is used to find 849 // the winding order for the points in our quad. 850 851 // First, choose an arbitrary corner point. 852 // We'll use the "lowerRight" point. 853 Point3F relPoint( 0, 0, 0 ); 854 855 // See comment below about radius. 856 //F32 radius = 0; 857 858 F32 theta = 0; 859 860 Vector<Point4F> tmpPoints; 861 862 for ( U32 i = 0; i < (*sortPoints).size(); i++ ) 863 { 864 const Point3F &pnt = (*sortPoints)[i]; 865 relPoint = cornerPoint - pnt; 866 867 // Get the radius (r^2 = x^2 + y^2). 868 869 // This is commented because for a quad 870 // you typically can't have the same values 871 // for theta, which is the caveat which would 872 // require sorting by the radius. 873 //radius = mSqrt( (relPoint.x * relPoint.x) + (relPoint.y * relPoint.y) ); 874 875 // Get the theta value for the 876 // interval -PI, PI. 877 878 // This algorithm for determining the 879 // theta value is defined by 880 // | arctan( y / x ) if x > 0 881 // | arctan( y / x ) if x < 0 and y >= 0 882 // theta = | arctan( y / x ) if x < 0 and y < 0 883 // | PI / 2 if x = 0 and y > 0 884 // | -( PI / 2 ) if x = 0 and y < 0 885 if ( relPoint.x > 0.0f ) 886 theta = mAtan2( relPoint.y, relPoint.x ); 887 else if ( relPoint.x < 0.0f ) 888 { 889 if ( relPoint.y >= 0.0f ) 890 theta = mAtan2( relPoint.y, relPoint.x ) + M_PI_F; 891 else if ( relPoint.y < 0.0f ) 892 theta = mAtan2( relPoint.y, relPoint.x ) - M_PI_F; 893 } 894 else if ( relPoint.x == 0.0f ) 895 { 896 if ( relPoint.y > 0.0f ) 897 theta = M_PI_F / 2.0f; 898 else if ( relPoint.y < 0.0f ) 899 theta = -(M_PI_F / 2.0f); 900 } 901 902 tmpPoints.push_back( Point4F( pnt.x, pnt.y, pnt.z, theta ) ); 903 } 904 905 dQsort( tmpPoints.address(), tmpPoints.size(), sizeof( Point4F ), cmpQuadPointTheta ); 906 907 for ( U32 i = 0; i < tmpPoints.size(); i++ ) 908 { 909 const Point4F &tmpPoint = tmpPoints[i]; 910 (*sortPoints)[i].set( tmpPoint.x, tmpPoint.y, tmpPoint.z ); 911 } 912} 913 914void DecalManager::_allocBuffers( DecalInstance *inst ) 915{ 916 const S32 sizeClass = _getSizeClass( inst ); 917 918 void* data; 919 if ( sizeClass == -1 ) 920 data = dMalloc( sizeof( DecalVertex ) * inst->mVertCount + sizeof( U16 ) * inst->mIndxCount ); 921 else 922 data = mChunkers[sizeClass]->alloc(); 923 924 inst->mVerts = reinterpret_cast< DecalVertex* >( data ); 925 data = (U8*)data + sizeof( DecalVertex ) * inst->mVertCount; 926 inst->mIndices = reinterpret_cast< U16* >( data ); 927} 928 929void DecalManager::_freeBuffers( DecalInstance *inst ) 930{ 931 if ( inst->mVerts != NULL ) 932 { 933 const S32 sizeClass = _getSizeClass( inst ); 934 935 if ( sizeClass == -1 ) 936 dFree( inst->mVerts ); 937 else 938 { 939 // Use FreeListChunker 940 mChunkers[sizeClass]->free( inst->mVerts ); 941 } 942 943 inst->mVerts = NULL; 944 inst->mVertCount = 0; 945 inst->mIndices = NULL; 946 inst->mIndxCount = 0; 947 } 948} 949 950void DecalManager::_freePools() 951{ 952 while ( !mVBPool.empty() ) 953 { 954 delete mVBPool.last(); 955 mVBPool.pop_back(); 956 } 957 958 while ( !mVBs.empty() ) 959 { 960 delete mVBs.last(); 961 mVBs.pop_back(); 962 } 963 964 while ( !mPBPool.empty() ) 965 { 966 delete mPBPool.last(); 967 mPBPool.pop_back(); 968 } 969 970 while ( !mPBs.empty() ) 971 { 972 delete mPBs.last(); 973 mPBs.pop_back(); 974 } 975} 976 977S32 DecalManager::_getSizeClass( DecalInstance *inst ) const 978{ 979 U32 bytes = inst->mVertCount * sizeof( DecalVertex ) + inst->mIndxCount * sizeof ( U16 ); 980 981 if ( bytes <= SIZE_CLASS_0 ) 982 return 0; 983 if ( bytes <= SIZE_CLASS_1 ) 984 return 1; 985 if ( bytes <= SIZE_CLASS_2 ) 986 return 2; 987 988 // Size is outside of the largest chunker. 989 return -1; 990} 991 992void DecalManager::prepRenderImage( SceneRenderState* state ) 993{ 994 PROFILE_SCOPE( DecalManager_RenderDecals ); 995 996 if ( !smDecalsOn || !mData ) 997 return; 998 999 // Decals only render in the diffuse pass! 1000 // We technically could render them into reflections but prefer to save 1001 // the performance. This would also break the DecalInstance::mLastAlpha 1002 // optimization. 1003 if ( !state->isDiffusePass() ) 1004 return; 1005 1006 PROFILE_START( DecalManager_RenderDecals_SphereTreeCull ); 1007 1008 const Frustum& rootFrustum = state->getCameraFrustum(); 1009 1010 // Populate vector of decal instances to be rendered with all 1011 // decals from visible decal spheres. 1012 1013 SceneManager* sceneManager = state->getSceneManager(); 1014 SceneZoneSpaceManager* zoneManager = sceneManager->getZoneManager(); 1015 AssertFatal( zoneManager, "DecalManager::prepRenderImage - No zone manager!" ); 1016 const Vector<DecalSphere*> &grid = mData->getSphereList(); 1017 const bool haveOnlyOutdoorZone = ( zoneManager->getNumActiveZones() == 1 ); 1018 1019 mDecalQueue.clear(); 1020 for ( U32 i = 0; i < grid.size(); i++ ) 1021 { 1022 DecalSphere* decalSphere = grid[i]; 1023 const SphereF& worldSphere = decalSphere->mWorldSphere; 1024 1025 // See if this decal sphere can be culled. 1026 1027 const SceneCullingState& cullingState = state->getCullingState(); 1028 if( haveOnlyOutdoorZone ) 1029 { 1030 U32 outdoorZone = SceneZoneSpaceManager::RootZoneId; 1031 if( cullingState.isCulled( worldSphere, &outdoorZone, 1 ) ) 1032 continue; 1033 } 1034 else 1035 { 1036 // Update the zoning state of the sphere, if we need to. 1037 1038 if( decalSphere->mZones.size() == 0 ) 1039 decalSphere->updateZoning( zoneManager ); 1040 1041 // Skip the sphere if it is not visible in any of its zones. 1042 1043 if( cullingState.isCulled( worldSphere, decalSphere->mZones.address(), decalSphere->mZones.size() ) ) 1044 continue; 1045 } 1046 1047 // TODO: If each sphere stored its largest decal instance we 1048 // could do an LOD step on it here and skip adding any of the 1049 // decals in the sphere. 1050 1051 mDecalQueue.merge( decalSphere->mItems ); 1052 } 1053 1054 PROFILE_END(); 1055 1056 PROFILE_START( DecalManager_RenderDecals_Update ); 1057 1058 const U32 &curSimTime = Sim::getCurrentTime(); 1059 F32 pixelSize; 1060 U32 delta, diff; 1061 DecalInstance *dinst; 1062 DecalData *ddata; 1063 1064 // Loop through DecalQueue once for preRendering work. 1065 // 1. Update DecalInstance fade (over time) 1066 // 2. Clip geometry if flagged to do so. 1067 // 3. Calculate lod - if decal is far enough away it will not render. 1068 for ( U32 i = 0; i < mDecalQueue.size(); i++ ) 1069 { 1070 dinst = mDecalQueue[i]; 1071 ddata = dinst->mDataBlock; 1072 1073 // LOD calculation... 1074 1075 pixelSize = dinst->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ); 1076 1077 if ( pixelSize != F32_MAX && pixelSize < ddata->fadeEndPixelSize ) 1078 { 1079 mDecalQueue.erase_fast( i ); 1080 i--; 1081 continue; 1082 } 1083 1084 // We will try to render this decal... so do any 1085 // final adjustments to it before rendering. 1086 1087 // Update fade and delete expired. 1088 if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) ) 1089 { 1090 delta = ( curSimTime - dinst->mCreateTime ); 1091 if ( delta > dinst->mDataBlock->lifeSpan ) 1092 { 1093 diff = delta - dinst->mDataBlock->lifeSpan; 1094 dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime; 1095 1096 if ( dinst->mVisibility <= 0.0f ) 1097 { 1098 mDecalQueue.erase_fast( i ); 1099 removeDecal( dinst ); 1100 i--; 1101 continue; 1102 } 1103 } 1104 } 1105 1106 // Build clipped geometry for this decal if needed. 1107 if ( dinst->mFlags & ClipDecal && !( dinst->mFlags & CustomDecal ) ) 1108 { 1109 // Turn off the flag so we don't continually try to clip 1110 // if it fails. 1111 dinst->mFlags = dinst->mFlags & ~<a href="/coding/file/decalmanager_8h/#decalmanager_8h_1a6024669615269f127b83f29f8745d0cfae5438cbd50e5d55e898774bca5cd6c98">ClipDecal</a>; 1112 1113 if ( !(dinst->mFlags & CustomDecal) && !clipDecal( dinst ) ) 1114 { 1115 // Clipping failed to get any geometry... 1116 1117 // Remove it from the render queue. 1118 mDecalQueue.erase_fast( i ); 1119 i--; 1120 1121 // If the decal is one placed at run-time (not the editor) 1122 // then we should also permanently delete the decal instance. 1123 if ( !(dinst->mFlags & SaveDecal) ) 1124 { 1125 removeDecal( dinst ); 1126 } 1127 1128 // If this is a decal placed by the editor it will be 1129 // flagged to attempt clipping again the next time it is 1130 // modified. For now we just skip rendering it. 1131 continue; 1132 } 1133 } 1134 1135 // If we get here and the decal still does not have any geometry 1136 // skip rendering it. It must be an editor placed decal that failed 1137 // to clip any geometry but has not yet been flagged to try again. 1138 if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 ) 1139 { 1140 mDecalQueue.erase_fast( i ); 1141 i--; 1142 continue; 1143 } 1144 1145 // Calculate the alpha value for this decal and apply it to the verts. 1146 { 1147 PROFILE_START( DecalManager_RenderDecals_Update_SetAlpha ); 1148 1149 F32 alpha = 1.0f; 1150 1151 // Only necessary for decals which fade over time or distance. 1152 if ( !( dinst->mFlags & PermanentDecal ) || dinst->mDataBlock->fadeStartPixelSize >= 0.0f ) 1153 { 1154 if ( pixelSize < ddata->fadeStartPixelSize ) 1155 { 1156 const F32 range = ddata->fadeStartPixelSize - ddata->fadeEndPixelSize; 1157 alpha = 1.0f - mClampF( ( ddata->fadeStartPixelSize - pixelSize ) / range, 0.0f, 1.0f ); 1158 } 1159 1160 alpha *= dinst->mVisibility; 1161 } 1162 1163 // If the alpha value has not changed since last render avoid 1164 // looping through all the verts! 1165 if ( alpha != dinst->mLastAlpha ) 1166 { 1167 // calculate the swizzles color once, outside the loop. 1168 GFXVertexColor color; 1169 color.set( 255, 255, 255, (U8)(alpha * 255.0f) ); 1170 1171 for ( U32 v = 0; v < dinst->mVertCount; v++ ) 1172 dinst->mVerts[v].color = color; 1173 1174 dinst->mLastAlpha = alpha; 1175 } 1176 1177 PROFILE_END(); 1178 } 1179 } 1180 1181 PROFILE_END(); 1182 1183 if ( mDecalQueue.empty() ) 1184 return; 1185 1186 // Sort queued decals... 1187 // 1. Editor decals - in render priority order first, creation time second, and material third. 1188 // 2. Dynamic decals - in render priority order first and creation time second. 1189 // 1190 // With the constraint that decals with different render priority cannot 1191 // be rendered together in the same draw call. 1192 1193 PROFILE_START( DecalManager_RenderDecals_Sort ); 1194 dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder ); 1195 PROFILE_END(); 1196 1197 PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch ); 1198 1199 RenderPassManager *renderPass = state->getRenderPass(); 1200 1201 // Base render instance we use for convenience. 1202 // Data shared by all instances we allocate below can be copied 1203 // from the base instance at the same time. 1204 MeshRenderInst baseRenderInst; 1205 baseRenderInst.clear(); 1206 1207 MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) ); 1208 MathUtils::getZBiasProjectionMatrix( gDecalBias, rootFrustum, tempMat ); 1209 baseRenderInst.projection = tempMat; 1210 1211 baseRenderInst.objectToWorld = &MatrixF::Identity; 1212 baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); 1213 1214 baseRenderInst.type = RenderPassManager::RIT_Decal; 1215 1216 // Make it the sort distance the max distance so that 1217 // it renders after all the other opaque geometry in 1218 // the deferred bin. 1219 baseRenderInst.sortDistSq = F32_MAX; 1220 1221 Vector<DecalBatch> batches; 1222 DecalBatch *currentBatch = NULL; 1223 1224 // Loop through DecalQueue collecting them into render batches. 1225 for ( U32 i = 0; i < mDecalQueue.size(); i++ ) 1226 { 1227 DecalInstance *decal = mDecalQueue[i]; 1228 DecalData *data = decal->mDataBlock; 1229 Material *mat = data->getMaterial(); 1230 1231 if ( currentBatch == NULL ) 1232 { 1233 // Start a new batch, beginning with this decal. 1234 1235 batches.increment(); 1236 currentBatch = &batches.last(); 1237 currentBatch->startDecal = i; 1238 currentBatch->decalCount = 1; 1239 1240 // Shrink and warning: preventing a potential crash. 1241 currentBatch->iCount = 1242 (decal->mIndxCount > smMaxIndices) ? smMaxIndices : decal->mIndxCount; 1243 currentBatch->vCount = 1244 (decal->mVertCount > smMaxVerts) ? smMaxVerts : decal->mVertCount; 1245#ifdef TORQUE_DEBUG 1246 // we didn't mean send a spam to the console 1247 static U32 countMsgIndx = 0; 1248 if ( (decal->mIndxCount > smMaxIndices) && ((countMsgIndx++ % 1024) == 0) ) { 1249 Con::warnf( 1250 "DecalManager::prepRenderImage() - Shrinked indices of decal." 1251 " Lost %u.", (decal->mIndxCount - smMaxIndices) 1252 ); 1253 } 1254 static U32 countMsgVert = 0; 1255 if ( (decal->mVertCount > smMaxVerts) && ((countMsgVert++ % 1024) == 0) ) { 1256 Con::warnf( 1257 "DecalManager::prepRenderImage() - Shrinked vertices of decal." 1258 " Lost %u.", (decal->mVertCount - smMaxVerts) 1259 ); 1260 } 1261#endif 1262 1263 currentBatch->mat = mat; 1264 currentBatch->matInst = decal->mDataBlock->getMaterialInstance(); 1265 currentBatch->priority = decal->getRenderPriority(); 1266 currentBatch->dynamic = !(decal->mFlags & SaveDecal); 1267 1268 continue; 1269 } 1270 1271 if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices || 1272 currentBatch->vCount + decal->mVertCount >= smMaxVerts || 1273 currentBatch->mat != mat || 1274 currentBatch->priority != decal->getRenderPriority() || 1275 decal->mCustomTex ) 1276 { 1277 // End batch. 1278 1279 currentBatch = NULL; 1280 i--; 1281 continue; 1282 } 1283 1284 // Add on to current batch. 1285 currentBatch->decalCount++; 1286 currentBatch->iCount += decal->mIndxCount; 1287 currentBatch->vCount += decal->mVertCount; 1288 } 1289 1290 // Make sure our primitive and vertex buffer handle storage is 1291 // big enough to take all batches. Doing this now avoids reallocation 1292 // later on which would invalidate all the pointers we already had 1293 // passed into render instances. 1294 1295 //mPBs.reserve( batches.size() ); 1296 //mVBs.reserve( batches.size() ); 1297 1298 // System memory array of verts and indices so we can fill them incrementally 1299 // and then memcpy to the graphics device buffers in one call. 1300 static DecalVertex vertData[smMaxVerts]; 1301 static U16 indexData[smMaxIndices]; 1302 1303 // Loop through batches allocating buffers and submitting render instances. 1304 for ( U32 i = 0; i < batches.size(); i++ ) 1305 { 1306 currentBatch = &batches[i]; 1307 1308 // Copy data into the system memory arrays, from all decals in this batch... 1309 1310 DecalVertex *vpPtr = vertData; 1311 U16 *pbPtr = indexData; 1312 1313 U32 lastDecal = currentBatch->startDecal + currentBatch->decalCount; 1314 1315 U32 voffset = 0; 1316 U32 ioffset = 0; 1317 1318 // This is an ugly hack for ProjectedShadow! 1319 GFXTextureObject *customTex = NULL; 1320 1321 for ( U32 j = currentBatch->startDecal; j < lastDecal; j++ ) 1322 { 1323 dinst = mDecalQueue[j]; 1324 1325 const U32 indxCount = 1326 (dinst->mIndxCount > currentBatch->iCount) ? 1327 currentBatch->iCount : dinst->mIndxCount; 1328 for ( U32 k = 0; k < indxCount; k++ ) 1329 { 1330 *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset; 1331 } 1332 1333 ioffset += indxCount; 1334 1335 const U32 vertCount = 1336 (dinst->mVertCount > currentBatch->vCount) ? 1337 currentBatch->vCount : dinst->mVertCount; 1338 dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * vertCount ); 1339 voffset += vertCount; 1340 1341 // Ugly hack for ProjectedShadow! 1342 if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL ) 1343 customTex = *dinst->mCustomTex; 1344 } 1345 1346 AssertFatal( ioffset == currentBatch->iCount, "bad" ); 1347 AssertFatal( voffset == currentBatch->vCount, "bad" ); 1348 1349 // Get handles to video memory buffers we will be filling... 1350 1351 GFXVertexBufferHandle<DecalVertex> *vb = NULL; 1352 1353 if ( mVBPool.empty() ) 1354 { 1355 // If the Pool is empty allocate a new one. 1356 vb = new GFXVertexBufferHandle<DecalVertex>; 1357 vb->set( GFX, smMaxVerts, GFXBufferTypeDynamic ); 1358 } 1359 else 1360 { 1361 // Otherwise grab from the pool. 1362 vb = mVBPool.last(); 1363 mVBPool.pop_back(); 1364 } 1365 1366 // Push into our vector of 'in use' buffers. 1367 mVBs.push_back( vb ); 1368 1369 // Ready to start filling. 1370 vpPtr = vb->lock(); 1371 1372 // Same deal as above... 1373 GFXPrimitiveBufferHandle *pb = NULL; 1374 if ( mPBPool.empty() ) 1375 { 1376 pb = new GFXPrimitiveBufferHandle; 1377 pb->set( GFX, smMaxIndices, 0, GFXBufferTypeDynamic ); 1378 } 1379 else 1380 { 1381 pb = mPBPool.last(); 1382 mPBPool.pop_back(); 1383 } 1384 mPBs.push_back( pb ); 1385 1386 pb->lock( &pbPtr ); 1387 1388 // Memcpy from system to video memory. 1389 const U32 vpCount = sizeof( DecalVertex ) * currentBatch->vCount; 1390 dMemcpy( vpPtr, vertData, vpCount ); 1391 const U32 pbCount = sizeof( U16 ) * currentBatch->iCount; 1392 dMemcpy( pbPtr, indexData, pbCount ); 1393 1394 pb->unlock(); 1395 vb->unlock(); 1396 1397 // DecalManager must hold handles to these buffers so they remain valid, 1398 // we don't actually use them elsewhere. 1399 //mPBs.push_back( pb ); 1400 //mVBs.push_back( vb ); 1401 1402 // Get the best lights for the current camera position 1403 // if the materail is forward lit and we haven't got them yet. 1404 if ( currentBatch->matInst->isForwardLit() && !baseRenderInst.lights[0] ) 1405 { 1406 LightQuery query; 1407 query.init( rootFrustum.getPosition(), 1408 rootFrustum.getTransform().getForwardVector(), 1409 rootFrustum.getFarDist() ); 1410 query.getLights( baseRenderInst.lights, 8 ); 1411 } 1412 1413 // Submit render inst... 1414 MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>(); 1415 *ri = baseRenderInst; 1416 1417 ri->primBuff = pb; 1418 ri->vertBuff = vb; 1419 1420 ri->matInst = currentBatch->matInst; 1421 1422 ri->prim = renderPass->allocPrim(); 1423 ri->prim->type = GFXTriangleList; 1424 ri->prim->minIndex = 0; 1425 ri->prim->startIndex = 0; 1426 ri->prim->numPrimitives = currentBatch->iCount / 3; 1427 ri->prim->startVertex = 0; 1428 ri->prim->numVertices = currentBatch->vCount; 1429 1430 // Ugly hack for ProjectedShadow! 1431 if ( customTex ) 1432 ri->miscTex = customTex; 1433 1434 // The decal bin will contain render instances for both decals and decalRoad's. 1435 // Dynamic decals render last, then editor decals and roads in priority order. 1436 // DefaultKey is sorted in descending order. 1437 ri->defaultKey = currentBatch->dynamic ? 0xFFFFFFFF : (U32)currentBatch->priority; 1438 ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock; 1439 1440 renderPass->addInst( ri ); 1441 } 1442 1443#ifdef TORQUE_GATHER_METRICS 1444 Con::setIntVariable( "$Decal::Batches", batches.size() ); 1445 Con::setIntVariable( "$Decal::Buffers", mPBs.size() + mPBPool.size() ); 1446 Con::setIntVariable( "$Decal::DecalsRendered", mDecalQueue.size() ); 1447#endif 1448 1449 if( smDebugRender && gEditingMission ) 1450 { 1451 ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >(); 1452 1453 ri->renderDelegate.bind( this, &DecalManager::_renderDecalSpheres ); 1454 ri->type = RenderPassManager::RIT_Editor; 1455 ri->defaultKey = 0; 1456 ri->defaultKey2 = 0; 1457 1458 state->getRenderPass()->addInst( ri ); 1459 } 1460} 1461 1462void DecalManager::_renderDecalSpheres( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ) 1463{ 1464 if( !mData ) 1465 return; 1466 1467 const Vector<DecalSphere*> &grid = mData->getSphereList(); 1468 1469 GFXDrawUtil *drawUtil = GFX->getDrawUtil(); 1470 ColorI sphereColor( 0, 255, 0, 45 ); 1471 1472 GFXStateBlockDesc desc; 1473 desc.setBlend( true ); 1474 desc.setZReadWrite( true, false ); 1475 desc.setCullMode( GFXCullNone ); 1476 1477 for ( U32 i = 0; i < grid.size(); i++ ) 1478 { 1479 DecalSphere *decalSphere = grid[i]; 1480 const SphereF &worldSphere = decalSphere->mWorldSphere; 1481 1482 if( state->getCullingFrustum().isCulled( worldSphere ) ) 1483 continue; 1484 1485 drawUtil->drawSphere( desc, worldSphere.radius, worldSphere.center, sphereColor ); 1486 } 1487} 1488 1489bool DecalManager::_createDataFile() 1490{ 1491 AssertFatal( !mData, "DecalManager::tried to create duplicate data file?" ); 1492 1493 // We need to construct a default file name 1494 char fileName[1024]; 1495 fileName[0] = 0; 1496 1497 // See if we know our current mission name 1498 char missionName[1024]; 1499 dStrcpy( missionName, Con::getVariable( "$Client::MissionFile" ), 1024 ); 1500 char *dot = dStrstr((const char*)missionName, ".mis"); 1501 if(dot) 1502 *dot = '\0'; 1503 1504 dSprintf( fileName, sizeof(fileName), "%s.mis.decals", missionName ); 1505 1506 mDataFileName = StringTable->insert( fileName ); 1507 1508 // If the file doesn't exist, create an empty file. 1509 1510 if( !Torque::FS::IsFile( fileName ) ) 1511 { 1512 FileStream stream; 1513 if( stream.open( mDataFileName, Torque::FS::File::Write ) ) 1514 { 1515 DecalDataFile dataFile; 1516 dataFile.write( stream ); 1517 } 1518 } 1519 1520 mData = ResourceManager::get().load( mDataFileName ); 1521 return (bool)mData; 1522} 1523 1524void DecalManager::saveDecals( const UTF8* fileName ) 1525{ 1526 if( !mData ) 1527 return; 1528 1529 // Create the file. 1530 1531 FileStream stream; 1532 if ( !stream.open( fileName, Torque::FS::File::Write ) ) 1533 { 1534 Con::errorf( "DecalManager::saveDecals - Could not open '%s' for writing!", fileName ); 1535 return; 1536 } 1537 1538 // Write the data. 1539 1540 if( !mData->write( stream ) ) 1541 { 1542 Con::errorf( "DecalManager::saveDecals - Failed to write '%s'", fileName ); 1543 return; 1544 } 1545 1546 mDirty = false; 1547} 1548 1549bool DecalManager::loadDecals( const UTF8 *fileName ) 1550{ 1551 if( mData ) 1552 clearData(); 1553 1554 mData = ResourceManager::get().load( fileName ); 1555 1556 mDirty = false; 1557 1558 return mData != NULL; 1559} 1560 1561void DecalManager::clearData() 1562{ 1563 mClearDataSignal.trigger(); 1564 1565 // Free all geometry buffers. 1566 1567 if( mData ) 1568 { 1569 const Vector< DecalSphere*> grid = mData->getSphereList(); 1570 for( U32 i = 0; i < grid.size(); ++ i ) 1571 { 1572 DecalSphere* sphere = grid[ i ]; 1573 for( U32 n = 0; n < sphere->mItems.size(); ++ n ) 1574 _freeBuffers( sphere->mItems[ n ] ); 1575 } 1576 } 1577 1578 mData = NULL; 1579 mDecalInstanceVec.clear(); 1580 1581 _freePools(); 1582} 1583 1584bool DecalManager::onSceneAdd() 1585{ 1586 if( !Parent::onSceneAdd() ) 1587 return false; 1588 1589 SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &DecalManager::_handleZoningChangedEvent ); 1590 1591 return true; 1592} 1593 1594void DecalManager::onSceneRemove() 1595{ 1596 SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &DecalManager::_handleZoningChangedEvent ); 1597 Parent::onSceneRemove(); 1598} 1599 1600void DecalManager::_handleZoningChangedEvent( SceneZoneSpaceManager* zoneManager ) 1601{ 1602 if( zoneManager != getSceneManager()->getZoneManager() || !getDecalDataFile() ) 1603 return; 1604 1605 // Clear the zoning state of all DecalSpheres in the data file. 1606 1607 const Vector< DecalSphere*> grid = getDecalDataFile()->getSphereList(); 1608 const U32 numSpheres = grid.size(); 1609 1610 for( U32 i = 0; i < numSpheres; ++ i ) 1611 grid[ i ]->mZones.clear(); 1612} 1613 1614DefineEngineFunction( decalManagerSave, void, ( String decalSaveFile ), ( "" ), 1615 "Saves the decals for the active mission in the entered filename.\n" 1616 "@param decalSaveFile Filename to save the decals to.\n" 1617 "@tsexample\n" 1618 "// Set the filename to save the decals in. If no filename is set, then the\n" 1619 "// decals will default to <activeMissionName>.mis.decals\n" 1620 "%fileName = \"./missionDecals.mis.decals\";\n" 1621 "// Inform the decal manager to save the decals for the active mission.\n" 1622 "decalManagerSave( %fileName );\n" 1623 "@endtsexample\n" 1624 "@ingroup Decals" ) 1625{ 1626 // If not given a file name, synthesize one. 1627 1628 if( decalSaveFile.isEmpty() ) 1629 { 1630 String fileName = String::ToString( "%s.decals", Con::getVariable( "$Client::MissionFile" ) ); 1631 1632 char fullName[ 4096 ]; 1633 Platform::makeFullPathName( fileName, fullName, sizeof( fullName ) ); 1634 1635 decalSaveFile = String( fullName ); 1636 } 1637 1638 // Write the data. 1639 1640 gDecalManager->saveDecals( decalSaveFile ); 1641} 1642 1643DefineEngineFunction( decalManagerLoad, bool, ( const char* fileName ),, 1644 "Clears existing decals and replaces them with decals loaded from the specified file.\n" 1645 "@param fileName Filename to load the decals from.\n" 1646 "@return True if the decal manager was able to load the requested file, " 1647 "false if it could not.\n" 1648 "@tsexample\n" 1649 "// Set the filename to load the decals from.\n" 1650 "%fileName = \"./missionDecals.mis.decals\";\n" 1651 "// Inform the decal manager to load the decals from the entered filename.\n" 1652 "decalManagerLoad( %fileName );\n" 1653 "@endtsexample\n" 1654 "@ingroup Decals" ) 1655{ 1656 return gDecalManager->loadDecals( fileName ); 1657} 1658 1659DefineEngineFunction( decalManagerDirty, bool, (),, 1660 "Returns whether the decal manager has unsaved modifications.\n" 1661 "@return True if the decal manager has unsaved modifications, false if " 1662 "everything has been saved.\n" 1663 "@tsexample\n" 1664 "// Ask the decal manager if it has unsaved modifications.\n" 1665 "%hasUnsavedModifications = decalManagerDirty();\n" 1666 "@endtsexample\n" 1667 "@ingroup Decals" ) 1668{ 1669 return gDecalManager->isDirty(); 1670} 1671 1672DefineEngineFunction( decalManagerClear, void, (),, 1673 "Removes all decals currently loaded in the decal manager.\n" 1674 "@tsexample\n" 1675 "// Tell the decal manager to remove all existing decals.\n" 1676 "decalManagerClear();\n" 1677 "@endtsexample\n" 1678 "@ingroup Decals" ) 1679{ 1680 gDecalManager->clearData(); 1681} 1682 1683DefineEngineFunction( decalManagerAddDecal, S32, 1684 ( Point3F position, Point3F normal, F32 rot, F32 scale, DecalData* decalData, bool isImmortal), ( false ), 1685 "Adds a new decal to the decal manager.\n" 1686 "@param position World position for the decal.\n" 1687 "@param normal Decal normal vector (if the decal was a tire lying flat on a " 1688 "surface, this is the vector pointing in the direction of the axle).\n" 1689 "@param rot Angle (in radians) to rotate this decal around its normal vector.\n" 1690 "@param scale Scale factor applied to the decal.\n" 1691 "@param decalData DecalData datablock to use for the new decal.\n" 1692 "@param isImmortal Whether or not this decal is immortal. If immortal, it " 1693 "does not expire automatically and must be removed explicitly.\n" 1694 "@return Returns the ID of the new Decal object or -1 on failure.\n" 1695 "@tsexample\n" 1696 "// Specify the decal position\n" 1697 "%position = \"1.0 1.0 1.0\";\n\n" 1698 "// Specify the up vector\n" 1699 "%normal = \"0.0 0.0 1.0\";\n\n" 1700 "// Add the new decal.\n" 1701 "%decalObj = decalManagerAddDecal( %position, %normal, 0.5, 0.35, ScorchBigDecal, false );\n" 1702 "@endtsexample\n" 1703 "@ingroup Decals" ) 1704{ 1705 if( !decalData ) 1706 { 1707 Con::errorf( "decalManagerAddDecal - Invalid Decal DataBlock" ); 1708 return -1; 1709 } 1710 1711 U8 flags = 0; 1712 if( isImmortal ) 1713 flags |= PermanentDecal; 1714 1715 DecalInstance* inst = gDecalManager->addDecal( position, normal, rot, decalData, scale, -1, flags ); 1716 if( !inst ) 1717 { 1718 Con::errorf( "decalManagerAddDecal - Unable to create decal instance." ); 1719 return -1; 1720 } 1721 1722 // Add the decal to the instance vector. 1723 inst->mId = gDecalManager->mDecalInstanceVec.size(); 1724 gDecalManager->mDecalInstanceVec.push_back( inst ); 1725 1726 return inst->mId; 1727} 1728 1729DefineEngineFunction( decalManagerRemoveDecal, bool, ( S32 decalID ),, 1730 "Remove specified decal from the scene.\n" 1731 "@param decalID ID of the decal to remove.\n" 1732 "@return Returns true if successful, false if decal ID not found.\n" 1733 "@tsexample\n" 1734 "// Specify a decal ID to be removed\n" 1735 "%decalID = 1;\n\n" 1736 "// Tell the decal manager to remove the specified decal ID.\n" 1737 "decalManagerRemoveDecal( %decalId )\n" 1738 "@endtsexample\n" 1739 "@ingroup Decals" ) 1740{ 1741 DecalInstance *inst = gDecalManager->getDecal( decalID ); 1742 if( !inst ) 1743 return false; 1744 1745 gDecalManager->removeDecal(inst); 1746 return true; 1747} 1748 1749DefineEngineFunction( decalManagerEditDecal, bool, ( S32 decalID, Point3F pos, Point3F normal, F32 rotAroundNormal, F32 decalScale ),, 1750 "Edit specified decal of the decal manager.\n" 1751 "@param decalID ID of the decal to edit.\n" 1752 "@param pos World position for the decal.\n" 1753 "@param normal Decal normal vector (if the decal was a tire lying flat on a " 1754 "surface, this is the vector pointing in the direction of the axle).\n" 1755 "@param rotAroundNormal Angle (in radians) to rotate this decal around its normal vector.\n" 1756 "@param decalScale Scale factor applied to the decal.\n" 1757 "@return Returns true if successful, false if decalID not found.\n" 1758 "" ) 1759{ 1760 DecalInstance *decalInstance = gDecalManager->getDecal( decalID ); 1761 if( !decalInstance ) 1762 return false; 1763 1764 //Internally we need Point3F tangent instead of the user friendly F32 rotAroundNormal 1765 MatrixF mat( true ); 1766 MathUtils::getMatrixFromUpVector( normal, &mat ); 1767 1768 AngAxisF rot( normal, rotAroundNormal ); 1769 MatrixF rotmat; 1770 rot.setMatrix( &rotmat ); 1771 mat.mul( rotmat ); 1772 1773 Point3F tangent; 1774 mat.getColumn( 1, &tangent ); 1775 1776 //if everything is unchanged just do nothing and return "everything is ok" 1777 if ( pos.equal(decalInstance->mPosition) && 1778 normal.equal(decalInstance->mNormal) && 1779 tangent.equal(decalInstance->mTangent) && 1780 mFabs( decalInstance->mSize - (decalInstance->mDataBlock->size * decalScale) ) < POINT_EPSILON ) 1781 return true; 1782 1783 decalInstance->mPosition = pos; 1784 decalInstance->mNormal = normal; 1785 decalInstance->mTangent = tangent; 1786 decalInstance->mSize = decalInstance->mDataBlock->size * decalScale; 1787 1788 gDecalManager->clipDecal( decalInstance, NULL, NULL); 1789 1790 gDecalManager->notifyDecalModified( decalInstance ); 1791 return true; 1792} 1793