explosion.cpp

Engine/source/T3D/fx/explosion.cpp

More...

Public Defines

define

Public Variables

sgRandom (0xdeadbeef)

Public Functions

ConsoleDocClass(Explosion , "@brief The emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an explosion effect, with properties defined by a " "<a href="/coding/class/classexplosiondata/">ExplosionData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "The object will initiate the explosion effects automatically after being " "added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">simulation.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>(GrenadeSubExplosion)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " offset=0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " emitter[0]=GrenadeExpSparkEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " lightStartRadius=4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightEndRadius=0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightStartColor=\"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>( GrenadeLauncherExplosion )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   soundProfile = GrenadeLauncherExplosionSound;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lifeTimeMS = 400; // Quick flash, short burn, and moderate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dispersal\n\n</a>" "   // Volume <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">particles\n</a>" "   particleEmitter = GrenadeExpFireEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleDensity = 75;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleRadius = 2.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Point <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">emission\n</a>" "   emitter[0] = GrenadeExpDustEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[1] = GrenadeExpSparksEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[2] = GrenadeExpSmokeEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Sub explosion <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects\n</a>" "   subExplosion[0] = GrenadeSubExplosion;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // <a href="/coding/class/classcamera/">Camera</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Shaking\n</a>" "   shakeCamera = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeFreq = \"10.0 11.0 9.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeAmp = \"15.0 15.0 15.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeDuration = 1.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeRadius = 20;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Exploding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">debris\n</a>" "   debris = GrenadeDebris;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMin = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMax = 60;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNum = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNumVariance = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocity = 25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocityVariance = 5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   lightStartRadius = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndRadius = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightNormalOffset = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function ServerPlayExplosion(%position, %datablock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play the given explosion on every <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   // The explosion will be transmitted as an event, not attached <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "   <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a>(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> = 0; %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> < ClientGroup.getCount(); %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>++)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      %client = ClientGroup.getObject(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      commandToClient(%client, 'PlayExplosion', %position, %datablock.getId());\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function clientCmdPlayExplosion(%position, %effectDataBlock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play an explosion sent by the server. Make sure this function is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">defined\n</a>" "   // on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> (isObject(%effectDataBlock))\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classexplosion/">Explosion</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         position = %position;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         dataBlock = %effectDataBlock;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      };\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "// schedule an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion\n</a>" "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )
ConsoleDocClass(ExplosionData , "@brief Defines the attributes of an Explosion: particleEmitters, debris , " "lighting and camera shake <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
DefineEngineFunction(calcExplosionCoverage , F32 , (Point3F pos, S32 id, U32 covMask) , "@brief Calculates how much an explosion effects a specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "Use this <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> determine how much damage <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> objects based on their " "distance from the explosion's center point, and whether the explosion is " "blocked by other <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" " @param pos Center position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @param <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> Id of the object of which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coverage.\n</a>" " @param covMask Mask of object types that may block the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @return Coverage value from 0(not affected by the explosion) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1(fully affected)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Get the position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" "% position)

Detailed Description

Public Defines

MaxLightRadius() 20

Public Variables

MRandomLCG sgRandom (0xdeadbeef)

Public Functions

ConsoleDocClass(Explosion , "@brief The emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an explosion effect, with properties defined by a " "<a href="/coding/class/classexplosiondata/">ExplosionData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" "The object will initiate the explosion effects automatically after being " "added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">simulation.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>(GrenadeSubExplosion)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " offset=0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " emitter[0]=GrenadeExpSparkEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " lightStartRadius=4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightEndRadius=0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lightStartColor=\"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"0.9 0.7 0.7\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>( GrenadeLauncherExplosion )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   soundProfile = GrenadeLauncherExplosionSound;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lifeTimeMS = 400; // Quick flash, short burn, and moderate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dispersal\n\n</a>" "   // Volume <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">particles\n</a>" "   particleEmitter = GrenadeExpFireEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleDensity = 75;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   particleRadius = 2.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Point <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">emission\n</a>" "   emitter[0] = GrenadeExpDustEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[1] = GrenadeExpSparksEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   emitter[2] = GrenadeExpSmokeEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Sub explosion <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects\n</a>" "   subExplosion[0] = GrenadeSubExplosion;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // <a href="/coding/class/classcamera/">Camera</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Shaking\n</a>" "   shakeCamera = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeFreq = \"10.0 11.0 9.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeAmp = \"15.0 15.0 15.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeDuration = 1.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   camShakeRadius = 20;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   // Exploding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">debris\n</a>" "   debris = GrenadeDebris;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMin = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisThetaMax = 60;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNum = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisNumVariance = 2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocity = 25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   debrisVelocityVariance = 5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "   lightStartRadius = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndRadius = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndColor = \"1.0 1.0 1.0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightStartBrightness = 4.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightEndBrightness = 0.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   lightNormalOffset = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function ServerPlayExplosion(%position, %datablock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play the given explosion on every <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   // The explosion will be transmitted as an event, not attached <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "   <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a>(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> = 0; %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a> < ClientGroup.getCount(); %<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>++)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      %client = ClientGroup.getObject(%<a href="/coding/file/mquat_8cpp/#mquat_8cpp_1ad66b7690eb5b70f95cb7cb584e91c9ea">idx</a>);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      commandToClient(%client, 'PlayExplosion', %position, %datablock.getId());\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "function clientCmdPlayExplosion(%position, %effectDataBlock)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   // Play an explosion sent by the server. Make sure this function is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">defined\n</a>" "   // on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n</a>" "   <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> (isObject(%effectDataBlock))\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classexplosion/">Explosion</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         position = %position;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "         dataBlock = %effectDataBlock;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "      };\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   }\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "}\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "// schedule an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion\n</a>" "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@endtsexample" )

ConsoleDocClass(ExplosionData , "@brief Defines the attributes of an Explosion: particleEmitters, debris , " "lighting and camera shake <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )

DefineEngineFunction(calcExplosionCoverage , F32 , (Point3F pos, S32 id, U32 covMask) , "@brief Calculates how much an explosion effects a specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "Use this <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> determine how much damage <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> objects based on their " "distance from the explosion's center point, and whether the explosion is " "blocked by other <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" " @param pos Center position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @param <a href="/coding/file/win32cursorcontroller_8cpp/#win32cursorcontroller_8cpp_1ab38592509822a5f4674447022cc62efe">id</a> Id of the object of which <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coverage.\n</a>" " @param covMask Mask of object types that may block the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" " @return Coverage value from 0(not affected by the explosion) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1(fully affected)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Get the position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">explosion.\n</a>" "% position)

IMPLEMENT_CO_DATABLOCK_V1(ExplosionData )

IMPLEMENT_CONOBJECT(Explosion )

   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/fx/explosion.h"
  26
  27#include "core/resourceManager.h"
  28#include "console/consoleTypes.h"
  29#include "console/typeValidators.h"
  30#include "sfx/sfxSystem.h"
  31#include "sfx/sfxTrack.h"
  32#include "sfx/sfxTypes.h"
  33#include "scene/sceneManager.h"
  34#include "scene/sceneRenderState.h"
  35#include "lighting/lightInfo.h"
  36#include "lighting/lightManager.h"
  37#include "core/stream/bitStream.h"
  38#include "sim/netConnection.h"
  39#include "ts/tsShape.h"
  40#include "ts/tsShapeInstance.h"
  41#include "math/mRandom.h"
  42#include "math/mathIO.h"
  43#include "math/mathUtils.h"
  44#include "T3D/debris.h"
  45#include "T3D/gameBase/gameConnection.h"
  46#include "T3D/fx/particleEmitter.h"
  47#include "T3D/fx/cameraFXMgr.h"
  48#include "T3D/debris.h"
  49#include "T3D/shapeBase.h"
  50#include "T3D/gameBase/gameProcess.h"
  51#include "renderInstance/renderPassManager.h"
  52#include "console/engineAPI.h"
  53
  54IMPLEMENT_CONOBJECT(Explosion);
  55
  56ConsoleDocClass( Explosion,
  57   "@brief The emitter for an explosion effect, with properties defined by a "
  58   "ExplosionData object.\n\n"
  59   "@ingroup FX\n"
  60   "The object will initiate the explosion effects automatically after being "
  61   "added to the simulation.\n"
  62   "@tsexample\n"
  63   "datablock ExplosionData( GrenadeSubExplosion )\n"
  64   "{\n"
  65   "   offset = 0.25;\n"
  66   "   emitter[0] = GrenadeExpSparkEmitter;\n\n"
  67   "   lightStartRadius = 4.0;\n"
  68   "   lightEndRadius = 0.0;\n"
  69   "   lightStartColor = \"0.9 0.7 0.7\";\n"
  70   "   lightEndColor = \"0.9 0.7 0.7\";\n"
  71   "   lightStartBrightness = 2.0;\n"
  72   "   lightEndBrightness = 0.0;\n"
  73   "};\n\n"
  74   "datablock ExplosionData( GrenadeLauncherExplosion )\n"
  75   "{\n"
  76   "   soundProfile = GrenadeLauncherExplosionSound;\n"
  77   "   lifeTimeMS = 400; // Quick flash, short burn, and moderate dispersal\n\n"
  78   "   // Volume particles\n"
  79   "   particleEmitter = GrenadeExpFireEmitter;\n"
  80   "   particleDensity = 75;\n"
  81   "   particleRadius = 2.25;\n\n"
  82   "   // Point emission\n"
  83   "   emitter[0] = GrenadeExpDustEmitter;\n"
  84   "   emitter[1] = GrenadeExpSparksEmitter;\n"
  85   "   emitter[2] = GrenadeExpSmokeEmitter;\n\n"
  86   "   // Sub explosion objects\n"
  87   "   subExplosion[0] = GrenadeSubExplosion;\n\n"
  88   "   // Camera Shaking\n"
  89   "   shakeCamera = true;\n"
  90   "   camShakeFreq = \"10.0 11.0 9.0\";\n"
  91   "   camShakeAmp = \"15.0 15.0 15.0\";\n"
  92   "   camShakeDuration = 1.5;\n"
  93   "   camShakeRadius = 20;\n\n"
  94   "   // Exploding debris\n"
  95   "   debris = GrenadeDebris;\n"
  96   "   debrisThetaMin = 10;\n"
  97   "   debrisThetaMax = 60;\n"
  98   "   debrisNum = 4;\n"
  99   "   debrisNumVariance = 2;\n"
 100   "   debrisVelocity = 25;\n"
 101   "   debrisVelocityVariance = 5;\n\n"
 102   "   lightStartRadius = 4.0;\n"
 103   "   lightEndRadius = 0.0;\n"
 104   "   lightStartColor = \"1.0 1.0 1.0\";\n"
 105   "   lightEndColor = \"1.0 1.0 1.0\";\n"
 106   "   lightStartBrightness = 4.0;\n"
 107   "   lightEndBrightness = 0.0;\n"
 108   "   lightNormalOffset = 2.0;\n"
 109   "};\n\n"
 110   "function ServerPlayExplosion(%position, %datablock)\n"
 111   "{\n"
 112   "   // Play the given explosion on every client.\n"
 113   "   // The explosion will be transmitted as an event, not attached to any object.\n"
 114   "   for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n"
 115   "   {\n"
 116   "      %client = ClientGroup.getObject(%idx);\n"
 117   "      commandToClient(%client, 'PlayExplosion', %position, %datablock.getId());\n"
 118   "   }\n"
 119   "}\n\n"
 120   "function clientCmdPlayExplosion(%position, %effectDataBlock)\n"
 121   "{\n"
 122   "   // Play an explosion sent by the server. Make sure this function is defined\n"
 123   "   // on the client.\n"
 124   "   if (isObject(%effectDataBlock))\n"
 125   "   {\n"
 126   "      new Explosion()\n"
 127   "      {\n"
 128   "         position = %position;\n"
 129   "         dataBlock = %effectDataBlock;\n"
 130   "      };\n"
 131   "   }\n"
 132   "}\n\n"
 133   "// schedule an explosion\n"
 134   "schedule(1000, 0, ServerPlayExplosion, \"0 0 0\", GrenadeLauncherExplosion);\n"
 135   "@endtsexample"
 136);
 137
 138#define MaxLightRadius 20
 139
 140MRandomLCG sgRandom(0xdeadbeef);
 141
 142//WLE - Vince - The defaults are bad, the whole point of calling this function\
 143//is to determine the explosion coverage on a object.  Why would you want them
 144//To call this with a null for the ID?  In fact, it just returns a 1f if
 145//it can't find the object.  Seems useless to me.  Cause how can I apply
 146//damage to a object that doesn't exist?
 147
 148//I could possible see a use with passing in a null covMask, but even that
 149//sounds flaky because it will be 100 percent if your saying not to take
 150//any thing in consideration for coverage.  So I'm removing these defaults they are just bad.
 151
 152DefineEngineFunction(calcExplosionCoverage, F32, (Point3F pos, S32 id, U32 covMask),,
 153   "@brief Calculates how much an explosion effects a specific object.\n\n"
 154   "Use this to determine how much damage to apply to objects based on their "
 155   "distance from the explosion's center point, and whether the explosion is "
 156   "blocked by other objects.\n\n"
 157   "@param pos Center position of the explosion.\n"
 158   "@param id Id of the object of which to check coverage.\n"
 159   "@param covMask Mask of object types that may block the explosion.\n"
 160   "@return Coverage value from 0 (not affected by the explosion) to 1 (fully affected)\n\n"
 161   "@tsexample\n"
 162   "// Get the position of the explosion.\n"
 163   "%position = %explosion.getPosition();\n\n"
 164   "// Set a list of TypeMasks (defined in gameFunctioncs.cpp), seperated by the | character.\n"
 165   "%TypeMasks = $TypeMasks::StaticObjectType | $TypeMasks::ItemObjectType\n\n"
 166   "// Acquire the damage value from 0.0f - 1.0f.\n"
 167   "%coverage = calcExplosionCoverage( %position, %sceneObject, %TypeMasks );\n\n"
 168   "// Apply damage to object\n" 
 169   "%sceneObject.applyDamage( %coverage * 20 );\n"
 170   "@endtsexample\n"
 171   "@ingroup FX")
 172{
 173   Point3F center;
 174
 175   SceneObject* sceneObject = NULL;
 176   if (Sim::findObject(id, sceneObject) == false) {
 177      Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: couldn't find object: %d", id);
 178      return 1.0f;
 179   }
 180   if (sceneObject->isClientObject() || sceneObject->getContainer() == NULL) {
 181      Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: object is on the client, or not in the container system");
 182      return 1.0f;
 183   }
 184
 185   sceneObject->getObjBox().getCenter(&center);
 186   center.convolve(sceneObject->getScale());
 187   sceneObject->getTransform().mulP(center);
 188
 189   RayInfo rayInfo;
 190   sceneObject->disableCollision();
 191   if (sceneObject->getContainer()->castRay(pos, center, covMask, &rayInfo) == true) {
 192      // Try casting up and then out
 193      if (sceneObject->getContainer()->castRay(pos, pos + Point3F(0.0f, 0.0f, 1.0f), covMask, &rayInfo) == false)
 194      {
 195         if (sceneObject->getContainer()->castRay(pos + Point3F(0.0f, 0.0f, 1.0f), center, covMask, &rayInfo) == false)
 196         {
 197            sceneObject->enableCollision();
 198            return 1.0f;
 199         }
 200      }
 201
 202      sceneObject->enableCollision();
 203      return 0.0f;
 204   } else {
 205      sceneObject->enableCollision();
 206      return 1.0f;
 207   }
 208}
 209
 210//----------------------------------------------------------------------------
 211//
 212IMPLEMENT_CO_DATABLOCK_V1(ExplosionData);
 213
 214ConsoleDocClass( ExplosionData,
 215   "@brief Defines the attributes of an Explosion: particleEmitters, debris, "
 216   "lighting and camera shake effects.\n"
 217   "@ingroup FX\n"
 218);
 219
 220ExplosionData::ExplosionData()
 221{
 222   dtsFileName  = NULL;
 223   particleDensity = 10;
 224   particleRadius = 1.0f;
 225
 226   faceViewer   = false;
 227
 228   soundProfile      = NULL;
 229   particleEmitter   = NULL;
 230   particleEmitterId = 0;
 231
 232   explosionScale.set(1.0f, 1.0f, 1.0f);
 233   playSpeed = 1.0f;
 234
 235   dMemset( emitterList, 0, sizeof( emitterList ) );
 236   dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
 237   dMemset( debrisList, 0, sizeof( debrisList ) );
 238   dMemset( debrisIDList, 0, sizeof( debrisIDList ) );
 239
 240   debrisThetaMin = 0.0f;
 241   debrisThetaMax = 90.0f;
 242   debrisPhiMin = 0.0f;
 243   debrisPhiMax = 360.0f;
 244   debrisNum = 1;
 245   debrisNumVariance = 0;
 246   debrisVelocity = 2.0f;
 247   debrisVelocityVariance = 0.0f;
 248
 249   dMemset( explosionList, 0, sizeof( explosionList ) );
 250   dMemset( explosionIDList, 0, sizeof( explosionIDList ) );
 251
 252   delayMS = 0;
 253   delayVariance = 0;
 254   lifetimeMS = 1000;
 255   lifetimeVariance = 0;
 256   offset = 0.0f;
 257
 258   shakeCamera = false;
 259   camShakeFreq.set( 10.0f, 10.0f, 10.0f );
 260   camShakeAmp.set( 1.0f, 1.0f, 1.0f );
 261   camShakeDuration = 1.5f;
 262   camShakeRadius = 10.0f;
 263   camShakeFalloff = 10.0f;
 264
 265   for( U32 i=0; i<EC_NUM_TIME_KEYS; i++ )
 266   {
 267      times[i] = 1.0f;
 268   }
 269   times[0] = 0.0f;
 270
 271   for( U32 j=0; j<EC_NUM_TIME_KEYS; j++ )
 272   {
 273      sizes[j].set( 1.0f, 1.0f, 1.0f );
 274   }
 275
 276   //
 277   lightStartRadius = lightEndRadius = 0.0f;
 278   lightStartColor.set(1.0f,1.0f,1.0f);
 279   lightEndColor.set(1.0f,1.0f,1.0f);
 280   lightStartBrightness = 1.0f;
 281   lightEndBrightness = 1.0f;
 282   lightNormalOffset = 0.1f;
 283}
 284
 285void ExplosionData::initPersistFields()
 286{
 287   addField( "explosionShape", TypeShapeFilename, Offset(dtsFileName, ExplosionData),
 288      "@brief Optional DTS or DAE shape to place at the center of the explosion.\n\n"
 289      "The <i>ambient</i> animation of this model will be played automatically at "
 290      "the start of the explosion." );
 291   addField( "explosionScale", TypePoint3F, Offset(explosionScale, ExplosionData),
 292      "\"X Y Z\" scale factor applied to the explosionShape model at the start "
 293      "of the explosion." );
 294   addField( "playSpeed", TypeF32, Offset(playSpeed, ExplosionData),
 295      "Time scale at which to play the explosionShape <i>ambient</i> sequence." );
 296   addField( "soundProfile", TYPEID< SFXTrack >(), Offset(soundProfile, ExplosionData),
 297      "Non-looping sound effect that will be played at the start of the explosion." );
 298   addField( "faceViewer", TypeBool, Offset(faceViewer, ExplosionData),
 299      "Controls whether the visual effects of the explosion always face the camera." );
 300
 301   addField( "particleEmitter", TYPEID< ParticleEmitterData >(), Offset(particleEmitter, ExplosionData),
 302      "@brief Emitter used to generate a cloud of particles at the start of the explosion.\n\n"
 303      "Explosions can generate two different particle effects. The first is a "
 304      "single burst of particles at the start of the explosion emitted in a "
 305      "spherical cloud using particleEmitter.\n\n"
 306      "The second effect spawns the list of ParticleEmitters given by the emitter[] "
 307      "field. These emitters generate particles in the normal way throughout the "
 308      "lifetime of the explosion." );
 309   addField( "particleDensity", TypeS32, Offset(particleDensity, ExplosionData),
 310      "@brief Density of the particle cloud created at the start of the explosion.\n\n"
 311      "@see particleEmitter" );
 312   addField( "particleRadius", TypeF32, Offset(particleRadius, ExplosionData),
 313      "@brief Radial distance from the explosion center at which cloud particles "
 314      "are emitted.\n\n"
 315      "@see particleEmitter" );
 316   addField( "emitter", TYPEID< ParticleEmitterData >(), Offset(emitterList, ExplosionData), EC_NUM_EMITTERS,
 317      "@brief List of additional ParticleEmitterData objects to spawn with this "
 318      "explosion.\n\n"
 319      "@see particleEmitter" );
 320
 321   addField( "debris", TYPEID< DebrisData >(), Offset(debrisList, ExplosionData), EC_NUM_DEBRIS_TYPES,
 322      "List of DebrisData objects to spawn with this explosion." );
 323   addField( "debrisThetaMin", TypeF32, Offset(debrisThetaMin, ExplosionData),
 324      "Minimum angle, from the horizontal plane, to eject debris from." );
 325   addField( "debrisThetaMax", TypeF32, Offset(debrisThetaMax, ExplosionData),
 326      "Maximum angle, from the horizontal plane, to eject debris from." );
 327   addField( "debrisPhiMin", TypeF32, Offset(debrisPhiMin, ExplosionData),
 328      "Minimum reference angle, from the vertical plane, to eject debris from." );
 329   addField( "debrisPhiMax", TypeF32, Offset(debrisPhiMax, ExplosionData),
 330      "Maximum reference angle, from the vertical plane, to eject debris from." );
 331   addField( "debrisNum", TypeS32, Offset(debrisNum, ExplosionData),
 332      "Number of debris objects to create." );
 333   addField( "debrisNumVariance", TypeS32, Offset(debrisNumVariance, ExplosionData),
 334      "Variance in the number of debris objects to create (must be from 0 - debrisNum)." );
 335   addField( "debrisVelocity", TypeF32, Offset(debrisVelocity, ExplosionData),
 336      "Velocity to toss debris at." );
 337   addField( "debrisVelocityVariance", TypeF32, Offset(debrisVelocityVariance, ExplosionData),
 338      "Variance in the debris initial velocity (must be >= 0)." );
 339
 340   addField( "subExplosion", TYPEID< ExplosionData >(), Offset(explosionList, ExplosionData), EC_MAX_SUB_EXPLOSIONS,
 341      "List of additional ExplosionData objects to create at the start of the "
 342      "explosion." );
 343
 344   addField( "delayMS", TypeS32, Offset(delayMS, ExplosionData),
 345      "Amount of time, in milliseconds, to delay the start of the explosion effect "
 346      "from the creation of the Explosion object." );
 347   addField( "delayVariance", TypeS32, Offset(delayVariance, ExplosionData),
 348      "Variance, in milliseconds, of delayMS." );
 349   addField( "lifetimeMS", TypeS32, Offset(lifetimeMS, ExplosionData),
 350      "@brief Lifetime, in milliseconds, of the Explosion object.\n\n"
 351      "@note If explosionShape is defined and contains an <i>ambient</i> animation, "
 352      "this field is ignored, and the playSpeed scaled duration of the animation "
 353      "is used instead." );
 354   addField( "lifetimeVariance", TypeS32, Offset(lifetimeVariance, ExplosionData),
 355      "Variance, in milliseconds, of the lifetimeMS of the Explosion object.\n" );
 356   addField( "offset", TypeF32, Offset(offset, ExplosionData),
 357      "@brief Offset distance (in a random direction) of the center of the explosion "
 358      "from the Explosion object position.\n\n"
 359      "Most often used to create some variance in position for subExplosion effects." );
 360
 361   addField( "times", TypeF32, Offset(times, ExplosionData), EC_NUM_TIME_KEYS,
 362      "@brief Time keyframes used to scale the explosionShape model.\n\n"
 363      "Values should be in increasing order from 0.0 - 1.0, and correspond to "
 364      "the life of the Explosion where 0 is the beginning and 1 is the end of "
 365      "the explosion lifetime.\n"
 366      "@see lifetimeMS" );
 367   addField( "sizes", TypePoint3F, Offset(sizes, ExplosionData), EC_NUM_TIME_KEYS,
 368      "@brief \"X Y Z\" size keyframes used to scale the explosionShape model.\n\n"
 369      "The explosionShape (if defined) will be scaled using the times/sizes "
 370      "keyframes over the lifetime of the explosion.\n"
 371      "@see lifetimeMS" );
 372
 373   addField( "shakeCamera", TypeBool, Offset(shakeCamera, ExplosionData),
 374      "Controls whether the camera shakes during this explosion." );
 375   addField( "camShakeFreq", TypePoint3F, Offset(camShakeFreq, ExplosionData),
 376      "Frequency of camera shaking, defined in the \"X Y Z\" axes." );
 377   addField( "camShakeAmp", TypePoint3F, Offset(camShakeAmp, ExplosionData),
 378      "@brief Amplitude of camera shaking, defined in the \"X Y Z\" axes.\n\n"
 379      "Set any value to 0 to disable shaking in that axis." );
 380   addField( "camShakeDuration", TypeF32, Offset(camShakeDuration, ExplosionData),
 381      "Duration (in seconds) to shake the camera." );
 382   addField( "camShakeRadius", TypeF32, Offset(camShakeRadius, ExplosionData),
 383      "Radial distance that a camera's position must be within relative to the "
 384      "center of the explosion to be shaken." );
 385   addField( "camShakeFalloff", TypeF32, Offset(camShakeFalloff, ExplosionData),
 386      "Falloff value for the camera shake." );
 387
 388   addField( "lightStartRadius", TypeF32, Offset(lightStartRadius, ExplosionData),
 389      "@brief Initial radius of the PointLight created by this explosion.\n\n"
 390      "Radius is linearly interpolated from lightStartRadius to lightEndRadius "
 391      "over the lifetime of the explosion.\n"
 392      "@see lifetimeMS" );
 393   addField( "lightEndRadius", TypeF32, Offset(lightEndRadius, ExplosionData),
 394      "@brief Final radius of the PointLight created by this explosion.\n\n"
 395      "@see lightStartRadius" );
 396   addField( "lightStartColor", TypeColorF, Offset(lightStartColor, ExplosionData),
 397      "@brief Initial color of the PointLight created by this explosion.\n\n"
 398      "Color is linearly interpolated from lightStartColor to lightEndColor "
 399      "over the lifetime of the explosion.\n"
 400      "@see lifetimeMS" );
 401   addField( "lightEndColor", TypeColorF, Offset(lightEndColor, ExplosionData),
 402      "@brief Final color of the PointLight created by this explosion.\n\n"
 403      "@see lightStartColor" );
 404   addField( "lightStartBrightness", TypeF32, Offset(lightStartBrightness, ExplosionData),
 405      "@brief Initial brightness of the PointLight created by this explosion.\n\n"
 406      "Brightness is linearly interpolated from lightStartBrightness to "
 407      "lightEndBrightness over the lifetime of the explosion.\n"
 408      "@see lifetimeMS" );
 409   addField("lightEndBrightness", TypeF32, Offset(lightEndBrightness, ExplosionData),
 410      "@brief Final brightness of the PointLight created by this explosion.\n\n"
 411      "@see lightStartBrightness" );
 412   addField( "lightNormalOffset", TypeF32, Offset(lightNormalOffset, ExplosionData),
 413      "Distance (in the explosion normal direction) of the PointLight position "
 414      "from the explosion center." );
 415
 416   Parent::initPersistFields();
 417}
 418
 419bool ExplosionData::onAdd()
 420{
 421   if (Parent::onAdd() == false)
 422      return false;
 423
 424   if (explosionScale.x < 0.01f || explosionScale.y < 0.01f || explosionScale.z < 0.01f)
 425   {
 426      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s)::onAdd: ExplosionScale components must be >= 0.01", getName());
 427      explosionScale.x = explosionScale.x < 0.01f ? 0.01f : explosionScale.x;
 428      explosionScale.y = explosionScale.y < 0.01f ? 0.01f : explosionScale.y;
 429      explosionScale.z = explosionScale.z < 0.01f ? 0.01f : explosionScale.z;
 430   }
 431
 432   if (debrisThetaMin < 0.0f)
 433   {
 434      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin < 0.0", getName());
 435      debrisThetaMin = 0.0f;
 436   }
 437   if (debrisThetaMax > 180.0f)
 438   {
 439      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMax > 180.0", getName());
 440      debrisThetaMax = 180.0f;
 441   }
 442   if (debrisThetaMin > debrisThetaMax) {
 443      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin > debrisThetaMax", getName());
 444      debrisThetaMin = debrisThetaMax;
 445   }
 446   if (debrisPhiMin < 0.0f)
 447   {
 448      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin < 0.0", getName());
 449      debrisPhiMin = 0.0f;
 450   }
 451   if (debrisPhiMax > 360.0f)
 452   {
 453      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMax > 360.0", getName());
 454      debrisPhiMax = 360.0f;
 455   }
 456   if (debrisPhiMin > debrisPhiMax) {
 457      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin > debrisPhiMax", getName());
 458      debrisPhiMin = debrisPhiMax;
 459   }
 460   if (debrisNum > 1000) {
 461      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNum > 1000", getName());
 462      debrisNum = 1000;
 463   }
 464   if (debrisNumVariance > 1000) {
 465      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNumVariance > 1000", getName());
 466      debrisNumVariance = 1000;
 467   }
 468   if (debrisVelocity < 0.1f)
 469   {
 470      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocity < 0.1", getName());
 471      debrisVelocity = 0.1f;
 472   }
 473   if (debrisVelocityVariance > 1000) {
 474      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocityVariance > 1000", getName());
 475      debrisVelocityVariance = 1000;
 476   }
 477   if (playSpeed < 0.05f)
 478   {
 479      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) playSpeed < 0.05", getName());
 480      playSpeed = 0.05f;
 481   }
 482   if (lifetimeMS < 1) {
 483      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeMS < 1", getName());
 484      lifetimeMS = 1;
 485   }
 486   if (lifetimeVariance > lifetimeMS) {
 487      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeVariance > lifetimeMS", getName());
 488      lifetimeVariance = lifetimeMS;
 489   }
 490   if (delayMS < 0) {
 491      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayMS < 0", getName());
 492      delayMS = 0;
 493   }
 494   if (delayVariance > delayMS) {
 495      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayVariance > delayMS", getName());
 496      delayVariance = delayMS;
 497   }
 498   if (offset < 0.0f)
 499   {
 500      Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) offset < 0.0", getName());
 501      offset = 0.0f;
 502   }
 503
 504   S32 i;
 505   for( i=0; i<EC_NUM_DEBRIS_TYPES; i++ )
 506   {
 507      if( !debrisList[i] && debrisIDList[i] != 0 )
 508      {
 509         if( !Sim::findObject( debrisIDList[i], debrisList[i] ) )
 510         {
 511            Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(debris): 0x%x", debrisIDList[i] );
 512         }
 513      }
 514   }
 515
 516   for( i=0; i<EC_NUM_EMITTERS; i++ )
 517   {
 518      if( !emitterList[i] && emitterIDList[i] != 0 )
 519      {
 520         if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
 521         {
 522            Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
 523         }
 524      }
 525   }
 526
 527   for( S32 k=0; k<EC_MAX_SUB_EXPLOSIONS; k++ )
 528   {
 529      if( !explosionList[k] && explosionIDList[k] != 0 )
 530      {
 531         if( Sim::findObject( explosionIDList[k], explosionList[k] ) == false)
 532         {
 533            Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(explosion): 0x%x", explosionIDList[k] );
 534         }
 535      }
 536   }
 537
 538   return true;
 539}
 540
 541void ExplosionData::packData(BitStream* stream)
 542{
 543   Parent::packData(stream);
 544
 545   stream->writeString(dtsFileName);
 546
 547   sfxWrite( stream, soundProfile );
 548   if (stream->writeFlag(particleEmitter))
 549      stream->writeRangedU32(particleEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
 550
 551   stream->writeInt(particleDensity, 14);
 552   stream->write(particleRadius);
 553   stream->writeFlag(faceViewer);
 554   if(stream->writeFlag(explosionScale.x != 1 || explosionScale.y != 1 || explosionScale.z != 1))
 555   {
 556      stream->writeInt((S32)(explosionScale.x * 100), 16);
 557      stream->writeInt((S32)(explosionScale.y * 100), 16);
 558      stream->writeInt((S32)(explosionScale.z * 100), 16);
 559   }
 560   stream->writeInt((S32)(playSpeed * 20), 14);
 561   stream->writeRangedU32((U32)debrisThetaMin, 0, 180);
 562   stream->writeRangedU32((U32)debrisThetaMax, 0, 180);
 563   stream->writeRangedU32((U32)debrisPhiMin, 0, 360);
 564   stream->writeRangedU32((U32)debrisPhiMax, 0, 360);
 565   stream->writeRangedU32((U32)debrisNum, 0, 1000);
 566   stream->writeRangedU32(debrisNumVariance, 0, 1000);
 567   stream->writeInt((S32)(debrisVelocity * 10), 14);
 568   stream->writeRangedU32((U32)(debrisVelocityVariance * 10), 0, 10000);
 569   stream->writeInt(delayMS >> 5, 16);
 570   stream->writeInt(delayVariance >> 5, 16);
 571   stream->writeInt(lifetimeMS >> 5, 16);
 572   stream->writeInt(lifetimeVariance >> 5, 16);
 573   stream->write(offset);
 574
 575   stream->writeFlag( shakeCamera );
 576   stream->write(camShakeFreq.x);
 577   stream->write(camShakeFreq.y);
 578   stream->write(camShakeFreq.z);
 579   stream->write(camShakeAmp.x);
 580   stream->write(camShakeAmp.y);
 581   stream->write(camShakeAmp.z);
 582   stream->write(camShakeDuration);
 583   stream->write(camShakeRadius);
 584   stream->write(camShakeFalloff);
 585
 586   for( S32 j=0; j<EC_NUM_DEBRIS_TYPES; j++ )
 587   {
 588      if( stream->writeFlag( debrisList[j] ) )
 589      {
 590         stream->writeRangedU32( debrisList[j]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 591      }
 592   }
 593
 594   S32 i;
 595   for( i=0; i<EC_NUM_EMITTERS; i++ )
 596   {
 597      if( stream->writeFlag( emitterList[i] != NULL ) )
 598      {
 599         stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 600      }
 601   }
 602
 603   for( i=0; i<EC_MAX_SUB_EXPLOSIONS; i++ )
 604   {
 605      if( stream->writeFlag( explosionList[i] != NULL ) )
 606      {
 607         stream->writeRangedU32( explosionList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 608      }
 609   }
 610   U32 count;
 611   for(count = 0; count < EC_NUM_TIME_KEYS; count++)
 612      if(times[count] >= 1)
 613         break;
 614   count++;
 615   if(count > EC_NUM_TIME_KEYS)
 616      count = EC_NUM_TIME_KEYS;
 617
 618   stream->writeRangedU32(count, 0, EC_NUM_TIME_KEYS);
 619
 620   for( i=0; i<count; i++ )
 621      stream->writeFloat( times[i], 8 );
 622
 623   for( i=0; i<count; i++ )
 624   {
 625      stream->writeRangedU32((U32)(sizes[i].x * 100), 0, 16000);
 626      stream->writeRangedU32((U32)(sizes[i].y * 100), 0, 16000);
 627      stream->writeRangedU32((U32)(sizes[i].z * 100), 0, 16000);
 628   }
 629
 630   // Dynamic light info
 631   stream->writeFloat(lightStartRadius/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 632   stream->writeFloat(lightEndRadius/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 633   stream->writeFloat(lightStartColor.red,7);
 634   stream->writeFloat(lightStartColor.green,7);
 635   stream->writeFloat(lightStartColor.blue,7);
 636   stream->writeFloat(lightEndColor.red,7);
 637   stream->writeFloat(lightEndColor.green,7);
 638   stream->writeFloat(lightEndColor.blue,7);
 639   stream->writeFloat(lightStartBrightness/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 640   stream->writeFloat(lightEndBrightness/<a href="/coding/file/explosion_8cpp/#explosion_8cpp_1a91285d7c47bf4712b406cb79bf3a6d02">MaxLightRadius</a>, 8);
 641   stream->write(lightNormalOffset);
 642}
 643
 644void ExplosionData::unpackData(BitStream* stream)
 645{
 646   Parent::unpackData(stream);
 647
 648   dtsFileName = stream->readSTString();
 649
 650   sfxRead( stream, &soundProfile );
 651
 652   if (stream->readFlag())
 653      particleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 654   else
 655      particleEmitterId = 0;
 656
 657   particleDensity = stream->readInt(14);
 658   stream->read(&particleRadius);
 659   faceViewer = stream->readFlag();
 660   if(stream->readFlag())
 661   {
 662      explosionScale.x = stream->readInt(16) / 100.0f;
 663      explosionScale.y = stream->readInt(16) / 100.0f;
 664      explosionScale.z = stream->readInt(16) / 100.0f;
 665   }
 666   else
 667      explosionScale.set(1,1,1);
 668   playSpeed = stream->readInt(14) / 20.0f;
 669   debrisThetaMin = stream->readRangedU32(0, 180);
 670   debrisThetaMax = stream->readRangedU32(0, 180);
 671   debrisPhiMin = stream->readRangedU32(0, 360);
 672   debrisPhiMax = stream->readRangedU32(0, 360);
 673   debrisNum = stream->readRangedU32(0, 1000);
 674   debrisNumVariance = stream->readRangedU32(0, 1000);
 675
 676   debrisVelocity = stream->readInt(14) / 10.0f;
 677   debrisVelocityVariance = stream->readRangedU32(0, 10000) / 10.0f;
 678   delayMS = stream->readInt(16) << 5;
 679   delayVariance = stream->readInt(16) << 5;
 680   lifetimeMS = stream->readInt(16) << 5;
 681   lifetimeVariance = stream->readInt(16) << 5;
 682
 683   stream->read(&offset);
 684
 685   shakeCamera = stream->readFlag();
 686   stream->read(&camShakeFreq.x);
 687   stream->read(&camShakeFreq.y);
 688   stream->read(&camShakeFreq.z);
 689   stream->read(&camShakeAmp.x);
 690   stream->read(&camShakeAmp.y);
 691   stream->read(&camShakeAmp.z);
 692   stream->read(&camShakeDuration);
 693   stream->read(&camShakeRadius);
 694   stream->read(&camShakeFalloff);
 695
 696
 697   for( S32 j=0; j<EC_NUM_DEBRIS_TYPES; j++ )
 698   {
 699      if( stream->readFlag() )
 700      {
 701         debrisIDList[j] = (S32) stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 702      }
 703   }
 704
 705   U32 i;
 706   for( i=0; i<EC_NUM_EMITTERS; i++ )
 707   {
 708      if( stream->readFlag() )
 709      {
 710         emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 711      }
 712   }
 713
 714   for( S32 k=0; k<EC_MAX_SUB_EXPLOSIONS; k++ )
 715   {
 716      if( stream->readFlag() )
 717      {
 718         explosionIDList[k] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 719      }
 720   }
 721
 722   U32 count = stream->readRangedU32(0, EC_NUM_TIME_KEYS);
 723
 724   for( i=0; i<count; i++ )
 725      times[i] = stream->readFloat(8);
 726
 727   for( i=0; i<count; i++ )
 728   {
 729      sizes[i].x = stream->readRangedU32(0, 16000) / 100.0f;
 730      sizes[i].y = stream->readRangedU32(0, 16000) / 100.0f;
 731      sizes[i].z = stream->readRangedU32(0, 16000) / 100.0f;
 732   }
 733
 734   //
 735   lightStartRadius = stream->readFloat(8) * MaxLightRadius;
 736   lightEndRadius = stream->readFloat(8) * MaxLightRadius;
 737   lightStartColor.red = stream->readFloat(7);
 738   lightStartColor.green = stream->readFloat(7);
 739   lightStartColor.blue = stream->readFloat(7);
 740   lightEndColor.red = stream->readFloat(7);
 741   lightEndColor.green = stream->readFloat(7);
 742   lightEndColor.blue = stream->readFloat(7);
 743   lightStartBrightness = stream->readFloat(8) * MaxLightRadius;
 744   lightEndBrightness = stream->readFloat(8) * MaxLightRadius;
 745   stream->read( &lightNormalOffset );
 746}
 747
 748bool ExplosionData::preload(bool server, String &errorStr)
 749{
 750   if (Parent::preload(server, errorStr) == false)
 751      return false;
 752      
 753   if( !server )
 754   {
 755      String sfxErrorStr;
 756      if( !sfxResolve( &soundProfile, sfxErrorStr ) )
 757         Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", sfxErrorStr.c_str());
 758      if (!particleEmitter && particleEmitterId != 0)
 759         if (Sim::findObject(particleEmitterId, particleEmitter) == false)
 760            Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock");
 761   }
 762
 763   if (dtsFileName && dtsFileName[0]) {
 764      explosionShape = ResourceManager::get().load(dtsFileName);
 765      if (!bool(explosionShape)) {
 766         errorStr = String::ToString("ExplosionData: Couldn't load shape \"%s\"", dtsFileName);
 767         return false;
 768      }
 769
 770      // Resolve animations
 771      explosionAnimation = explosionShape->findSequence("ambient");
 772
 773      // Preload textures with a dummy instance...
 774      TSShapeInstance* pDummy = new TSShapeInstance(explosionShape, !server);
 775      delete pDummy;
 776
 777   } else {
 778      explosionShape     = NULL;
 779      explosionAnimation = -1;
 780   }
 781
 782   return true;
 783}
 784
 785
 786//--------------------------------------------------------------------------
 787//--------------------------------------
 788//
 789Explosion::Explosion()
 790   : mDataBlock( NULL )
 791{
 792   mTypeMask |= ExplosionObjectType | LightObjectType;
 793
 794   mExplosionInstance = NULL;
 795   mExplosionThread   = NULL;
 796
 797   dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
 798   mMainEmitter = NULL;
 799
 800   mFade = 1;
 801   mDelayMS = 0;
 802   mCurrMS = 0;
 803   mEndingMS = 1000;
 804   mActive = false;
 805   mCollideType = 0;
 806
 807   mInitialNormal.set( 0.0f, 0.0f, 1.0f );
 808   mRandAngle = sgRandom.randF( 0.0f, 1.0f ) * M_PI_F * 2.0f;
 809   mLight = LIGHTMGR->createLightInfo();
 810
 811   mNetFlags.set( IsGhost );
 812}
 813
 814Explosion::~Explosion()
 815{
 816   if( mExplosionInstance )
 817   {
 818      delete mExplosionInstance;
 819      mExplosionInstance = NULL;
 820      mExplosionThread   = NULL;
 821   }
 822   
 823   SAFE_DELETE(mLight);
 824}
 825
 826
 827void Explosion::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade)
 828{
 829   setPosition(point);
 830   mInitialNormal   = normal;
 831   mFade            = fade;
 832}
 833
 834//--------------------------------------------------------------------------
 835void Explosion::initPersistFields()
 836{
 837   Parent::initPersistFields();
 838
 839   //
 840}
 841
 842//--------------------------------------------------------------------------
 843bool Explosion::onAdd()
 844{
 845   // first check if we have a server connection, if we dont then this is on the server
 846   //  and we should exit, then check if the parent fails to add the object
 847   GameConnection *conn = GameConnection::getConnectionToServer();
 848   if ( !conn || !Parent::onAdd() )
 849      return false;
 850
 851   if( !mDataBlock )
 852   {
 853      Con::errorf("Explosion::onAdd - Fail - No datablok");
 854      return false;
 855   }
 856
 857   mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
 858   mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
 859
 860   if( mFabs( mDataBlock->offset ) > 0.001f )
 861   {
 862      MatrixF axisOrient = MathUtils::createOrientFromDir( mInitialNormal );
 863
 864      MatrixF trans = getTransform();
 865      Point3F randVec;
 866      randVec.x = sgRandom.randF( -1.0f, 1.0f );
 867      randVec.y = sgRandom.randF( 0.0f, 1.0f );
 868      randVec.z = sgRandom.randF( -1.0f, 1.0f );
 869      randVec.normalize();
 870      randVec *= mDataBlock->offset;
 871      axisOrient.mulV( randVec );
 872      trans.setPosition( trans.getPosition() + randVec );
 873      setTransform( trans );
 874   }
 875
 876   // shake camera
 877   if( mDataBlock->shakeCamera )
 878   {
 879      // first check if explosion is near player
 880      GameConnection* connection = GameConnection::getConnectionToServer();
 881      ShapeBase *obj = dynamic_cast<ShapeBase*>(connection->getControlObject());
 882
 883      bool applyShake = true;
 884
 885      if( obj )
 886      {
 887         ShapeBase* cObj = obj;
 888         while((cObj = cObj->getControlObject()) != 0)
 889         {
 890            if(cObj->useObjsEyePoint())
 891            {
 892               applyShake = false;
 893               break;
 894            }
 895         }
 896      }
 897
 898
 899      if( applyShake && obj )
 900      {
 901         VectorF diff = obj->getPosition() - getPosition();
 902         F32 dist = diff.len();
 903         if( dist < mDataBlock->camShakeRadius )
 904         {
 905            CameraShake *camShake = new CameraShake;
 906            camShake->setDuration( mDataBlock->camShakeDuration );
 907            camShake->setFrequency( mDataBlock->camShakeFreq );
 908
 909            F32 falloff =  dist / mDataBlock->camShakeRadius;
 910            falloff = 1.0f + falloff * 10.0f;
 911            falloff = 1.0f / (falloff * falloff);
 912
 913            VectorF shakeAmp = mDataBlock->camShakeAmp * falloff;
 914            camShake->setAmplitude( shakeAmp );
 915            camShake->setFalloff( mDataBlock->camShakeFalloff );
 916            camShake->init();
 917            gCamFXMgr.addFX( camShake );
 918         }
 919      }
 920   }
 921
 922
 923   if( mDelayMS == 0 )
 924   {
 925      if( !explode() )
 926      {
 927         return false;
 928      }
 929   }
 930
 931   gClientSceneGraph->addObjectToScene(this);
 932
 933   removeFromProcessList();
 934   ClientProcessList::get()->addObject(this);
 935
 936   mRandomVal = sgRandom.randF();
 937
 938   NetConnection* pNC = NetConnection::getConnectionToServer();
 939   AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
 940   pNC->addObject(this);
 941
 942   // Initialize the light structure and register as a dynamic light
 943   if (mDataBlock->lightStartRadius != 0.0f || mDataBlock->lightEndRadius)
 944   {
 945      mLight->setType( LightInfo::Point );
 946      mLight->setRange( mDataBlock->lightStartRadius );
 947      mLight->setColor( mDataBlock->lightStartColor );
 948   }
 949
 950   return true;
 951}
 952
 953void Explosion::onRemove()
 954{
 955   for( S32 i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
 956   {
 957      if( mEmitterList[i] )
 958      {
 959         mEmitterList[i]->deleteWhenEmpty();
 960         mEmitterList[i] = NULL;
 961      }
 962   }
 963
 964   if( mMainEmitter )
 965   {
 966      mMainEmitter->deleteWhenEmpty();
 967      mMainEmitter = NULL;
 968   }
 969
 970   removeFromScene();
 971
 972   Parent::onRemove();
 973}
 974
 975
 976bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload )
 977{
 978   mDataBlock = dynamic_cast<ExplosionData*>( dptr );
 979   if (!mDataBlock || !Parent::onNewDataBlock( dptr, reload ))
 980      return false;
 981
 982   scriptOnNewDataBlock();
 983   return true;
 984}
 985
 986
 987//--------------------------------------------------------------------------
 988void Explosion::prepRenderImage( SceneRenderState* state )
 989{
 990   prepBatchRender( state );
 991}
 992
 993void Explosion::setCurrentScale()
 994{
 995   F32 t = F32(mCurrMS) / F32(mEndingMS);
 996
 997   for( U32 i = 1; i < ExplosionData::EC_NUM_TIME_KEYS; i++ )
 998   {
 999      if( mDataBlock->times[i] >= t )
1000      {
1001         F32 firstPart =   t - mDataBlock->times[i-1];
1002         F32 total     =   mDataBlock->times[i] -
1003                           mDataBlock->times[i-1];
1004
1005         firstPart /= total;
1006
1007         mObjScale =      (mDataBlock->sizes[i-1] * (1.0f - firstPart)) +
1008                          (mDataBlock->sizes[i]   * firstPart);
1009
1010         return;
1011      }
1012   }
1013
1014}
1015
1016//--------------------------------------------------------------------------
1017// Make the explosion face the viewer (if desired)
1018//--------------------------------------------------------------------------
1019void Explosion::prepModelView(SceneRenderState* state)
1020{
1021   MatrixF rotMatrix( true );
1022   Point3F targetVector;
1023
1024   if( mDataBlock->faceViewer )
1025   {
1026      targetVector = getPosition() - state->getCameraPosition();
1027      targetVector.normalize();
1028
1029      // rotate explosion each time so it's a little different
1030      rotMatrix.set( EulerF( 0.0f, mRandAngle, 0.0f ) );
1031   }
1032   else
1033   {
1034      targetVector = mInitialNormal;
1035   }
1036
1037   MatrixF explOrient = MathUtils::createOrientFromDir( targetVector );
1038   explOrient.mul( rotMatrix );
1039   explOrient.setPosition( getPosition() );
1040
1041   setCurrentScale();
1042   explOrient.scale( mObjScale );
1043   GFX->setWorldMatrix( explOrient );
1044}
1045
1046//--------------------------------------------------------------------------
1047// Render object
1048//--------------------------------------------------------------------------
1049void Explosion::prepBatchRender(SceneRenderState* state)
1050{
1051   if ( !mExplosionInstance )
1052      return;
1053
1054   MatrixF proj = GFX->getProjectionMatrix();
1055   RectI viewport = GFX->getViewport();
1056
1057   // Set up our TS render state here.
1058   TSRenderState rdata;
1059   rdata.setSceneState( state );
1060
1061   // We might have some forward lit materials
1062   // so pass down a query to gather lights.
1063   LightQuery query;
1064   query.init( getWorldSphere() );
1065   rdata.setLightQuery( &query );
1066
1067   // render mesh
1068   GFX->pushWorldMatrix();
1069
1070   prepModelView( state );
1071
1072   mExplosionInstance->animate();
1073   mExplosionInstance->render( rdata );
1074
1075   GFX->popWorldMatrix();
1076   GFX->setProjectionMatrix( proj );
1077   GFX->setViewport( viewport );
1078}
1079
1080void Explosion::submitLights( LightManager *lm, bool staticLighting )
1081{
1082   if ( staticLighting )
1083      return;
1084
1085   // Update the light's info and add it to the scene, the light will
1086   // only be visible for this current frame.
1087   mLight->setPosition( getRenderTransform().getPosition() + mInitialNormal * mDataBlock->lightNormalOffset );
1088   F32 t = F32(mCurrMS) / F32(mEndingMS);
1089   mLight->setRange( mDataBlock->lightStartRadius +
1090      (mDataBlock->lightEndRadius - mDataBlock->lightStartRadius) * t );
1091   mLight->setColor( mDataBlock->lightStartColor +
1092      (mDataBlock->lightEndColor - mDataBlock->lightStartColor) * t );
1093   mLight->setBrightness( mDataBlock->lightStartBrightness +
1094      (mDataBlock->lightEndBrightness - mDataBlock->lightStartBrightness) * t );
1095
1096   lm->registerGlobalLight( mLight, this );
1097}
1098
1099
1100//--------------------------------------------------------------------------
1101void Explosion::processTick(const Move*)
1102{
1103   mCurrMS += TickMs;
1104
1105   if( mCurrMS >= mEndingMS )
1106   {
1107         deleteObject();
1108         return;
1109   }
1110         
1111   if( (mCurrMS > mDelayMS) && !mActive )
1112      explode();
1113}
1114
1115void Explosion::advanceTime(F32 dt)
1116{
1117   if (dt == 0.0f)
1118      return;
1119
1120   GameConnection* conn = GameConnection::getConnectionToServer();
1121   if(!conn)
1122      return;
1123
1124   updateEmitters( dt );
1125
1126   if( mExplosionInstance )
1127      mExplosionInstance->advanceTime(dt, mExplosionThread);
1128}
1129
1130//----------------------------------------------------------------------------
1131// Update emitters
1132//----------------------------------------------------------------------------
1133void Explosion::updateEmitters( F32 dt )
1134{
1135   Point3F pos = getPosition();
1136
1137   for( S32 i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
1138   {
1139      if( mEmitterList[i] )
1140      {
1141         mEmitterList[i]->emitParticles( pos, pos, mInitialNormal, Point3F( 0.0f, 0.0f, 0.0f ), (U32)(dt * 1000));
1142      }
1143   }
1144
1145}
1146
1147//----------------------------------------------------------------------------
1148// Launch Debris
1149//----------------------------------------------------------------------------
1150void Explosion::launchDebris( Point3F &axis )
1151{
1152   GameConnection* conn = GameConnection::getConnectionToServer();
1153   if(!conn)
1154      return;
1155
1156   bool hasDebris = false;
1157   for( S32 j=0; j<ExplosionData::EC_NUM_DEBRIS_TYPES; j++ )
1158   {
1159      if( mDataBlock->debrisList[j] )
1160      {
1161         hasDebris = true;
1162         break;
1163      }
1164   }
1165   if( !hasDebris )
1166   {
1167      return;
1168   }
1169
1170   Point3F axisx;
1171   if (mFabs(axis.z) < 0.999f)
1172      mCross(axis, Point3F(0.0f, 0.0f, 1.0f), &axisx);
1173   else
1174      mCross(axis, Point3F(0.0f, 1.0f, 0.0f), &axisx);
1175   axisx.normalize();
1176
1177   Point3F pos( 0.0f, 0.0f, 0.5f );
1178   pos += getPosition();
1179
1180
1181   U32 numDebris = mDataBlock->debrisNum + sgRandom.randI( -mDataBlock->debrisNumVariance, mDataBlock->debrisNumVariance );
1182
1183   for( S32 i=0; i<numDebris; i++ )
1184   {
1185
1186      Point3F launchDir = MathUtils::randomDir( axis, mDataBlock->debrisThetaMin, mDataBlock->debrisThetaMax,
1187                                                mDataBlock->debrisPhiMin, mDataBlock->debrisPhiMax );
1188
1189      F32 debrisVel = mDataBlock->debrisVelocity + mDataBlock->debrisVelocityVariance * sgRandom.randF( -1.0f, 1.0f );
1190
1191      launchDir *= debrisVel;
1192
1193      Debris *debris = new Debris;
1194      debris->setDataBlock( mDataBlock->debrisList[0] );
1195      debris->setTransform( getTransform() );
1196      debris->init( pos, launchDir );
1197
1198      if( !debris->registerObject() )
1199      {
1200         Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() );
1201         delete debris;
1202         debris = NULL;
1203      }
1204   }
1205}
1206
1207//----------------------------------------------------------------------------
1208// Spawn sub explosions
1209//----------------------------------------------------------------------------
1210void Explosion::spawnSubExplosions()
1211{
1212   GameConnection* conn = GameConnection::getConnectionToServer();
1213   if(!conn)
1214      return;
1215
1216   for( S32 i=0; i<ExplosionData::EC_MAX_SUB_EXPLOSIONS; i++ )
1217   {
1218      if( mDataBlock->explosionList[i] )
1219      {
1220         MatrixF trans = getTransform();
1221         Explosion* pExplosion = new Explosion;
1222         pExplosion->setDataBlock( mDataBlock->explosionList[i] );
1223         pExplosion->setTransform( trans );
1224         pExplosion->setInitialState( trans.getPosition(), mInitialNormal, 1);
1225         if (!pExplosion->registerObject())
1226            delete pExplosion;
1227      }
1228   }
1229}
1230
1231//----------------------------------------------------------------------------
1232// Explode
1233//----------------------------------------------------------------------------
1234bool Explosion::explode()
1235{
1236   mActive = true;
1237
1238   GameConnection* conn = GameConnection::getConnectionToServer();
1239   if(!conn)
1240      return false;
1241
1242   launchDebris( mInitialNormal );
1243   spawnSubExplosions();
1244
1245   if (bool(mDataBlock->explosionShape) && mDataBlock->explosionAnimation != -1) {
1246      mExplosionInstance = new TSShapeInstance(mDataBlock->explosionShape, true);
1247
1248      mExplosionThread   = mExplosionInstance->addThread();
1249      mExplosionInstance->setSequence(mExplosionThread, mDataBlock->explosionAnimation, 0);
1250      mExplosionInstance->setTimeScale(mExplosionThread, mDataBlock->playSpeed);
1251
1252      mCurrMS   = 0;
1253      mEndingMS = U32(mExplosionInstance->getScaledDuration(mExplosionThread) * 1000.0f);
1254
1255      mObjScale.convolve(mDataBlock->explosionScale);
1256      mObjBox = mDataBlock->explosionShape->bounds;
1257      resetWorldBox();
1258   }
1259
1260   if (mDataBlock->soundProfile)
1261      SFX->playOnce( mDataBlock->soundProfile, &getTransform() );
1262
1263   if (mDataBlock->particleEmitter) {
1264      mMainEmitter = new ParticleEmitter;
1265      mMainEmitter->setDataBlock(mDataBlock->particleEmitter);
1266      mMainEmitter->registerObject();
1267
1268      mMainEmitter->emitParticles(getPosition(), mInitialNormal, mDataBlock->particleRadius,
1269         Point3F::Zero, U32(mDataBlock->particleDensity * mFade));
1270   }
1271
1272   for( S32 i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
1273   {
1274      if( mDataBlock->emitterList[i] != NULL )
1275      {
1276         ParticleEmitter * pEmitter = new ParticleEmitter;
1277         pEmitter->setDataBlock( mDataBlock->emitterList[i] );
1278         if( !pEmitter->registerObject() )
1279         {
1280            Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
1281            SAFE_DELETE(pEmitter);
1282         }
1283         mEmitterList[i] = pEmitter;
1284      }
1285   }
1286
1287   return true;
1288}
1289
1290