rigidShape.cpp
Engine/source/T3D/rigidShape.cpp
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