rigidShape.cpp

Engine/source/T3D/rigidShape.cpp

More...

Public Variables

Public Functions

ConsoleDocClass(RigidShape , "@brief The <a href="/coding/class/classrigidshape/">RigidShape</a> class implements rigid-body physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> DTS objects in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n\n</a>" "\"Rigid body physics\" refers <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> system whereby objects are assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> finite size, \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "equally distributed masses, and where deformations of the objects themselves are not accounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for.\n</a>" "Uses the <a href="/coding/class/classrigidshape/">RigidShape</a> class <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> its <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">physics.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" " datablock <a href="/coding/class/classrigidshapedata/">RigidShapeData</a>(BouncingBoulder)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " category=\"RigidShape\";\n" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n" "	   emap = true;\<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/class/classrigid/">Rigid</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Body\n</a>" "	   mass = 500;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   massCenter = \"0 0 0\";    // Center of mass <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rigid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">body\n</a>" "	   massBox = \"0 0 0\";         // Size of box used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> moment of inertia,\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "								  // <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> zero it defaults <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> object bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box\n</a>" "	   drag = 0.2;                // Drag <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coefficient\n</a>" "	   bodyFriction = 0.2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   bodyRestitution = 0.1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minImpactSpeed = 5;        // Impacts over this invoke the script <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">callback\n</a>" "	   softImpactSpeed = 5;       // Play SoftImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   hardImpactSpeed = 15;      // Play HardImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   integration = 4;           // Physics integration: <a href="/coding/file/processlist_8h/#processlist_8h_1a6dfddc4b6033c4de60ed37c4fe30f650">TickSec</a>/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rate\n</a>" "	   collisionTol = 0.1;        // <a href="/coding/class/structcollision/">Collision</a> distance <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "	   contactTol = 0.1;          // Contact velocity <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minRollSpeed = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   maxDrag = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minDrag = 0.01;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dustHeight = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dragForce = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   vertFactor = 0.05;\<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/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classrigidshape/">RigidShape</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		dataBlock = \"BouncingBoulder\";\n" "		parentGroup = EWCreatorWindow.objectGroup;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">RigidShapeData\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBase\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Physics\n</a>" )
ConsoleDocClass(RigidShapeData , "@brief Defines the physics properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an individual <a href="/coding/class/classrigidshapedata/">RigidShapeData</a> physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" " datablock <a href="/coding/class/classrigidshapedata/">RigidShapeData</a>( BouncingBoulder )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   category = \"RigidShape\";\n" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n" "	   emap = true;\<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/class/classrigid/">Rigid</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Body\n</a>" "	   mass = 500;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   massCenter = \"0 0 0\";    // Center of mass <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rigid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">body\n</a>" "	   massBox = \"0 0 0\";         // Size of box used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> moment of inertia,\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "								  // <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> zero it defaults <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> object bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box\n</a>" "	   drag = 0.2;                // Drag <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coefficient\n</a>" "	   bodyFriction = 0.2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   bodyRestitution = 0.1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minImpactSpeed = 5;        // Impacts over this invoke the script <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">callback\n</a>" "	   softImpactSpeed = 5;       // Play SoftImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   hardImpactSpeed = 15;      // Play HardImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   integration = 4;           // Physics integration: <a href="/coding/file/processlist_8h/#processlist_8h_1a6dfddc4b6033c4de60ed37c4fe30f650">TickSec</a>/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rate\n</a>" "	   collisionTol = 0.1;        // <a href="/coding/class/structcollision/">Collision</a> distance <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "	   contactTol = 0.1;          // Contact velocity <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minRollSpeed = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   maxDrag = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minDrag = 0.01;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dustHeight = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dragForce = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   vertFactor = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">RigidShape\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBase\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Physics\n</a>" )
DefineEngineMethod(RigidShape , forceClientTransform , void , () , "@brief Forces the client <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> jump <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/class/classrigidshape/">RigidShape</a>'s transform rather then warp <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n\n</a>" )
DefineEngineMethod(RigidShape , freezeSim , void , (bool isFrozen) , "@brief Enables or disables the physics simulation on the <a href="/coding/class/classrigidshape/">RigidShape</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param isFrozen Boolean frozen state <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the frozen <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n</a>" "% isFrozen)
DefineEngineMethod(RigidShape , reset , void , () , "@brief Clears physic forces from the shape and sets it at <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">rest.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Inform the <a href="/coding/class/classrigidshape/">RigidShape</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reset.\n</a>" "%thisRigidShape.reset();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/class/structshapebasedata/">ShapeBaseData</a>" )
IMPLEMENT_CALLBACK(RigidShapeData , onEnterLiquid , void , (RigidShape *obj, F32 coverage, const char *type) , (obj, coverage, type) , "Called when the vehicle enters <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n</a>" "@param obj the <a href="/coding/class/classvehicle/">Vehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param coverage percentage of the vehicle's bounding box covered by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid\n</a>" "@param type type of liquid the vehicle has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered\n</a>" )
IMPLEMENT_CALLBACK(RigidShapeData , onLeaveLiquid , void , (RigidShape *obj, const char *type) , (obj, type) , "Called when the vehicle leaves <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n</a>" "@param obj the <a href="/coding/class/classvehicle/">Vehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param type type of liquid the vehicle has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left\n</a>" )

Detailed Description

Public Variables

U32 sTriggerMask 

Public Functions

ConsoleDocClass(RigidShape , "@brief The <a href="/coding/class/classrigidshape/">RigidShape</a> class implements rigid-body physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> DTS objects in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n\n</a>" "\"Rigid body physics\" refers <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> system whereby objects are assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> finite size, \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "equally distributed masses, and where deformations of the objects themselves are not accounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for.\n</a>" "Uses the <a href="/coding/class/classrigidshape/">RigidShape</a> class <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> its <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">physics.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" " datablock <a href="/coding/class/classrigidshapedata/">RigidShapeData</a>(BouncingBoulder)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " category=\"RigidShape\";\n" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n" "	   emap = true;\<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/class/classrigid/">Rigid</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Body\n</a>" "	   mass = 500;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   massCenter = \"0 0 0\";    // Center of mass <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rigid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">body\n</a>" "	   massBox = \"0 0 0\";         // Size of box used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> moment of inertia,\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "								  // <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> zero it defaults <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> object bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box\n</a>" "	   drag = 0.2;                // Drag <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coefficient\n</a>" "	   bodyFriction = 0.2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   bodyRestitution = 0.1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minImpactSpeed = 5;        // Impacts over this invoke the script <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">callback\n</a>" "	   softImpactSpeed = 5;       // Play SoftImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   hardImpactSpeed = 15;      // Play HardImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   integration = 4;           // Physics integration: <a href="/coding/file/processlist_8h/#processlist_8h_1a6dfddc4b6033c4de60ed37c4fe30f650">TickSec</a>/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rate\n</a>" "	   collisionTol = 0.1;        // <a href="/coding/class/structcollision/">Collision</a> distance <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "	   contactTol = 0.1;          // Contact velocity <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minRollSpeed = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   maxDrag = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minDrag = 0.01;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dustHeight = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dragForce = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   vertFactor = 0.05;\<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/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classrigidshape/">RigidShape</a>()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "		dataBlock = \"BouncingBoulder\";\n" "		parentGroup = EWCreatorWindow.objectGroup;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">RigidShapeData\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBase\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Physics\n</a>" )

ConsoleDocClass(RigidShapeData , "@brief Defines the physics properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an individual <a href="/coding/class/classrigidshapedata/">RigidShapeData</a> physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" " datablock <a href="/coding/class/classrigidshapedata/">RigidShapeData</a>( BouncingBoulder )\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   category = \"RigidShape\";\n" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n" "	   emap = true;\<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/class/classrigid/">Rigid</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Body\n</a>" "	   mass = 500;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   massCenter = \"0 0 0\";    // Center of mass <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rigid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">body\n</a>" "	   massBox = \"0 0 0\";         // Size of box used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> moment of inertia,\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "								  // <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> zero it defaults <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> object bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box\n</a>" "	   drag = 0.2;                // Drag <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">coefficient\n</a>" "	   bodyFriction = 0.2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   bodyRestitution = 0.1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minImpactSpeed = 5;        // Impacts over this invoke the script <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">callback\n</a>" "	   softImpactSpeed = 5;       // Play SoftImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   hardImpactSpeed = 15;      // Play HardImpact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Sound\n</a>" "	   integration = 4;           // Physics integration: <a href="/coding/file/processlist_8h/#processlist_8h_1a6dfddc4b6033c4de60ed37c4fe30f650">TickSec</a>/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Rate\n</a>" "	   collisionTol = 0.1;        // <a href="/coding/class/structcollision/">Collision</a> distance <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "	   contactTol = 0.1;          // Contact velocity <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tolerance\n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minRollSpeed = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   maxDrag = 0.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   minDrag = 0.01;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dustHeight = 10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   dragForce = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	   vertFactor = 0.05;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "	};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">RigidShape\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShapeBase\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Physics\n</a>" )

DefineEngineMethod(RigidShape , forceClientTransform , void , () , "@brief Forces the client <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> jump <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/class/classrigidshape/">RigidShape</a>'s transform rather then warp <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it.\n\n</a>" )

DefineEngineMethod(RigidShape , freezeSim , void , (bool isFrozen) , "@brief Enables or disables the physics simulation on the <a href="/coding/class/classrigidshape/">RigidShape</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param isFrozen Boolean frozen state <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Define the frozen <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n</a>" "% isFrozen)

DefineEngineMethod(RigidShape , reset , void , () , "@brief Clears physic forces from the shape and sets it at <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">rest.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Inform the <a href="/coding/class/classrigidshape/">RigidShape</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reset.\n</a>" "%thisRigidShape.reset();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/class/structshapebasedata/">ShapeBaseData</a>" )

IMPLEMENT_CALLBACK(RigidShapeData , onEnterLiquid , void , (RigidShape *obj, F32 coverage, const char *type) , (obj, coverage, type) , "Called when the vehicle enters <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n</a>" "@param obj the <a href="/coding/class/classvehicle/">Vehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param coverage percentage of the vehicle's bounding box covered by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid\n</a>" "@param type type of liquid the vehicle has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entered\n</a>" )

IMPLEMENT_CALLBACK(RigidShapeData , onLeaveLiquid , void , (RigidShape *obj, const char *type) , (obj, type) , "Called when the vehicle leaves <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">liquid.\n</a>" "@param obj the <a href="/coding/class/classvehicle/">Vehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" "@param type type of liquid the vehicle has <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">left\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData )

IMPLEMENT_CO_NETOBJECT_V1(RigidShape )

   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/rigidShape.h"
  26
  27#include "app/game.h"
  28#include "math/mMath.h"
  29#include "console/simBase.h"
  30#include "console/console.h"
  31#include "console/consoleTypes.h"
  32#include "collision/clippedPolyList.h"
  33#include "collision/planeExtractor.h"
  34#include "T3D/gameBase/moveManager.h"
  35#include "core/stream/bitStream.h"
  36#include "core/dnet.h"
  37#include "T3D/gameBase/gameConnection.h"
  38#include "ts/tsShapeInstance.h"
  39#include "math/mathIO.h"
  40#include "scene/sceneRenderState.h"
  41#include "scene/sceneManager.h"
  42#include "T3D/fx/cameraFXMgr.h"
  43#include "T3D/trigger.h"
  44#include "T3D/item.h"
  45#include "gfx/primBuilder.h"
  46#include "gfx/gfxDrawUtil.h"
  47#include "sfx/sfxTypes.h"
  48#include "sfx/sfxSystem.h"
  49#include "T3D/fx/particleEmitter.h"
  50#include "console/engineAPI.h"
  51#include "T3D/physics/physicsPlugin.h"
  52#include "T3D/physics/physicsCollision.h"
  53
  54IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData);
  55
  56ConsoleDocClass( RigidShapeData,
  57   "@brief Defines the physics properties for an individual RigidShapeData physics object.\n\n"
  58
  59   "@tsexample\n"
  60   "  datablock RigidShapeData( BouncingBoulder )\n"
  61   "  {\n"  
  62   "     category = \"RigidShape\";\n"
  63   "\n"     
  64   "     shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n"
  65   "     emap = true;\n"
  66   "\n"
  67   "     // Rigid Body\n"
  68   "     mass = 500;\n"
  69   "     massCenter = \"0 0 0\";    // Center of mass for rigid body\n"
  70   "     massBox = \"0 0 0\";         // Size of box used for moment of inertia,\n"
  71   "                         // if zero it defaults to object bounding box\n"
  72   "     drag = 0.2;                // Drag coefficient\n"
  73   "     bodyFriction = 0.2;\n"
  74   "     bodyRestitution = 0.1;\n"
  75   "     minImpactSpeed = 5;        // Impacts over this invoke the script callback\n"
  76   "     softImpactSpeed = 5;       // Play SoftImpact Sound\n"
  77   "     hardImpactSpeed = 15;      // Play HardImpact Sound\n"
  78   "     integration = 4;           // Physics integration: TickSec/Rate\n"
  79   "     collisionTol = 0.1;        // Collision distance tolerance\n"
  80   "     contactTol = 0.1;          // Contact velocity tolerance\n"
  81   "\n"     
  82   "     minRollSpeed = 10;\n"
  83   "\n"     
  84   "     maxDrag = 0.5;\n"
  85   "     minDrag = 0.01;\n"
  86   "\n"
  87   "     dustHeight = 10;\n"
  88   "\n"
  89   "     dragForce = 0.05;\n"
  90   "     vertFactor = 0.05;\n"
  91   "  };\n"
  92   "@endtsexample\n\n"
  93
  94   "@see RigidShape\n"
  95   "@see ShapeBase\n\n"
  96
  97   "@ingroup Physics\n"
  98);
  99
 100
 101IMPLEMENT_CO_NETOBJECT_V1(RigidShape);
 102
 103ConsoleDocClass( RigidShape,
 104   "@brief The RigidShape class implements rigid-body physics for DTS objects in the world.\n\n"
 105
 106   "\"Rigid body physics\" refers to a system whereby objects are assumed to have a finite size,\n"
 107   "equally distributed masses, and where deformations of the objects themselves are not accounted for.\n"
 108   "Uses the RigidShape class to control its physics.\n\n"
 109
 110   "@tsexample\n"
 111   "  datablock RigidShapeData( BouncingBoulder )\n"
 112   "  {\n"  
 113   "     category = \"RigidShape\";\n"
 114   "\n"     
 115   "     shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n"
 116   "     emap = true;\n"
 117   "\n"
 118   "     // Rigid Body\n"
 119   "     mass = 500;\n"
 120   "     massCenter = \"0 0 0\";    // Center of mass for rigid body\n"
 121   "     massBox = \"0 0 0\";         // Size of box used for moment of inertia,\n"
 122   "                         // if zero it defaults to object bounding box\n"
 123   "     drag = 0.2;                // Drag coefficient\n"
 124   "     bodyFriction = 0.2;\n"
 125   "     bodyRestitution = 0.1;\n"
 126   "     minImpactSpeed = 5;        // Impacts over this invoke the script callback\n"
 127   "     softImpactSpeed = 5;       // Play SoftImpact Sound\n"
 128   "     hardImpactSpeed = 15;      // Play HardImpact Sound\n"
 129   "     integration = 4;           // Physics integration: TickSec/Rate\n"
 130   "     collisionTol = 0.1;        // Collision distance tolerance\n"
 131   "     contactTol = 0.1;          // Contact velocity tolerance\n"
 132   "\n"     
 133   "     minRollSpeed = 10;\n"
 134   "\n"     
 135   "     maxDrag = 0.5;\n"
 136   "     minDrag = 0.01;\n"
 137   "\n"
 138   "     dustHeight = 10;\n"
 139   "\n"
 140   "     dragForce = 0.05;\n"
 141   "     vertFactor = 0.05;\n"
 142   "  };\n"
 143   "\n"
 144   " new RigidShape()\n"
 145   "  {\n"
 146    "    dataBlock = \"BouncingBoulder\";\n"
 147    "    parentGroup = EWCreatorWindow.objectGroup;\n"
 148    " };\n"
 149   "@endtsexample\n\n"
 150
 151   "@see RigidShapeData\n"
 152   "@see ShapeBase\n\n"
 153   
 154   "@ingroup Physics\n"
 155);
 156
 157IMPLEMENT_CALLBACK(RigidShapeData, onEnterLiquid, void, (RigidShape* obj, F32 coverage, const char* type), (obj, coverage, type),
 158   "Called when the vehicle enters liquid.\n"
 159   "@param obj the Vehicle object\n"
 160   "@param coverage percentage of the vehicle's bounding box covered by the liquid\n"
 161   "@param type type of liquid the vehicle has entered\n");
 162
 163IMPLEMENT_CALLBACK(RigidShapeData, onLeaveLiquid, void, (RigidShape* obj, const char* type), (obj, type),
 164   "Called when the vehicle leaves liquid.\n"
 165   "@param obj the Vehicle object\n"
 166   "@param type type of liquid the vehicle has left\n");
 167
 168//----------------------------------------------------------------------------
 169
 170namespace {
 171
 172   static U32 sWorkingQueryBoxStaleThreshold = 10;    // The maximum number of ticks that go by before
 173                                                      // the mWorkingQueryBox is considered stale and
 174                                                      // needs updating.  Set to -1 to disable.
 175
 176   static F32 sWorkingQueryBoxSizeMultiplier = 2.0f;  // How much larger should the mWorkingQueryBox be
 177                                                      // made when updating the working collision list.
 178                                                      // The larger this number the less often the working list
 179                                                      // will be updated due to motion, but any non-static shape
 180                                                      // that moves into the query box will not be noticed.
 181   // Client prediction
 182   const S32 sMaxWarpTicks = 3;           // Max warp duration in ticks
 183   const S32 sMaxPredictionTicks = 30;    // Number of ticks to predict
 184
 185   // Physics and collision constants
 186   static F32 sRestTol = 0.5;             // % of gravity energy to be at rest
 187   static S32 sRestCount = 10;            // Consecutive ticks before comming to rest
 188
 189   const U32 sCollisionMoveMask = ( TerrainObjectType     | PlayerObjectType  | 
 190                                    StaticShapeObjectType | VehicleObjectType |
 191                                    VehicleBlockerObjectType );
 192
 193   const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType
 194   const U32 sClientCollisionMask = sCollisionMoveMask;
 195
 196   void nonFilter(SceneObject* object,void *key)
 197   {
 198      SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
 199      object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
 200   }
 201
 202} // namespace {}
 203
 204
 205// Trigger objects that are not normally collided with.
 206static U32 sTriggerMask = ItemObjectType     |
 207TriggerObjectType  |
 208CorpseObjectType;
 209
 210
 211//----------------------------------------------------------------------------
 212
 213RigidShapeData::RigidShapeData()
 214{
 215   shadowEnable = true;
 216
 217   body.friction = 0;
 218   body.restitution = 1;
 219
 220   minImpactSpeed = 25;
 221   softImpactSpeed = 25;
 222   hardImpactSpeed = 50;
 223   minRollSpeed = 0;
 224
 225   cameraRoll = true;
 226   cameraLag = 0;
 227   cameraDecay = 0;
 228   cameraOffset = 0;
 229
 230   minDrag = 0;
 231   maxDrag = 0;
 232   integration = 1;
 233   collisionTol = 0.1f;
 234   contactTol = 0.1f;
 235   massCenter.set(0,0,0);
 236   massBox.set(0,0,0);
 237
 238   drag = 0.7f;
 239   density = 4;
 240
 241   for (S32 i = 0; i < Body::MaxSounds; i++)
 242      body.sound[i] = 0;
 243
 244   dustEmitter = NULL;
 245   dustID = 0;
 246   triggerDustHeight = 3.0;
 247   dustHeight = 1.0;
 248
 249   dMemset( splashEmitterList, 0, sizeof( splashEmitterList ) );
 250   dMemset( splashEmitterIDList, 0, sizeof( splashEmitterIDList ) );
 251
 252   splashFreqMod = 300.0;
 253   splashVelEpsilon = 0.50;
 254   exitSplashSoundVel = 2.0;
 255   softSplashSoundVel = 1.0;
 256   medSplashSoundVel = 2.0;
 257   hardSplashSoundVel = 3.0;
 258   enablePhysicsRep = true;
 259
 260   dMemset(waterSound, 0, sizeof(waterSound));
 261
 262   dragForce            = 0;
 263   vertFactor           = 0.25;
 264
 265   dustTrailEmitter = NULL;
 266   dustTrailID = 0;
 267}
 268
 269RigidShapeData::~RigidShapeData()
 270{
 271
 272}
 273
 274//----------------------------------------------------------------------------
 275
 276
 277bool RigidShapeData::onAdd()
 278{
 279   if(!Parent::onAdd())
 280      return false;
 281
 282   return true;
 283}
 284
 285
 286bool RigidShapeData::preload(bool server, String &errorStr)
 287{
 288   if (!Parent::preload(server, errorStr))
 289      return false;
 290
 291   // RigidShape objects must define a collision detail
 292   if (!collisionDetails.size() || collisionDetails[0] == -1)
 293   {
 294      Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail");
 295      errorStr = String::ToString("RigidShapeData: Couldn't load shape \"%s\"", mShapeName);
 296      return false;
 297   }
 298
 299   // Resolve objects transmitted from server
 300   if (!server) {
 301      for (S32 i = 0; i < Body::MaxSounds; i++)
 302         sfxResolve( &body.sound[ i ], errorStr );
 303   }
 304
 305   if( !dustEmitter && dustID != 0 )
 306   {
 307      if( !Sim::findObject( dustID, dustEmitter ) )
 308      {
 309         Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(dustEmitter): 0x%x", dustID );
 310      }
 311   }
 312
 313   U32 i;
 314   for( i=0; i<VC_NUM_SPLASH_EMITTERS; i++ )
 315   {
 316      if( !splashEmitterList[i] && splashEmitterIDList[i] != 0 )
 317      {
 318         if( !Sim::findObject( splashEmitterIDList[i], splashEmitterList[i] ) )
 319         {
 320            Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(splashEmitter): 0x%x", splashEmitterIDList[i] );
 321         }
 322      }
 323   }
 324
 325   if (dragForce <= 0.01f) 
 326   {
 327      Con::warnf("RigidShapeData::preload: dragForce must be at least 0.01");
 328      dragForce = 0.01f;
 329   }
 330
 331   if (vertFactor < 0.0f || vertFactor > 1.0f) 
 332   {
 333      Con::warnf("RigidShapeData::preload: vert factor must be [0, 1]");
 334      vertFactor = vertFactor < 0.0f ? 0.0f : 1.0f;
 335   }
 336
 337   if( !dustTrailEmitter && dustTrailID != 0 )
 338   {
 339      if( !Sim::findObject( dustTrailID, dustTrailEmitter ) )
 340      {
 341         Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(dustTrailEmitter): 0x%x", dustTrailID );
 342      }
 343   }
 344
 345   return true;
 346}   
 347
 348
 349//----------------------------------------------------------------------------
 350
 351void RigidShapeData::packData(BitStream* stream)
 352{
 353   Parent::packData(stream);
 354
 355   stream->write(body.restitution);
 356   stream->write(body.friction);
 357   for( U32 i = 0; i < Body::MaxSounds; ++ i )
 358      sfxWrite( stream, body.sound[ i ] );
 359
 360   stream->write(minImpactSpeed);
 361   stream->write(softImpactSpeed);
 362   stream->write(hardImpactSpeed);
 363   stream->write(minRollSpeed);
 364
 365   stream->write(maxDrag);
 366   stream->write(minDrag);
 367   stream->write(integration);
 368   stream->write(collisionTol);
 369   stream->write(contactTol);
 370   mathWrite(*stream,massCenter);
 371   mathWrite(*stream,massBox);
 372
 373   stream->writeFlag(cameraRoll);
 374   stream->write(cameraLag);
 375   stream->write(cameraDecay);
 376   stream->write(cameraOffset);
 377
 378   stream->write( dustHeight );
 379
 380   stream->write(exitSplashSoundVel);
 381   stream->write(softSplashSoundVel);
 382   stream->write(medSplashSoundVel);
 383   stream->write(hardSplashSoundVel);
 384   stream->write(enablePhysicsRep);
 385
 386   // write the water sound profiles
 387   for( U32 i = 0; i < MaxSounds; ++ i )
 388      sfxWrite( stream, waterSound[ i ] );
 389
 390   if (stream->writeFlag( dustEmitter ))
 391      stream->writeRangedU32( dustEmitter->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 392
 393   for( U32 i = 0; i < VC_NUM_SPLASH_EMITTERS; ++ i )
 394   {
 395      if( stream->writeFlag( splashEmitterList[i] != NULL ) )
 396         stream->writeRangedU32( splashEmitterList[i]->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 397   }
 398
 399   stream->write(splashFreqMod);
 400   stream->write(splashVelEpsilon);
 401
 402   stream->write(dragForce);
 403   stream->write(vertFactor);
 404
 405   if (stream->writeFlag( dustTrailEmitter ))
 406      stream->writeRangedU32( dustTrailEmitter->getId(), DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 407}   
 408
 409void RigidShapeData::unpackData(BitStream* stream)
 410{
 411   Parent::unpackData(stream);
 412
 413   stream->read(&body.restitution);
 414   stream->read(&body.friction);
 415
 416   for( U32 i = 0; i < Body::MaxSounds; i++)
 417      sfxRead( stream, &body.sound[ i ] );
 418
 419   stream->read(&minImpactSpeed);
 420   stream->read(&softImpactSpeed);
 421   stream->read(&hardImpactSpeed);
 422   stream->read(&minRollSpeed);
 423
 424   stream->read(&maxDrag);
 425   stream->read(&minDrag);
 426   stream->read(&integration);
 427   stream->read(&collisionTol);
 428   stream->read(&contactTol);
 429   mathRead(*stream,&massCenter);
 430   mathRead(*stream,&massBox);
 431
 432   cameraRoll = stream->readFlag();
 433   stream->read(&cameraLag);
 434   stream->read(&cameraDecay);
 435   stream->read(&cameraOffset);
 436
 437   stream->read( &dustHeight );
 438
 439   stream->read(&exitSplashSoundVel);
 440   stream->read(&softSplashSoundVel);
 441   stream->read(&medSplashSoundVel);
 442   stream->read(&hardSplashSoundVel);
 443   stream->read(&enablePhysicsRep);
 444
 445   // write the water sound profiles
 446   for( U32 i = 0; i < MaxSounds; ++ i )
 447      sfxRead( stream, &waterSound[ i ] );
 448
 449   if( stream->readFlag() )
 450      dustID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 451
 452   for( U32 i = 0; i < VC_NUM_SPLASH_EMITTERS; ++ i )
 453   {
 454      if( stream->readFlag() )
 455         splashEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 456   }
 457
 458   stream->read(&splashFreqMod);
 459   stream->read(&splashVelEpsilon);
 460
 461   stream->read(&dragForce);
 462   stream->read(&vertFactor);
 463
 464   if( stream->readFlag() )
 465      dustTrailID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
 466}   
 467
 468
 469//----------------------------------------------------------------------------
 470
 471void RigidShapeData::initPersistFields()
 472{
 473   addGroup("Physics");
 474   addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, RigidShapeData),
 475      "@brief Creates a representation of the object in the physics plugin.\n");
 476   endGroup("Physics");
 477
 478   addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body.");
 479   addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box.");
 480   addField("bodyRestitution", TypeF32, Offset(body.restitution, RigidShapeData), "The percentage of kinetic energy kept by this object in a collision.");
 481   addField("bodyFriction", TypeF32, Offset(body.friction, RigidShapeData), "How much friction this object has. Lower values will cause the object to appear to be more slippery.");
 482
 483   addField("minImpactSpeed", TypeF32, Offset(minImpactSpeed, RigidShapeData),
 484      "Minimum collision speed to classify collision as impact (triggers onImpact on server object)." );
 485   addField("softImpactSpeed", TypeF32, Offset(softImpactSpeed, RigidShapeData), "Minimum speed at which this object must be travelling for the soft impact sound to be played.");
 486   addField("hardImpactSpeed", TypeF32, Offset(hardImpactSpeed, RigidShapeData), "Minimum speed at which the object must be travelling for the hard impact sound to be played.");
 487   addField("minRollSpeed", TypeF32, Offset(minRollSpeed, RigidShapeData));
 488
 489   addField("maxDrag", TypeF32, Offset(maxDrag, RigidShapeData), "Maximum drag available to this object.");
 490   addField("minDrag", TypeF32, Offset(minDrag, RigidShapeData), "Minimum drag available to this object.");
 491   addField("integration", TypeS32, Offset(integration, RigidShapeData), "Number of physics steps to process per tick.");
 492   addField("collisionTol", TypeF32, Offset(collisionTol, RigidShapeData), "Collision distance tolerance.");
 493   addField("contactTol", TypeF32, Offset(contactTol, RigidShapeData), "Contact velocity tolerance.");
 494   
 495   addGroup( "Forces" );
 496
 497      addField("dragForce",            TypeF32, Offset(dragForce,            RigidShapeData), "Used to simulate the constant drag acting on the object");
 498      addField("vertFactor",           TypeF32, Offset(vertFactor,           RigidShapeData), "The scalar applied to the vertical portion of the velocity drag acting on a object.");
 499   
 500   endGroup( "Forces" );
 501   
 502   addGroup( "Particle Effects" );
 503
 504      addField("dustEmitter",       TYPEID< ParticleEmitterData >(),   Offset(dustEmitter,        RigidShapeData), "Array of pointers to ParticleEmitterData datablocks which will be used to emit particles at object/terrain contact point.\n");
 505      addField("triggerDustHeight", TypeF32,                      Offset(triggerDustHeight,  RigidShapeData), "Maximum height from the ground at which the object will generate dust.\n");
 506      addField("dustHeight",        TypeF32,                      Offset(dustHeight,         RigidShapeData), "Height of dust effects.\n");
 507
 508      addField("dustTrailEmitter",     TYPEID< ParticleEmitterData >(),   Offset(dustTrailEmitter,   RigidShapeData), "Particle emitter used to create a dust trail for the moving object.\n");
 509
 510      addField("splashEmitter",        TYPEID< ParticleEmitterData >(),   Offset(splashEmitterList,     RigidShapeData), VC_NUM_SPLASH_EMITTERS, "Array of pointers to ParticleEmitterData datablocks which will generate splash effects.\n");
 511
 512      addField("splashFreqMod",  TypeF32,                Offset(splashFreqMod,   RigidShapeData), "The simulated frequency modulation of a splash generated by this object. Multiplied along with speed and time elapsed when determining splash emition rate.\n");
 513      addField("splashVelEpsilon", TypeF32,              Offset(splashVelEpsilon, RigidShapeData), "The threshold speed at which we consider the object's movement to have stopped when updating splash effects.\n");
 514      
 515   endGroup( "Particle Effects" );
 516   
 517   addGroup( "Sounds" );
 518
 519      addField("softImpactSound", TypeSFXTrackName, Offset(body.sound[Body::SoftImpactSound], RigidShapeData),
 520         "Sound to play when body impacts with at least softImageSpeed but less than hardImpactSpeed." );
 521      addField("hardImpactSound", TypeSFXTrackName, Offset(body.sound[Body::HardImpactSound], RigidShapeData),
 522         "Sound to play when body impacts with at least hardImpactSpeed." );
 523
 524      addField("exitSplashSoundVelocity", TypeF32,       Offset(exitSplashSoundVel, RigidShapeData), "The minimum velocity at which the exit splash sound will be played when emerging from water.\n");
 525      addField("softSplashSoundVelocity", TypeF32,       Offset(softSplashSoundVel, RigidShapeData),"The minimum velocity at which the soft splash sound will be played when impacting water.\n");
 526      addField("mediumSplashSoundVelocity", TypeF32,     Offset(medSplashSoundVel, RigidShapeData), "The minimum velocity at which the medium splash sound will be played when impacting water.\n");
 527      addField("hardSplashSoundVelocity", TypeF32,       Offset(hardSplashSoundVel, RigidShapeData), "The minimum velocity at which the hard splash sound will be played when impacting water.\n");
 528      addField("exitingWater",      TypeSFXTrackName,   Offset(waterSound[ExitWater],   RigidShapeData), "The AudioProfile will be used to produce sounds when emerging from water.\n");
 529      addField("impactWaterEasy",   TypeSFXTrackName,   Offset(waterSound[ImpactSoft],   RigidShapeData), "The AudioProfile will be used to produce sounds when a soft impact with water occurs.\n");
 530      addField("impactWaterMedium", TypeSFXTrackName,   Offset(waterSound[ImpactMedium],   RigidShapeData), "The AudioProfile will be used to produce sounds when a medium impact with water occurs.\n");
 531      addField("impactWaterHard",   TypeSFXTrackName,   Offset(waterSound[ImpactHard],   RigidShapeData), "The AudioProfile will be used to produce sounds when a hard impact with water occurs.\n");
 532      addField("waterWakeSound",    TypeSFXTrackName,   Offset(waterSound[Wake],   RigidShapeData), "The AudioProfile will be used to produce sounds when a water wake is displayed.\n");
 533      
 534   endGroup( "Sounds" );
 535   
 536   addGroup( "Camera" );
 537
 538      addField("cameraRoll",     TypeBool,       Offset(cameraRoll,     RigidShapeData), "Specifies whether the camera's rotation matrix, and the render eye transform are multiplied during camera updates.\n");
 539      addField("cameraLag",      TypeF32,        Offset(cameraLag,      RigidShapeData), "Scalar amount by which the third person camera lags the object, relative to the object's linear velocity.\n");
 540      addField("cameraDecay",  TypeF32,        Offset(cameraDecay,  RigidShapeData), "Scalar rate at which the third person camera offset decays, per tick.\n");
 541      addField("cameraOffset",   TypeF32,        Offset(cameraOffset,   RigidShapeData), "The vertical offset of the object's camera.\n");
 542      
 543   endGroup( "Camera" );
 544
 545   Parent::initPersistFields();
 546}   
 547
 548
 549//----------------------------------------------------------------------------
 550//----------------------------------------------------------------------------
 551
 552//----------------------------------------------------------------------------
 553
 554RigidShape::RigidShape()
 555{
 556
 557   mNetFlags.set(Ghostable);
 558
 559   mDustTrailEmitter = NULL;
 560
 561   mDataBlock = 0;
 562   // [rene, 27-Apr-11] WTH is a RigidShape a vehicle???
 563   mTypeMask |= VehicleObjectType | DynamicShapeObjectType;
 564
 565   mDelta.pos = Point3F(0,0,0);
 566   mDelta.posVec = Point3F(0,0,0);
 567   mDelta.warpTicks = mDelta.warpCount = 0;
 568   mDelta.dt = 1;
 569   mDelta.move = NullMove;
 570   mPredictionCount = 0;
 571   mDelta.cameraOffset.set(0,0,0);
 572   mDelta.cameraVec.set(0,0,0);
 573   mDelta.cameraRot.set(0,0,0);
 574   mDelta.cameraRotVec.set(0,0,0);
 575
 576   mRigid.linPosition.set(0, 0, 0);
 577   mRigid.linVelocity.set(0, 0, 0);
 578   mRigid.angPosition.identity();
 579   mRigid.angVelocity.set(0, 0, 0);
 580   mRigid.linMomentum.set(0, 0, 0);
 581   mRigid.angMomentum.set(0, 0, 0);
 582   mContacts.clear();
 583
 584   mCameraOffset.set(0,0,0);
 585
 586   dMemset( mDustEmitterList, 0, sizeof( mDustEmitterList ) );
 587   dMemset( mSplashEmitterList, 0, sizeof( mSplashEmitterList ) );
 588
 589   mDisableMove = false; // start frozen by default
 590   restCount = 0;
 591
 592   inLiquid = false;
 593
 594   mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
 595   mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
 596   mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
 597
 598   mPhysicsRep = NULL;
 599}   
 600
 601RigidShape::~RigidShape()
 602{
 603   //
 604}
 605
 606U32 RigidShape::getCollisionMask()
 607{
 608   if (isServerObject())
 609      return sServerCollisionMask;
 610   else
 611      return sClientCollisionMask;
 612}
 613
 614Point3F RigidShape::getVelocity() const
 615{
 616   return mRigid.linVelocity;
 617}
 618
 619//----------------------------------------------------------------------------
 620
 621bool RigidShape::onAdd()
 622{
 623   if (!Parent::onAdd())
 624      return false;
 625
 626   mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
 627   mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
 628
 629   // When loading from a mission script, the base SceneObject's transform
 630   // will have been set and needs to be transfered to the rigid body.
 631   mRigid.setTransform(mObjToWorld);
 632
 633   // Initialize interpolation vars.      
 634   mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
 635   mDelta.pos = mRigid.linPosition;
 636   mDelta.posVec = Point3F(0,0,0);
 637
 638   // Create Emitters on the client
 639   if( isClientObject() )
 640   {
 641      if( mDataBlock->dustEmitter )
 642      {
 643         for( U32 i=0; i<RigidShapeData::VC_NUM_DUST_EMITTERS; i++ )
 644         {
 645            mDustEmitterList[i] = new ParticleEmitter;
 646            mDustEmitterList[i]->onNewDataBlock( mDataBlock->dustEmitter, false );
 647            if( !mDustEmitterList[i]->registerObject() )
 648            {
 649               Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
 650               delete mDustEmitterList[i];
 651               mDustEmitterList[i] = NULL;
 652            }
 653         }
 654      }
 655
 656      for( U32 j=0; j<RigidShapeData::VC_NUM_SPLASH_EMITTERS; j++ )
 657      {
 658         if( mDataBlock->splashEmitterList[j] )
 659         {
 660            mSplashEmitterList[j] = new ParticleEmitter;
 661            mSplashEmitterList[j]->onNewDataBlock( mDataBlock->splashEmitterList[j], false );
 662            if( !mSplashEmitterList[j]->registerObject() )
 663            {
 664               Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() );
 665               delete mSplashEmitterList[j];
 666               mSplashEmitterList[j] = NULL;
 667            }
 668
 669         }
 670      }
 671   }
 672
 673   // Create a new convex.
 674   AssertFatal(mDataBlock->collisionDetails[0] != -1, "Error, a rigid shape must have a collision-1 detail!");
 675   mConvex.mObject    = this;
 676   mConvex.pShapeBase = this;
 677   mConvex.hullId     = 0;
 678   mConvex.box        = mObjBox;
 679   mConvex.box.minExtents.convolve(mObjScale);
 680   mConvex.box.maxExtents.convolve(mObjScale);
 681   mConvex.findNodeTransform();
 682   _createPhysics();
 683
 684   addToScene();
 685
 686
 687   if( !isServerObject() )
 688   {
 689      if( mDataBlock->dustTrailEmitter )
 690      {
 691         mDustTrailEmitter = new ParticleEmitter;
 692         mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter, false );
 693         if( !mDustTrailEmitter->registerObject() )
 694         {
 695            Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
 696            delete mDustTrailEmitter;
 697            mDustTrailEmitter = NULL;
 698         }
 699      }
 700   }
 701
 702
 703   if (isServerObject())
 704      scriptOnAdd();
 705
 706   return true;
 707}
 708
 709void RigidShape::onRemove()
 710{
 711   scriptOnRemove();
 712   removeFromScene();
 713
 714   U32 i=0;
 715   for( i=0; i<RigidShapeData::VC_NUM_DUST_EMITTERS; i++ )
 716   {
 717      if( mDustEmitterList[i] )
 718      {
 719         mDustEmitterList[i]->deleteWhenEmpty();
 720         mDustEmitterList[i] = NULL;
 721      }
 722   }
 723
 724   for( i=0; i<RigidShapeData::VC_NUM_SPLASH_EMITTERS; i++ )
 725   {
 726      if( mSplashEmitterList[i] )
 727      {
 728         mSplashEmitterList[i]->deleteWhenEmpty();
 729         mSplashEmitterList[i] = NULL;
 730      }
 731   }
 732
 733   mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f);
 734   mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f);
 735   Parent::onRemove();
 736}
 737
 738void RigidShape::_createPhysics()
 739{
 740   SAFE_DELETE(mPhysicsRep);
 741
 742   if (!PHYSICSMGR || !mDataBlock->enablePhysicsRep)
 743      return;
 744
 745   TSShape* shape = mShapeInstance->getShape();
 746   PhysicsCollision* colShape = NULL;
 747   colShape = shape->buildColShape(false, getScale());
 748
 749   if (colShape)
 750   {
 751      PhysicsWorld* world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
 752      mPhysicsRep = PHYSICSMGR->createBody();
 753      mPhysicsRep->init(colShape, 0, PhysicsBody::BF_KINEMATIC, this, world);
 754      mPhysicsRep->setTransform(getTransform());
 755   }
 756}
 757
 758//----------------------------------------------------------------------------
 759void RigidShape::processTick(const Move* move)
 760{     
 761   PROFILE_SCOPE(RigidShape_ProcessTick);
 762
 763   Parent::processTick(move);
 764   if ( isMounted() )
 765      return;
 766
 767   // Warp to catch up to server
 768   if (mDelta.warpCount < mDelta.warpTicks) 
 769   {
 770      mDelta.warpCount++;
 771
 772      // Set new pos.
 773      mObjToWorld.getColumn(3,&mDelta.pos);
 774      mDelta.pos += mDelta.warpOffset;
 775      mDelta.rot[0] = mDelta.rot[1];
 776      mDelta.rot[1].interpolate(mDelta.warpRot[0],mDelta.warpRot[1],F32(mDelta.warpCount)/<a href="/coding/class/classrigidshape/#classrigidshape_1a4a7d0e60ba7dff122314e6ac9034e3bb">mDelta</a>.warpTicks);
 777      setPosition(mDelta.pos,mDelta.rot[1]);
 778
 779      // Pos backstepping
 780      mDelta.posVec.x = -mDelta.warpOffset.x;
 781      mDelta.posVec.y = -mDelta.warpOffset.y;
 782      mDelta.posVec.z = -mDelta.warpOffset.z;
 783   }
 784   else 
 785   {
 786      if (!move) 
 787      {
 788         if (isGhost()) 
 789         {
 790            // If we haven't run out of prediction time,
 791            // predict using the last known move.
 792            if (mPredictionCount-- <= 0)
 793               return;
 794            move = &mDelta.move;
 795         }
 796         else
 797            move = &NullMove;
 798      }
 799
 800      // Process input move
 801      updateMove(move);
 802
 803      // Save current rigid state interpolation
 804      mDelta.posVec = mRigid.linPosition;
 805      mDelta.rot[0] = mRigid.angPosition;
 806
 807      // Update the physics based on the integration rate
 808      S32 count = mDataBlock->integration;
 809      --mWorkingQueryBoxCountDown;
 810
 811      if (!mDisableMove)
 812         updateWorkingCollisionSet(getCollisionMask());
 813      for (U32 i = 0; i < count; i++)
 814         updatePos(TickSec / count);
 815
 816      // Wrap up interpolation info
 817      mDelta.pos     = mRigid.linPosition;
 818      mDelta.posVec -= mRigid.linPosition;
 819      mDelta.rot[1]  = mRigid.angPosition;
 820
 821      // Update container database
 822      setPosition(mRigid.linPosition, mRigid.angPosition);
 823      setMaskBits(PositionMask);
 824      updateContainer();
 825
 826      //TODO: Only update when position has actually changed
 827      //no need to check if mDataBlock->enablePhysicsRep is false as mPhysicsRep will be NULL if it is
 828      if (mPhysicsRep)
 829         mPhysicsRep->moveKinematicTo(getTransform());
 830   }
 831}
 832
 833void RigidShape::interpolateTick(F32 dt)
 834{     
 835   Parent::interpolateTick(dt);
 836   if ( isMounted() )
 837      return;
 838
 839   if(dt == 0.0f)
 840      setRenderPosition(mDelta.pos, mDelta.rot[1]);
 841   else
 842   {
 843      QuatF rot;
 844      rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt);
 845      Point3F pos = mDelta.pos + mDelta.posVec * dt;
 846      setRenderPosition(pos,rot);
 847   }
 848   mDelta.dt = dt;
 849}
 850
 851void RigidShape::advanceTime(F32 dt)
 852{     
 853   Parent::advanceTime(dt);
 854
 855   updateFroth(dt);
 856
 857   if ( isMounted() )
 858      return;
 859
 860   // Update 3rd person camera offset.  Camera update is done
 861   // here as it's a client side only animation.
 862   mCameraOffset -=
 863      (mCameraOffset * mDataBlock->cameraDecay +
 864      mRigid.linVelocity * mDataBlock->cameraLag) * dt;
 865}
 866
 867
 868//----------------------------------------------------------------------------
 869
 870bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload)
 871{
 872   mDataBlock = dynamic_cast<RigidShapeData*>(dptr);
 873   if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
 874      return false;
 875
 876   // Update Rigid Info
 877   mRigid.mass = mDataBlock->mass;
 878   mRigid.oneOverMass = 1 / mRigid.mass;
 879   mRigid.friction = mDataBlock->body.friction;
 880   mRigid.restitution = mDataBlock->body.restitution;
 881   mRigid.setCenterOfMass(mDataBlock->massCenter);
 882
 883   // Ignores massBox, just set sphere for now. Derived objects
 884   // can set what they want.
 885   mRigid.setObjectInertia();
 886
 887   scriptOnNewDataBlock();
 888
 889   return true;
 890}
 891
 892
 893//----------------------------------------------------------------------------
 894
 895void RigidShape::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
 896{
 897   *min = mDataBlock->cameraMinDist;
 898   *max = mDataBlock->cameraMaxDist;
 899
 900   off->set(0,0,mDataBlock->cameraOffset);
 901   rot->identity();
 902}
 903
 904
 905//----------------------------------------------------------------------------
 906
 907void RigidShape::getCameraTransform(F32* pos,MatrixF* mat)
 908{
 909   // Returns camera to world space transform
 910   // Handles first person / third person camera position
 911   if (isServerObject() && mShapeInstance)
 912      mShapeInstance->animateNodeSubtrees(true);
 913
 914   if (*pos == 0) 
 915   {
 916      getRenderEyeTransform(mat);
 917      return;
 918   }
 919
 920   // Get the shape's camera parameters.
 921   F32 min,max;
 922   MatrixF rot;
 923   Point3F offset;
 924   getCameraParameters(&min,&max,&offset,&rot);
 925
 926   // Start with the current eye position
 927   MatrixF eye;
 928   getRenderEyeTransform(&eye);
 929
 930   // Build a transform that points along the eye axis
 931   // but where the Z axis is always up.
 932   if (mDataBlock->cameraRoll)
 933      mat->mul(eye,rot);
 934   else 
 935   {
 936      MatrixF cam(1);
 937      VectorF x,y,z(0,0,1);
 938      eye.getColumn(1, &y);
 939      
 940      mCross(y, z, &x);
 941      x.normalize();
 942      mCross(x, y, &z);
 943      z.normalize();
 944
 945      cam.setColumn(0,x);
 946      cam.setColumn(1,y);
 947      cam.setColumn(2,z);
 948      mat->mul(cam,rot);
 949   }
 950
 951   // Camera is positioned straight back along the eye's -Y axis.
 952   // A ray is cast to make sure the camera doesn't go through
 953   // anything solid.
 954   VectorF vp,vec;
 955   vp.x = vp.z = 0;
 956   vp.y = -(max - min) * *pos;
 957   eye.mulV(vp,&vec);
 958
 959   // Use the camera node as the starting position if it exists.
 960   Point3F osp,sp;
 961   if (mDataBlock->cameraNode != -1) 
 962   {
 963      mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
 964      getRenderTransform().mulP(osp,&sp);
 965   }
 966   else
 967      eye.getColumn(3,&sp);
 968
 969   // Make sure we don't hit ourself...
 970   disableCollision();
 971   if (isMounted())
 972      getObjectMount()->disableCollision();
 973
 974   // Cast the ray into the container database to see if we're going
 975   // to hit anything.
 976   RayInfo collision;
 977   Point3F ep = sp + vec + offset + mCameraOffset;
 978   if (mContainer->castRay(sp, ep,
 979      ~(WaterObjectType | GameBaseObjectType | DefaultObjectType),
 980      &collision) == true) 
 981   {
 982
 983         // Shift the collision point back a little to try and
 984         // avoid clipping against the front camera plane.
 985         F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1;
 986         if (t > 0.0f)
 987            ep = sp + offset + mCameraOffset + (vec * t);
 988         else
 989            eye.getColumn(3,&ep);
 990      }
 991      mat->setColumn(3,ep);
 992
 993      // Re-enable our collision.
 994      if (isMounted())
 995         getObjectMount()->enableCollision();
 996      enableCollision();
 997
 998   // Apply Camera FX.
 999   mat->mul( gCamFXMgr.getTrans() );
1000}
1001
1002
1003//----------------------------------------------------------------------------
1004
1005void RigidShape::getVelocity(const Point3F& r, Point3F* v)
1006{
1007   mRigid.getVelocity(r, v);
1008}
1009
1010void RigidShape::applyImpulse(const Point3F &pos, const Point3F &impulse)
1011{
1012   Point3F r;
1013   mRigid.getOriginVector(pos,&r);
1014   mRigid.applyImpulse(r, impulse);
1015}
1016
1017
1018//----------------------------------------------------------------------------
1019
1020void RigidShape::updateMove(const Move* move)
1021{
1022   mDelta.move = *move;
1023}
1024
1025//----------------------------------------------------------------------------
1026
1027void RigidShape::setPosition(const Point3F& pos,const QuatF& rot)
1028{
1029   MatrixF mat;
1030   rot.setMatrix(&mat);
1031   mat.setColumn(3,pos);
1032   Parent::setTransform(mat);
1033}
1034
1035void RigidShape::setRenderPosition(const Point3F& pos, const QuatF& rot)
1036{
1037   MatrixF mat;
1038   rot.setMatrix(&mat);
1039   mat.setColumn(3,pos);
1040   Parent::setRenderTransform(mat);
1041}
1042
1043void RigidShape::setTransform(const MatrixF& newMat)
1044{
1045   mRigid.setTransform(newMat);
1046   Parent::setTransform(newMat);
1047   mRigid.atRest = false;
1048   mContacts.clear();
1049}
1050
1051void RigidShape::forceClientTransform()
1052{
1053   setMaskBits(ForceMoveMask);
1054}
1055
1056
1057//-----------------------------------------------------------------------------
1058
1059void RigidShape::disableCollision()
1060{
1061   Parent::disableCollision();
1062}
1063
1064void RigidShape::enableCollision()
1065{
1066   Parent::enableCollision();
1067}   
1068
1069
1070//----------------------------------------------------------------------------
1071/** Update the physics
1072*/
1073
1074void RigidShape::updatePos(F32 dt)
1075{
1076   PROFILE_SCOPE(RigidShape_UpdatePos);
1077
1078   Point3F origVelocity = mRigid.linVelocity;
1079
1080   // Update internal forces acting on the body.
1081   mRigid.clearForces();
1082   updateForces(dt);
1083
1084   // Update collision information based on our current pos.
1085   bool collided = false;
1086   if (!mRigid.atRest && !mDisableMove)
1087   {
1088      collided = updateCollision(dt);
1089
1090      // Now that all the forces have been processed, lets
1091      // see if we're at rest.  Basically, if the kinetic energy of
1092      // the rigid body is less than some percentage of the energy added
1093      // by gravity for a short period, we're considered at rest.
1094      // This should really be part of the rigid class...
1095      if (mCollisionList.getCount())
1096      {
1097         F32 k = mRigid.getKineticEnergy();
1098         F32 G = mNetGravity * dt;
1099         F32 Kg = 0.5 * mRigid.mass * G * G;
1100         if (k < sRestTol * Kg && ++restCount > sRestCount)
1101            mRigid.setAtRest();
1102      }
1103      else
1104         restCount = 0;
1105   }
1106
1107   // Integrate forward
1108   if (!mRigid.atRest && !mDisableMove)
1109      mRigid.integrate(dt);
1110
1111   // Deal with client and server scripting, sounds, etc.
1112   if (isServerObject())
1113   {
1114
1115      // Check triggers and other objects that we normally don't
1116      // collide with.  This function must be called before notifyCollision
1117      // as it will queue collision.
1118      checkTriggers();
1119
1120      // Invoke the onCollision notify callback for all the objects
1121      // we've just hit.
1122      notifyCollision();
1123
1124      // Server side impact script callback
1125      if (collided)
1126      {
1127         VectorF collVec = mRigid.linVelocity - origVelocity;
1128         F32 collSpeed = collVec.len();
1129         if (collSpeed > mDataBlock->minImpactSpeed)
1130            onImpact(collVec);
1131      }
1132
1133      // Water script callbacks
1134      if (!inLiquid && mWaterCoverage != 0.0f)
1135      {
1136         mDataBlock->onEnterLiquid_callback(this, mWaterCoverage, mLiquidType.c_str());
1137         inLiquid = true;
1138      }
1139      else if (inLiquid && mWaterCoverage == 0.0f)
1140      {
1141         mDataBlock->onLeaveLiquid_callback(this, mLiquidType.c_str());
1142         inLiquid = false;
1143      }
1144
1145   }
1146   else {
1147
1148      // Play impact sounds on the client.
1149      if (collided) {
1150         F32 collSpeed = (mRigid.linVelocity - origVelocity).len();
1151         S32 impactSound = -1;
1152         if (collSpeed >= mDataBlock->hardImpactSpeed)
1153            impactSound = RigidShapeData::Body::HardImpactSound;
1154         else
1155            if (collSpeed >= mDataBlock->softImpactSpeed)
1156               impactSound = RigidShapeData::Body::SoftImpactSound;
1157
1158         if (impactSound != -1 && mDataBlock->body.sound[impactSound] != NULL)
1159            SFX->playOnce(mDataBlock->body.sound[impactSound], &getTransform());
1160      }
1161
1162      // Water volume sounds
1163      F32 vSpeed = getVelocity().len();
1164      if (!inLiquid && mWaterCoverage >= 0.8f) {
1165         if (vSpeed >= mDataBlock->hardSplashSoundVel)
1166            SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactHard], &getTransform());
1167         else
1168            if (vSpeed >= mDataBlock->medSplashSoundVel)
1169               SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactMedium], &getTransform());
1170            else
1171               if (vSpeed >= mDataBlock->softSplashSoundVel)
1172                  SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactSoft], &getTransform());
1173         inLiquid = true;
1174      }
1175      else
1176         if (inLiquid && mWaterCoverage < 0.8f) {
1177            if (vSpeed >= mDataBlock->exitSplashSoundVel)
1178               SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ExitWater], &getTransform());
1179            inLiquid = false;
1180         }
1181   }
1182}
1183
1184//----------------------------------------------------------------------------
1185
1186void RigidShape::updateForces(F32 dt)
1187{
1188   if (mDisableMove) return;
1189
1190   Point3F torque(0, 0, 0);
1191   Point3F force(0, 0, mRigid.mass * mNetGravity);
1192
1193   // Apply drag
1194   Point3F vertDrag = mRigid.linVelocity*Point3F(1, 1, mDataBlock->vertFactor);
1195   force -= vertDrag * mDataBlock->dragForce;
1196
1197   // Add in physical zone force
1198   force += mAppliedForce;
1199
1200   force  -= mRigid.linVelocity * mDrag;
1201   torque -= mRigid.angMomentum * mDrag;
1202
1203   mRigid.force  = force;
1204   mRigid.torque = torque;
1205
1206   // If we're still atRest, make sure we're not accumulating anything
1207   if (mRigid.atRest)
1208      mRigid.setAtRest();
1209}
1210
1211
1212//-----------------------------------------------------------------------------
1213/** Update collision information
1214Update the convex state and check for collisions. If the object is in
1215collision, impact and contact forces are generated.
1216*/
1217
1218bool RigidShape::updateCollision(F32 dt)
1219{
1220   // Update collision information
1221   MatrixF mat,cmat;
1222   mConvex.transform = &mat;
1223   mRigid.getTransform(&mat);
1224   cmat = mConvex.getTransform();
1225
1226   mCollisionList.clear();
1227   CollisionState *state = mConvex.findClosestState(cmat, getScale(), mDataBlock->collisionTol);
1228   if (state && state->mDist <= mDataBlock->collisionTol) 
1229   {
1230      //resolveDisplacement(ns,state,dt);
1231      mConvex.getCollisionInfo(cmat, getScale(), &mCollisionList, mDataBlock->collisionTol);
1232   }
1233
1234   // Resolve collisions
1235   bool collided = resolveCollision(mRigid,mCollisionList);
1236   resolveContacts(mRigid,mCollisionList,dt);
1237   return collided;
1238}
1239
1240
1241//----------------------------------------------------------------------------
1242/** Resolve collision impacts
1243Handle collision impacts, as opposed to contacts. Impulses are calculated based
1244on standard collision resolution formulas.
1245*/
1246bool RigidShape::resolveCollision(Rigid&  ns,CollisionList& cList)
1247{
1248   // Apply impulses to resolve collision
1249   bool collided = false;
1250   for (S32 i = 0; i < cList.getCount(); i++)
1251   {
1252      Collision& c = cList[i];
1253      if (c.distance < mDataBlock->collisionTol)
1254      {
1255         // Velocity into surface
1256         Point3F v, r;
1257         ns.getOriginVector(c.point, &r);
1258         ns.getVelocity(r, &v);
1259         F32 vn = mDot(v, c.normal);
1260
1261         // Only interested in velocities greater than sContactTol,
1262         // velocities less than that will be dealt with as contacts
1263         // "constraints".
1264         if (vn < -mDataBlock->contactTol)
1265         {
1266
1267            // Apply impulses to the rigid body to keep it from
1268            // penetrating the surface.
1269            if (c.object->getTypeMask() & VehicleObjectType)
1270            {
1271                  RigidShape* otherRigid = dynamic_cast<RigidShape*>(c.object);
1272                  if (otherRigid)
1273                     ns.resolveCollision(cList[i].point, cList[i].normal, &otherRigid->mRigid);
1274                  else
1275                     ns.resolveCollision(cList[i].point, cList[i].normal);
1276            }
1277            else ns.resolveCollision(cList[i].point, cList[i].normal);
1278            collided = true;
1279
1280            // Keep track of objects we collide with
1281            if (!isGhost() && c.object->getTypeMask() & ShapeBaseObjectType)
1282            {
1283               ShapeBase* col = static_cast<ShapeBase*>(c.object);
1284               queueCollision(col, v - col->getVelocity());
1285            }
1286         }
1287      }
1288   }
1289
1290   return collided;
1291}
1292
1293//----------------------------------------------------------------------------
1294/** Resolve contact forces
1295Resolve contact forces using the "penalty" method. Forces are generated based
1296on the depth of penetration and the moment of inertia at the point of contact.
1297*/
1298bool RigidShape::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt)
1299{
1300   // Use spring forces to manage contact constraints.
1301   bool collided = false;
1302   Point3F t,p(0,0,0),l(0,0,0);
1303   for (S32 i = 0; i < cList.getCount(); i++) 
1304   {
1305      const Collision& c = cList[i];
1306      if (c.distance < mDataBlock->collisionTol) 
1307      {
1308
1309         // Velocity into the surface
1310         Point3F v,r;
1311         ns.getOriginVector(c.point,&r);
1312         ns.getVelocity(r,&v);
1313         F32 vn = mDot(v,c.normal);
1314
1315         // Only interested in velocities less than mDataBlock->contactTol,
1316         // velocities greater than that are dealt with as collisions.
1317         if (mFabs(vn) < mDataBlock->contactTol) 
1318         {
1319            collided = true;
1320
1321            // Penetration force. This is actually a spring which
1322            // will seperate the body from the collision surface.
1323            F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r,c.normal));
1324            F32 s = (mDataBlock->collisionTol - c.distance) * zi - ((vn / mDataBlock->contactTol) * zi);
1325            Point3F f = c.normal * s;
1326
1327            // Friction impulse, calculated as a function of the
1328            // amount of force it would take to stop the motion
1329            // perpendicular to the normal.
1330            Point3F uv = v - (c.normal * vn);
1331            F32 ul = uv.len();
1332            if (s > 0 && ul) 
1333            {
1334               uv /= -ul;
1335               F32 u = ul * ns.getZeroImpulse(r,uv);
1336               s *= mRigid.friction;
1337               if (u > s)
1338                  u = s;
1339               f += uv * u;
1340            }
1341
1342            // Accumulate forces
1343            p += f;
1344            mCross(r,f,&t);
1345            l += t;
1346         }
1347      }
1348   }
1349
1350   // Contact constraint forces act over time...
1351   ns.linMomentum += p * dt;
1352   ns.angMomentum += l * dt;
1353   ns.updateVelocity();
1354   return true;
1355}
1356
1357
1358//----------------------------------------------------------------------------
1359
1360bool RigidShape::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt)
1361{
1362   SceneObject* obj = (state->mA->getObject() == this)?
1363      state->mB->getObject(): state->mA->getObject();
1364
1365   if (obj->isDisplacable() && ((obj->getTypeMask() & ShapeBaseObjectType) != 0))
1366   {
1367      // Try to displace the object by the amount we're trying to move
1368      Point3F objNewMom = ns.linVelocity * obj->getMass() * 1.1f;
1369      Point3F objOldMom = obj->getMomentum();
1370      Point3F objNewVel = objNewMom / obj->getMass();
1371
1372      Point3F myCenter;
1373      Point3F theirCenter;
1374      getWorldBox().getCenter(&myCenter);
1375      obj->getWorldBox().getCenter(&theirCenter);
1376      if (mDot(myCenter - theirCenter, objNewMom) >= 0.0f || objNewVel.len() < 0.01)
1377      {
1378         objNewMom = (theirCenter - myCenter);
1379         objNewMom.normalize();
1380         objNewMom *= 1.0f * obj->getMass();
1381         objNewVel = objNewMom / obj->getMass();
1382      }
1383
1384      obj->setMomentum(objNewMom);
1385      if (obj->displaceObject(objNewVel * 1.1f * dt) == true)
1386      {
1387         // Queue collision and change in velocity
1388         VectorF dv = (objOldMom - objNewMom) / obj->getMass();
1389         queueCollision(static_cast<ShapeBase*>(obj), dv);
1390         return true;
1391      }
1392   }
1393
1394   return false;
1395}   
1396
1397
1398//----------------------------------------------------------------------------
1399
1400void RigidShape::updateWorkingCollisionSet(const U32 mask)
1401{
1402   PROFILE_SCOPE( Vehicle_UpdateWorkingCollisionSet );
1403
1404   // First, we need to adjust our velocity for possible acceleration.  It is assumed
1405   // that we will never accelerate more than 20 m/s for gravity, plus 30 m/s for
1406   // jetting, and an equivalent 10 m/s for vehicle accel.  We also assume that our
1407   // working list is updated on a Tick basis, which means we only expand our box by
1408   // the possible movement in that tick, plus some extra for caching purposes
1409   Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
1410   F32 len = (mRigid.linVelocity.len() + 50) * TickSec;
1411   F32 l = (len * 1.1) + 0.1;  // fudge factor
1412   convexBox.minExtents -= Point3F(l, l, l);
1413   convexBox.maxExtents += Point3F(l, l, l);
1414
1415   // Check to see if it is actually necessary to construct the new working list,
1416   // or if we can use the cached version from the last query.  We use the x
1417   // component of the min member of the mWorkingQueryBox, which is lame, but
1418   // it works ok.
1419   bool updateSet = false;
1420
1421   // Check containment
1422   if ((sWorkingQueryBoxStaleThreshold == -1 || mWorkingQueryBoxCountDown > 0) && mWorkingQueryBox.minExtents.x != -1e9f)
1423   {
1424      if (mWorkingQueryBox.isContained(convexBox) == false)
1425         // Needed region is outside the cached region.  Update it.
1426         updateSet = true;
1427   }
1428   else
1429   {
1430      // Must update
1431      updateSet = true;
1432   }
1433
1434   // Actually perform the query, if necessary
1435   if (updateSet == true)
1436   {
1437      mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold;
1438
1439      const Point3F  lPoint( sWorkingQueryBoxSizeMultiplier * l );
1440      mWorkingQueryBox = convexBox;
1441      mWorkingQueryBox.minExtents -= lPoint;
1442      mWorkingQueryBox.maxExtents += lPoint;
1443
1444      disableCollision();
1445      mConvex.updateWorkingList(mWorkingQueryBox, mask);
1446      enableCollision();
1447   }
1448}
1449
1450//----------------------------------------------------------------------------
1451/** Check collisions with trigger and items
1452Perform a container search using the current bounding box
1453of the main body, wheels are not included.  This method should
1454only be called on the server.
1455*/
1456void RigidShape::checkTriggers()
1457{
1458   Box3F bbox = mConvex.getBoundingBox(getTransform(), getScale());
1459   gServerContainer.findObjects(bbox,sTriggerMask,findCallback,this);
1460}
1461
1462/** The callback used in by the checkTriggers() method.
1463The checkTriggers method uses a container search which will
1464invoke this callback on each obj that matches.
1465*/
1466void RigidShape::findCallback(SceneObject* obj,void *key)
1467{
1468   RigidShape* shape = reinterpret_cast<RigidShape*>(key);
1469   U32 objectMask = obj->getTypeMask();
1470
1471   // Check: triggers, corpses and items, basically the same things
1472   // that the player class checks for
1473   if (objectMask & TriggerObjectType) {
1474      Trigger* pTrigger = static_cast<Trigger*>(obj);
1475      pTrigger->potentialEnterObject(shape);
1476   }
1477   else if (objectMask & CorpseObjectType) {
1478      ShapeBase* col = static_cast<ShapeBase*>(obj);
1479      shape->queueCollision(col,shape->getVelocity() - col->getVelocity());
1480   }
1481   else if (objectMask & ItemObjectType) {
1482      Item* item = static_cast<Item*>(obj);
1483      if (shape != item->getCollisionObject())
1484         shape->queueCollision(item,shape->getVelocity() - item->getVelocity());
1485   }
1486}
1487
1488
1489//----------------------------------------------------------------------------
1490
1491void RigidShape::writePacketData(GameConnection *connection, BitStream *stream)
1492{
1493   Parent::writePacketData(connection, stream);
1494
1495   mathWrite(*stream, mRigid.linPosition);
1496   mathWrite(*stream, mRigid.angPosition);
1497   mathWrite(*stream, mRigid.linMomentum);
1498   mathWrite(*stream, mRigid.angMomentum);
1499   stream->writeFlag(mRigid.atRest);
1500   stream->writeFlag(mContacts.getCount() == 0);
1501
1502   stream->writeFlag(mDisableMove);
1503   stream->setCompressionPoint(mRigid.linPosition);
1504}
1505
1506void RigidShape::readPacketData(GameConnection *connection, BitStream *stream)
1507{
1508   Parent::readPacketData(connection, stream);
1509
1510   mathRead(*stream, &mRigid.linPosition);
1511   mathRead(*stream, &mRigid.angPosition);
1512   mathRead(*stream, &mRigid.linMomentum);
1513   mathRead(*stream, &mRigid.angMomentum);
1514   mRigid.atRest = stream->readFlag();
1515   if (stream->readFlag())
1516      mContacts.clear();
1517   mRigid.updateInertialTensor();
1518   mRigid.updateVelocity();
1519
1520   mDisableMove = stream->readFlag();
1521   stream->setCompressionPoint(mRigid.linPosition);
1522}   
1523
1524
1525//----------------------------------------------------------------------------
1526
1527U32 RigidShape::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
1528{
1529   U32 retMask = Parent::packUpdate(con, mask, stream);
1530
1531   // The rest of the data is part of the control object packet update.
1532   // If we're controlled by this client, we don't need to send it.
1533   if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
1534      return retMask;
1535
1536   mDelta.move.pack(stream);
1537
1538   if (stream->writeFlag(mask & PositionMask))
1539   {
1540      stream->writeFlag(mask & ForceMoveMask);
1541
1542      stream->writeCompressedPoint(mRigid.linPosition);
1543      mathWrite(*stream, mRigid.angPosition);
1544      mathWrite(*stream, mRigid.linMomentum);
1545      mathWrite(*stream, mRigid.angMomentum);
1546      stream->writeFlag(mRigid.atRest);
1547   }
1548   
1549   if(stream->writeFlag(mask & FreezeMask))
1550      stream->writeFlag(mDisableMove);
1551
1552   return retMask;
1553}   
1554
1555void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream)
1556{
1557   Parent::unpackUpdate(con,stream);
1558
1559   if (stream->readFlag())
1560      return;
1561
1562   mDelta.move.unpack(stream);
1563
1564   if (stream->readFlag()) 
1565   {
1566      // Check if we need to jump to the given transform
1567      // rather than interpolate to it.
1568      bool forceUpdate = stream->readFlag();
1569
1570      mPredictionCount = sMaxPredictionTicks;
1571      F32 speed = mRigid.linVelocity.len();
1572      mDelta.warpRot[0] = mRigid.angPosition;
1573
1574      // Read in new position and momentum values
1575      stream->readCompressedPoint(&mRigid.linPosition);
1576      mathRead(*stream, &mRigid.angPosition);
1577      mathRead(*stream, &mRigid.linMomentum);
1578      mathRead(*stream, &mRigid.angMomentum);
1579      mRigid.atRest = stream->readFlag();
1580      mRigid.updateVelocity();
1581
1582      if (!forceUpdate && isProperlyAdded()) 
1583      {
1584         // Determine number of ticks to warp based on the average
1585         // of the client and server velocities.
1586         Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
1587         mDelta.warpOffset = mRigid.linPosition - cp;
1588
1589         // Calc the distance covered in one tick as the average of
1590         // the old speed and the new speed from the server.
1591         F32 dt,as = (speed + mRigid.linVelocity.len()) * 0.5 * TickSec;
1592
1593         // Cal how many ticks it will take to cover the warp offset.
1594         // If it's less than what's left in the current tick, we'll just
1595         // warp in the remaining time.
1596         if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks)
1597            dt = mDelta.dt + sMaxWarpTicks;
1598         else
1599            dt = (dt <= mDelta.dt)? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt;
1600
1601         // Adjust current frame interpolation
1602         if (mDelta.dt) 
1603         {
1604            mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt));
1605            mDelta.posVec = (cp - mDelta.pos) / mDelta.dt;
1606            QuatF cr;
1607            cr.interpolate(mDelta.rot[1],mDelta.rot[0],mDelta.dt);
1608            mDelta.rot[1].interpolate(cr,mRigid.angPosition,mDelta.dt / dt);
1609            mDelta.rot[0].extrapolate(mDelta.rot[1],cr,mDelta.dt);
1610         }
1611
1612         // Calculated multi-tick warp
1613         mDelta.warpCount = 0;
1614         mDelta.warpTicks = (S32)(mFloor(dt));
1615         if (mDelta.warpTicks) 
1616         {
1617            mDelta.warpOffset = mRigid.linPosition - mDelta.pos;
1618            mDelta.warpOffset /= mDelta.warpTicks;
1619            mDelta.warpRot[0] = mDelta.rot[1];
1620            mDelta.warpRot[1] = mRigid.angPosition;
1621         }
1622      }
1623      else 
1624      {
1625         // Set the shape to the server position
1626         mDelta.dt  = 0;
1627         mDelta.pos = mRigid.linPosition;
1628         mDelta.posVec.set(0,0,0);
1629         mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
1630         mDelta.warpCount = mDelta.warpTicks = 0;
1631         setPosition(mRigid.linPosition, mRigid.angPosition);
1632      }
1633   }
1634   
1635   if(stream->readFlag())
1636      mDisableMove = stream->readFlag();
1637}
1638
1639
1640//----------------------------------------------------------------------------
1641
1642//----------------------------------------------------------------------------
1643
1644void RigidShape::consoleInit()
1645{
1646   Con::addVariable("$rigidPhysics::workingQueryBoxStaleThreshold", TypeS32, &sWorkingQueryBoxStaleThreshold,
1647      "@brief The maximum number of ticks that go by before the mWorkingQueryBox is considered stale and needs updating.\n\n"
1648      "Other factors can cause the collision working query box to become invalidated, such as the rigid body moving far "
1649      "enough outside of this cached box.  The smaller this number, the more times the working list of triangles that are "
1650      "considered for collision is refreshed.  This has the greatest impact with colliding with high triangle count meshes.\n\n"
1651      "@note Set to -1 to disable any time-based forced check.\n\n"
1652      "@ingroup GameObjects\n");
1653
1654   Con::addVariable("$rigidPhysics::workingQueryBoxSizeMultiplier", TypeF32, &sWorkingQueryBoxSizeMultiplier,
1655      "@brief How much larger the mWorkingQueryBox should be made when updating the working collision list.\n\n"
1656      "The larger this number the less often the working list will be updated due to motion, but any non-static shape that "
1657      "moves into the query box will not be noticed.\n\n"
1658      "@ingroup GameObjects\n");
1659}
1660
1661void RigidShape::initPersistFields()
1662{
1663   Parent::initPersistFields();
1664}
1665
1666//----------------------------------------------------------------------------
1667
1668void RigidShape::updateLiftoffDust( F32 dt )
1669{
1670   Point3F offset( 0.0, 0.0, mDataBlock->dustHeight );
1671   emitDust( mDustEmitterList[ 0 ], mDataBlock->triggerDustHeight, offset,
1672             ( U32 )( dt * 1000 ) );
1673}
1674
1675//--------------------------------------------------------------------------
1676void RigidShape::updateFroth( F32 dt )
1677{
1678   // update bubbles
1679   Point3F moveDir = getVelocity();
1680
1681   Point3F contactPoint;
1682
1683   F32 speed = moveDir.len();
1684   if( speed < mDataBlock->splashVelEpsilon ) speed = 0.0;
1685
1686   U32 emitRate = (U32)(speed * mDataBlock->splashFreqMod * dt);
1687
1688   U32 i;
1689   for( i=0; i<RigidShapeData::VC_NUM_SPLASH_EMITTERS; i++ )
1690   {
1691      if( mSplashEmitterList[i] )
1692      {
1693         mSplashEmitterList[i]->emitParticles( contactPoint, contactPoint, Point3F( 0.0, 0.0, 1.0 ), 
1694            moveDir, emitRate );
1695      }
1696   }
1697
1698}
1699
1700//--------------------------------------------------------------------------
1701// Returns true if shape is intersecting a water surface (roughly)
1702//--------------------------------------------------------------------------
1703bool RigidShape::collidingWithWater( Point3F &waterHeight )
1704{
1705   Point3F curPos = getPosition();
1706
1707   F32 height = mFabs( mObjBox.maxExtents.z - mObjBox.minExtents.z );
1708
1709   RayInfo rInfo;
1710   if( gClientContainer.castRay( curPos + Point3F(0.0, 0.0, height), curPos, WaterObjectType, &rInfo) )
1711   {
1712      waterHeight = rInfo.point;
1713      return true;
1714   }
1715
1716   return false;
1717}
1718
1719void RigidShape::setEnergyLevel(F32 energy)
1720{
1721   Parent::setEnergyLevel(energy);
1722   setMaskBits(EnergyMask);
1723}
1724
1725void RigidShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex )
1726{
1727   Parent::prepBatchRender( state, mountedImageIndex );
1728
1729   if ( !gShowBoundingBox )
1730      return;
1731
1732   ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
1733   ri->renderDelegate.bind( this, &RigidShape::_renderMassAndContacts );
1734   ri->type = RenderPassManager::RIT_Editor;
1735   state->getRenderPass()->addInst( ri );
1736}
1737
1738void RigidShape::_renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
1739{
1740   // Box for the center of Mass
1741   GFXStateBlockDesc desc;
1742   desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
1743   desc.setZReadWrite(false);
1744   desc.fillMode = GFXFillWireframe;
1745
1746   GFX->getDrawUtil()->drawCube( desc, Point3F(0.1f,0.1f,0.1f), mDataBlock->massCenter, ColorI(255, 255, 255), &mRenderObjToWorld );
1747
1748   // Collision points...
1749   for (S32 i = 0; i < mCollisionList.getCount(); i++)
1750   {
1751      const Collision& collision = mCollisionList[i];
1752      GFX->getDrawUtil()->drawCube( desc, Point3F(0.05f,0.05f,0.05f), collision.point, ColorI(0, 0, 255) );
1753   }
1754
1755   // Render the normals as one big batch... 
1756   PrimBuild::begin(GFXLineList, mCollisionList.getCount() * 2);
1757   for (S32 i = 0; i < mCollisionList.getCount(); i++)
1758   {
1759
1760      const Collision& collision = mCollisionList[i];
1761      PrimBuild::color3f(1, 1, 1);
1762      PrimBuild::vertex3fv(collision.point);
1763      PrimBuild::vertex3fv(collision.point + collision.normal * 0.05f);
1764   }
1765   PrimBuild::end();
1766
1767   // Build and render the collision polylist which is returned
1768   // in the server's world space.
1769   ClippedPolyList polyList;
1770   polyList.mPlaneList.setSize(6);
1771   polyList.mPlaneList[0].set(getWorldBox().minExtents,VectorF(-1,0,0));
1772   polyList.mPlaneList[1].set(getWorldBox().minExtents,VectorF(0,-1,0));
1773   polyList.mPlaneList[2].set(getWorldBox().minExtents,VectorF(0,0,-1));
1774   polyList.mPlaneList[3].set(getWorldBox().maxExtents,VectorF(1,0,0));
1775   polyList.mPlaneList[4].set(getWorldBox().maxExtents,VectorF(0,1,0));
1776   polyList.mPlaneList[5].set(getWorldBox().maxExtents,VectorF(0,0,1));
1777   Box3F dummyBox;
1778   SphereF dummySphere;
1779   buildPolyList(PLC_Collision, &polyList, dummyBox, dummySphere);
1780   //polyList.render();
1781}
1782
1783void RigidShape::reset()
1784{
1785   mRigid.clearForces();
1786   mRigid.setAtRest();
1787}
1788
1789void RigidShape::freezeSim(bool frozen)
1790{
1791   mDisableMove = frozen;
1792   setMaskBits(FreezeMask);
1793}
1794
1795DefineEngineMethod( RigidShape, reset, void, (),,
1796   "@brief Clears physic forces from the shape and sets it at rest.\n\n"
1797   "@tsexample\n"
1798   "// Inform the RigidShape object to reset.\n"
1799   "%thisRigidShape.reset();\n"
1800   "@endtsexample\n\n"
1801   "@see ShapeBaseData")
1802{
1803   object->reset();
1804}
1805
1806DefineEngineMethod( RigidShape, freezeSim, void, (bool isFrozen),,
1807   "@brief Enables or disables the physics simulation on the RigidShape object.\n\n"
1808   "@param isFrozen Boolean frozen state to set the object.\n"
1809   "@tsexample\n"
1810   "// Define the frozen state.\n"
1811   "%isFrozen = \"true\";\n\n"
1812   "// Inform the object of the defined frozen state\n"
1813   "%thisRigidShape.freezeSim(%isFrozen);\n"
1814   "@endtsexample\n\n"
1815   "@see ShapeBaseData")
1816{
1817   object->freezeSim(isFrozen);
1818}
1819
1820DefineEngineMethod( RigidShape, forceClientTransform, void, (),,
1821   "@brief Forces the client to jump to the RigidShape's transform rather then warp to it.\n\n")
1822{
1823   if(object->isServerObject())
1824   {
1825      object->forceClientTransform();
1826   }
1827}
1828