physicsShape.cpp
Engine/source/T3D/physics/physicsShape.cpp
Public Functions
ConsoleDocClass(PhysicsShape , "@brief Represents <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> destructible physical object simulated through the plugin <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysicsShapeData.\n</a>" "@ingroup Physics" )
ConsoleDocClass(PhysicsShapeData , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysicsShape.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysicsShape.\n</a>" "@ingroup Physics" )
DefineEngineMethod(PhysicsShape , applyForce , void , (Point3F force) , "@brief Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> force <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "@param force <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@note This <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">sleeping.\n</a>" )
DefineEngineMethod(PhysicsShape , applyTorque , void , (Point3F torque) , "@brief Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> torque <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "@param torque <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@note This <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">sleeping.\n</a>" )
DefineEngineMethod(PhysicsShape , destroy , void , () , "@brief Disables rendering and physical <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">simulation.\n\n</a>" "Calling destroy() will also spawn any explosions, debris , and/or destroyedShape " "defined <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> it, as well as remove it from the scene <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">graph.\n\n</a>" "Destroyed objects are only created on the server. Ghosting will later update the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n\n</a>" " @note This does not actually delete the PhysicsShape." )
DefineEngineMethod(PhysicsShape , isDestroyed , bool , () , "@brief Returns <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classphysicsshape/">PhysicsShape</a> has been destroyed or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not.\n\n</a>" )
DefineEngineMethod(PhysicsShape , restore , void , () , "@brief Restores the shape <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> its state before being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">destroyed.\n\n</a>" "Re-enables rendering and physical simulation on the object and " "adds it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the client's scene graph. " "Has no effect <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the shape is not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">destroyed.\n\n</a>" )
ImplementEnumType(PhysicsSimType , "How <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> handle the physics simulation with the client's and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Physics\n\n</a>" )
Detailed Description
Public Functions
ConsoleDocClass(PhysicsShape , "@brief Represents <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> destructible physical object simulated through the plugin <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysicsShapeData.\n</a>" "@ingroup Physics" )
ConsoleDocClass(PhysicsShapeData , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysicsShape.\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysicsShape.\n</a>" "@ingroup Physics" )
DefineEngineMethod(PhysicsShape , applyForce , void , (Point3F force) , "@brief Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> force <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "@param force <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@note This <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">sleeping.\n</a>" )
DefineEngineMethod(PhysicsShape , applyTorque , void , (Point3F torque) , "@brief Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> torque <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "@param torque <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> apply <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the dynamic physics <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@note This <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a> is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">sleeping.\n</a>" )
DefineEngineMethod(PhysicsShape , destroy , void , () , "@brief Disables rendering and physical <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">simulation.\n\n</a>" "Calling destroy() will also spawn any explosions, debris , and/or destroyedShape " "defined <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> it, as well as remove it from the scene <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">graph.\n\n</a>" "Destroyed objects are only created on the server. Ghosting will later update the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n\n</a>" " @note This does not actually delete the PhysicsShape." )
DefineEngineMethod(PhysicsShape , isDestroyed , bool , () , "@brief Returns <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classphysicsshape/">PhysicsShape</a> has been destroyed or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not.\n\n</a>" )
DefineEngineMethod(PhysicsShape , restore , void , () , "@brief Restores the shape <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> its state before being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">destroyed.\n\n</a>" "Re-enables rendering and physical simulation on the object and " "adds it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the client's scene graph. " "Has no effect <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the shape is not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">destroyed.\n\n</a>" )
IMPLEMENT_CO_DATABLOCK_V1(PhysicsShapeData )
IMPLEMENT_CO_NETOBJECT_V1(PhysicsShape )
ImplementEnumType(PhysicsSimType , "How <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> handle the physics simulation with the client's and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Physics\n\n</a>" )
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/physics/physicsShape.h" 26 27#include "console/consoleTypes.h" 28#include "core/stream/bitStream.h" 29#include "core/resourceManager.h" 30#include "math/mathIO.h" 31#include "T3D/physics/physicsPlugin.h" 32#include "T3D/physics/physicsBody.h" 33#include "T3D/physics/physicsWorld.h" 34#include "T3D/physics/physicsCollision.h" 35#include "T3D/gameBase/gameConnection.h" 36#include "collision/concretePolyList.h" 37#include "ts/tsShapeInstance.h" 38#include "scene/sceneRenderState.h" 39#include "gfx/gfxTransformSaver.h" 40#include "T3D/physics/physicsDebris.h" 41#include "T3D/fx/explosion.h" 42#include "T3D/containerQuery.h" 43#include "lighting/lightQuery.h" 44#include "console/engineAPI.h" 45 46using namespace Torque; 47 48bool PhysicsShape::smNoCorrections = false; 49bool PhysicsShape::smNoSmoothing = false; 50 51ImplementEnumType( PhysicsSimType, 52 "How to handle the physics simulation with the client's and server.\n" 53 "@ingroup Physics\n\n") 54 { PhysicsShapeData::SimType_ClientOnly, "ClientOnly", "Only handle physics on the client.\n" }, 55 { PhysicsShapeData::SimType_ServerOnly, "ServerOnly", "Only handle physics on the server.\n" }, 56 { PhysicsShapeData::SimType_ClientServer, "ClientServer", "Handle physics on both the client and server.\n" } 57EndImplementEnumType; 58 59 60IMPLEMENT_CO_DATABLOCK_V1( PhysicsShapeData ); 61 62ConsoleDocClass( PhysicsShapeData, 63 64 "@brief Defines the properties of a PhysicsShape.\n\n" 65 "@see PhysicsShape.\n" 66 "@ingroup Physics" 67); 68 69PhysicsShapeData::PhysicsShapeData() 70 : shapeName( NULL ), 71 mass( 1.0f ), 72 dynamicFriction( 0.0f ), 73 staticFriction( 0.0f ), 74 restitution( 0.0f ), 75 linearDamping( 0.0f ), 76 angularDamping( 0.0f ), 77 linearSleepThreshold( 1.0f ), 78 angularSleepThreshold( 1.0f ), 79 waterDampingScale( 1.0f ), 80 buoyancyDensity( 0.0f ), 81 simType( SimType_ClientServer ) 82{ 83} 84 85PhysicsShapeData::~PhysicsShapeData() 86{ 87} 88 89void PhysicsShapeData::initPersistFields() 90{ 91 Parent::initPersistFields(); 92 93 addGroup("Media"); 94 95 addField( "shapeName", TypeShapeFilename, Offset( shapeName, PhysicsShapeData ), 96 "@brief Path to the .DAE or .DTS file to use for this shape.\n\n" 97 "Compatable with Live-Asset Reloading. "); 98 99 addField( "debris", TYPEID< SimObjectRef<PhysicsDebrisData> >(), Offset( debris, PhysicsShapeData ), 100 "@brief Name of a PhysicsDebrisData to spawn when this shape is destroyed (optional)." ); 101 102 addField( "explosion", TYPEID< SimObjectRef<ExplosionData> >(), Offset( explosion, PhysicsShapeData ), 103 "@brief Name of an ExplosionData to spawn when this shape is destroyed (optional)." ); 104 105 addField( "destroyedShape", TYPEID< SimObjectRef<PhysicsShapeData> >(), Offset( destroyedShape, PhysicsShapeData ), 106 "@brief Name of a PhysicsShapeData to spawn when this shape is destroyed (optional)." ); 107 108 endGroup("Media"); 109 110 addGroup( "Physics" ); 111 112 addField( "mass", TypeF32, Offset( mass, PhysicsShapeData ), 113 "@brief Value representing the mass of the shape.\n\n" 114 "A shape's mass influences the magnitude of any force exerted on it. " 115 "For example, a PhysicsShape with a large mass requires a much larger force to move than " 116 "the same shape with a smaller mass.\n" 117 "@note A mass of zero will create a kinematic shape while anything greater will create a dynamic shape."); 118 119 addField( "friction", TypeF32, Offset( dynamicFriction, PhysicsShapeData ), 120 "@brief Coefficient of kinetic %friction to be applied to the shape.\n\n" 121 "Kinetic %friction reduces the velocity of a moving object while it is in contact with a surface. " 122 "A higher coefficient will result in a larger velocity reduction. " 123 "A shape's friction should be lower than it's staticFriction, but larger than 0.\n\n" 124 "@note This value is only applied while an object is in motion. For an object starting at rest, see PhysicsShape::staticFriction"); 125 126 addField( "staticFriction", TypeF32, Offset( staticFriction, PhysicsShapeData ), 127 "@brief Coefficient of static %friction to be applied to the shape.\n\n" 128 "Static %friction determines the force needed to start moving an at-rest object in contact with a surface. " 129 "If the force applied onto shape cannot overcome the force of static %friction, the shape will remain at rest. " 130 "A larger coefficient will require a larger force to start motion. " 131 "This value should be larger than zero and the physicsShape's friction.\n\n" 132 "@note This value is only applied while an object is at rest. For an object in motion, see PhysicsShape::friction"); 133 134 addField( "restitution", TypeF32, Offset( restitution, PhysicsShapeData ), 135 "@brief Coeffecient of a bounce applied to the shape in response to a collision.\n\n" 136 "Restitution is a ratio of a shape's velocity before and after a collision. " 137 "A value of 0 will zero out a shape's post-collision velocity, making it stop on contact. " 138 "Larger values will remove less velocity after a collision, making it \'bounce\' with a greater force. " 139 "Normal %restitution values range between 0 and 1.0." 140 "@note Values near or equaling 1.0 are likely to cause undesirable results in the physics simulation." 141 " Because of this it is reccomended to avoid values close to 1.0"); 142 143 addField( "linearDamping", TypeF32, Offset( linearDamping, PhysicsShapeData ), 144 "@brief Value that reduces an object's linear velocity over time.\n\n" 145 "Larger values will cause velocity to decay quicker.\n\n" ); 146 147 addField( "angularDamping", TypeF32, Offset( angularDamping, PhysicsShapeData ), 148 "@brief Value that reduces an object's rotational velocity over time.\n\n" 149 "Larger values will cause velocity to decay quicker.\n\n" ); 150 151 addField( "linearSleepThreshold", TypeF32, Offset( linearSleepThreshold, PhysicsShapeData ), 152 "@brief Minimum linear velocity before the shape can be put to sleep.\n\n" 153 "This should be a positive value. Shapes put to sleep will not be simulated in order to save system resources.\n\n" 154 "@note The shape must be dynamic."); 155 156 addField( "angularSleepThreshold", TypeF32, Offset( angularSleepThreshold, PhysicsShapeData ), 157 "@brief Minimum rotational velocity before the shape can be put to sleep.\n\n" 158 "This should be a positive value. Shapes put to sleep will not be simulated in order to save system resources.\n\n" 159 "@note The shape must be dynamic."); 160 161 addField( "waterDampingScale", TypeF32, Offset( waterDampingScale, PhysicsShapeData ), 162 "@brief Scale to apply to linear and angular dampening while underwater.\n\n " 163 "Used with the waterViscosity of the " 164 "@see angularDamping linearDamping" ); 165 166 addField( "buoyancyDensity", TypeF32, Offset( buoyancyDensity, PhysicsShapeData ), 167 "@brief The density of the shape for calculating buoyant forces.\n\n" 168 "The result of the calculated buoyancy is relative to the density of the WaterObject the PhysicsShape is within.\n\n" 169 "@see WaterObject::density"); 170 171 endGroup( "Physics" ); 172 173 addGroup( "Networking" ); 174 175 addField( "simType", TYPEID< PhysicsShapeData::SimType >(), Offset( simType, PhysicsShapeData ), 176 "@brief Controls whether this shape is simulated on the server, client, or both physics simulations.\n\n" ); 177 178 endGroup( "Networking" ); 179} 180 181void PhysicsShapeData::packData( BitStream *stream ) 182{ 183 Parent::packData( stream ); 184 185 stream->writeString( shapeName ); 186 187 stream->write( mass ); 188 stream->write( dynamicFriction ); 189 stream->write( staticFriction ); 190 stream->write( restitution ); 191 stream->write( linearDamping ); 192 stream->write( angularDamping ); 193 stream->write( linearSleepThreshold ); 194 stream->write( angularSleepThreshold ); 195 stream->write( waterDampingScale ); 196 stream->write( buoyancyDensity ); 197 198 stream->writeInt( simType, SimType_Bits ); 199 200 stream->writeRangedU32( debris ? debris->getId() : 0, 0, DataBlockObjectIdLast ); 201 stream->writeRangedU32( explosion ? explosion->getId() : 0, 0, DataBlockObjectIdLast ); 202 stream->writeRangedU32( destroyedShape ? destroyedShape->getId() : 0, 0, DataBlockObjectIdLast ); 203} 204 205void PhysicsShapeData::unpackData( BitStream *stream ) 206{ 207 Parent::unpackData(stream); 208 209 shapeName = stream->readSTString(); 210 211 stream->read( &mass ); 212 stream->read( &dynamicFriction ); 213 stream->read( &staticFriction ); 214 stream->read( &restitution ); 215 stream->read( &linearDamping ); 216 stream->read( &angularDamping ); 217 stream->read( &linearSleepThreshold ); 218 stream->read( &angularSleepThreshold ); 219 stream->read( &waterDampingScale ); 220 stream->read( &buoyancyDensity ); 221 222 simType = (SimType)stream->readInt( SimType_Bits ); 223 224 debris = stream->readRangedU32( 0, DataBlockObjectIdLast ); 225 explosion = stream->readRangedU32( 0, DataBlockObjectIdLast ); 226 destroyedShape = stream->readRangedU32( 0, DataBlockObjectIdLast ); 227} 228 229bool PhysicsShapeData::onAdd() 230{ 231 if ( !Parent::onAdd() ) 232 return false; 233 234 ResourceManager::get().getChangedSignal().notify( this, &PhysicsShapeData::_onResourceChanged ); 235 return true; 236} 237 238void PhysicsShapeData::onRemove() 239{ 240 ResourceManager::get().getChangedSignal().remove( this, &PhysicsShapeData::_onResourceChanged ); 241 Parent::onRemove(); 242} 243 244void PhysicsShapeData::_onResourceChanged( const Torque::Path &path ) 245{ 246 if ( path != Path( shapeName ) ) 247 return; 248 249 // Reload the changed shape. 250 Resource<TSShape> reloadShape; 251 PhysicsCollisionRef reloadcolShape; 252 253 reloadShape = ResourceManager::get().load( shapeName ); 254 if ( !bool(reloadShape) ) 255 { 256 Con::warnf( ConsoleLogEntry::General, "PhysicsShapeData::_onResourceChanged: Could not reload %s.", path.getFileName().c_str() ); 257 return; 258 } 259 260 // Reload the collision shape. 261 reloadcolShape = reloadShape->buildColShape( false, Point3F::One ); 262 263 if ( bool(reloadShape) && bool(reloadcolShape)) 264 { 265 shape = reloadShape; 266 colShape = reloadcolShape; 267 } 268 269 mReloadSignal.trigger(); 270} 271 272bool PhysicsShapeData::preload( bool server, String &errorBuffer ) 273{ 274 if ( !Parent::preload( server, errorBuffer ) ) 275 return false; 276 277 // If we don't have a physics plugin active then 278 // we have to fail completely. 279 if ( !PHYSICSMGR ) 280 { 281 errorBuffer = "PhysicsShapeData::preload - No physics plugin is active!"; 282 return false; 283 } 284 285 bool shapeError = false; 286 287 if (shapeName && shapeName[0]) 288 { 289 // Resolve shapename 290 shape = ResourceManager::get().load(shapeName); 291 if (bool(shape) == false) 292 { 293 errorBuffer = String::ToString("PhysicsShapeData: Couldn't load shape \"%s\"", shapeName); 294 return false; 295 } 296 if (!server && !shape->preloadMaterialList(shape.getPath()) && NetConnection::filesWereDownloaded()) 297 shapeError = true; 298 299 } 300 301 // Prepare the shared physics collision shape. 302 if ( !colShape && shape ) 303 { 304 colShape = shape->buildColShape( false, Point3F::One ); 305 306 // If we got here and didn't get a collision shape then 307 // we need to fail... can't have a shape without collision. 308 if ( !colShape ) 309 { 310 //no collision so we create a simple box collision shape from the shapes bounds and alert the user 311 Con::warnf( "PhysicsShapeData::preload - No collision found for shape '%s', auto-creating one", shapeName ); 312 Point3F halfWidth = shape->mBounds.getExtents() * 0.5f; 313 colShape = PHYSICSMGR->createCollision(); 314 MatrixF centerXfm(true); 315 centerXfm.setPosition(shape->mBounds.getCenter()); 316 colShape->addBox(halfWidth, centerXfm); 317 return true; 318 } 319 } 320 321 // My convex demcomposion test 322 /* 323 // Get the verts and triangles for the first visible detail. 324 ConcretePolyList polyList; 325 polyList.setTransform( &MatrixF::Identity, Point3F::One ); 326 TSShapeInstance shapeInst( shape, false ); 327 shapeInst.animate(0); 328 if ( !shapeInst.buildPolyList( &polyList, 0 ) ) 329 return false; 330 331 // Gah... Ratcliff's lib works on doubles... why, oh why? 332 Vector<F64> doubleVerts; 333 doubleVerts.setSize( polyList.mVertexList.size() * 3 ); 334 for ( U32 i=0; i < polyList.mVertexList.size(); i++ ) 335 { 336 doubleVerts[ ( i * 3 ) + 0 ] = (F64)polyList.mVertexList[i].x; 337 doubleVerts[ ( i * 3 ) + 1 ] = (F64)polyList.mVertexList[i].y; 338 doubleVerts[ ( i * 3 ) + 2 ] = (F64)polyList.mVertexList[i].z; 339 } 340 341 using namespace ConvexDecomposition; 342 343 class ConvexBuilder : public ConvexDecompInterface 344 { 345 public: 346 347 ConvexBuilder() { } 348 349 ~ConvexBuilder() 350 { 351 for ( U32 i=0; i < mHulls.size(); i++ ) 352 delete mHulls[i]; 353 } 354 355 virtual void ConvexDecompResult( ConvexResult &result ) 356 { 357 FConvexResult *hull = new FConvexResult( result ); 358 mHulls.push_back( hull ); 359 } 360 361 Vector<FConvexResult*> mHulls; 362 }; 363 364 DecompDesc d; 365 d.mVcount = polyList.mVertexList.size(); 366 d.mVertices = doubleVerts.address(); 367 d.mTcount = polyList.mIndexList.size() / 3; 368 d.mIndices = polyList.mIndexList.address(); 369 d.mDepth = 3; 370 d.mCpercent = 20.0f; 371 d.mPpercent = 30.0f; 372 d.mMaxVertices = 32; 373 d.mSkinWidth = 0.05f; // Need to expose this! 374 375 ConvexBuilder builder; 376 d.mCallback = &builder; 377 378 if ( performConvexDecomposition( d ) < 1 || builder.mHulls.empty() ) 379 return false; 380 381 // Add all the convex hull results into the collision shape. 382 colShape = PHYSICSMGR->createCollision(); 383 for ( U32 i=0; i < builder.mHulls.size(); i++ ) 384 colShape->addConvex( (const Point3F*)builder.mHulls[i]->mHullVertices, 385 builder.mHulls[i]->mHullVcount, 386 MatrixF::Identity ); 387 */ 388 389 return !shapeError; 390} 391 392 393IMPLEMENT_CO_NETOBJECT_V1(PhysicsShape); 394 395ConsoleDocClass( PhysicsShape, 396 397 "@brief Represents a destructible physical object simulated through the plugin system.\n\n" 398 "@see PhysicsShapeData.\n" 399 "@ingroup Physics" 400); 401 402PhysicsShape::PhysicsShape() 403 : mPhysicsRep( NULL ), 404 mWorld( NULL ), 405 mResetPos( MatrixF::Identity ), 406 mShapeInst( NULL ), 407 mDestroyed( false ), 408 mPlayAmbient( false ), 409 mAmbientSeq( -1 ), 410 mAmbientThread( NULL ) 411{ 412 mNetFlags.set( Ghostable | ScopeAlways ); 413 mTypeMask |= DynamicShapeObjectType; 414} 415 416PhysicsShape::~PhysicsShape() 417{ 418} 419 420void PhysicsShape::consoleInit() 421{ 422 Con::addVariable( "$PhysicsShape::noCorrections", TypeBool, &PhysicsShape::smNoCorrections, 423 "@brief Determines if the shape will recieve corrections from the server or " 424 "will instead be allowed to diverge.\n\n" 425 "In the event that the client and server object positions/orientations " 426 "differ and if this variable is true, the server will attempt to \'correct\' " 427 "the client object to keep it in sync. Otherwise, client and server objects may fall out of sync.\n\n"); 428 429 Con::addVariable( "$PhysicsShape::noSmoothing", TypeBool, &PhysicsShape::smNoSmoothing, 430 "@brief Determines if client-side shapes will attempt to smoothly transition to " 431 "their new position after reciving a correction.\n\n" 432 "If true, shapes will immediately render at the position they are corrected to.\n\n"); 433 434 Parent::consoleInit(); 435} 436 437void PhysicsShape::initPersistFields() 438{ 439 addGroup( "PhysicsShape" ); 440 441 addField( "playAmbient", TypeBool, Offset( mPlayAmbient, PhysicsShape ), 442 "@brief Enables or disables playing of an ambient animation upon loading the shape.\n\n" 443 "@note The ambient animation must be named \"ambient\"." ); 444 445 endGroup( "PhysicsShape" ); 446 447 Parent::initPersistFields(); 448 449 removeField( "scale" ); 450} 451 452void PhysicsShape::inspectPostApply() 453{ 454 Parent::inspectPostApply(); 455 456 setMaskBits( InitialUpdateMask ); 457} 458 459U32 PhysicsShape::packUpdate( NetConnection *con, U32 mask, BitStream *stream ) 460{ 461 U32 retMask = Parent::packUpdate( con, mask, stream ); 462 463 if ( stream->writeFlag( mask & InitialUpdateMask ) ) 464 { 465 stream->writeAffineTransform( getTransform() ); 466 stream->writeFlag( mPlayAmbient ); 467 468 stream->writeFlag( mDestroyed ); 469 470 return retMask; 471 } 472 473 // If we got here its not an initial update. So only send 474 // the least amount of data possible. 475 476 if ( stream->writeFlag( mask & StateMask ) ) 477 { 478 // This will encode the position relative to the control 479 // object position. 480 // 481 // This will compress the position to as little as 6.25 482 // bytes if the position is within about 30 meters of the 483 // control object. 484 // 485 // Worst case its a full 12 bytes + 2 bits if the position 486 // is more than 500 meters from the control object. 487 // 488 stream->writeCompressedPoint( mState.position ); 489 490 // Use only 3.5 bytes to send the orientation. 491 stream->writeQuat( mState.orientation, 9 ); 492 493 // If the server object has been set to sleep then 494 // we don't need to send any velocity. 495 if ( !stream->writeFlag( mState.sleeping ) ) 496 { 497 // This gives me ~0.015f resolution in velocity magnitude 498 // while only costing me 1 bit of the velocity is zero length, 499 // <5 bytes in normal cases, and <8 bytes if the velocity is 500 // greater than 1000. 501 AssertWarn( mState.linVelocity.len() < 1000.0f, 502 "PhysicsShape::packUpdate - The linVelocity is out of range!" ); 503 stream->writeVector( mState.linVelocity, 1000.0f, 16, 9 ); 504 505 // For angular velocity we get < 0.01f resolution in magnitude 506 // with the most common case being under 4 bytes. 507 AssertWarn( mState.angVelocity.len() < 10.0f, 508 "PhysicsShape::packUpdate - The angVelocity is out of range!" ); 509 stream->writeVector( mState.angVelocity, 10.0f, 10, 9 ); 510 } 511 } 512 513 if ( stream->writeFlag( mask & DamageMask ) ) 514 stream->writeFlag( mDestroyed ); 515 516 return retMask; 517} 518 519void PhysicsShape::unpackUpdate( NetConnection *con, BitStream *stream ) 520{ 521 Parent::unpackUpdate( con, stream ); 522 523 if ( stream->readFlag() ) // InitialUpdateMask 524 { 525 MatrixF mat; 526 stream->readAffineTransform( &mat ); 527 setTransform( mat ); 528 mPlayAmbient = stream->readFlag(); 529 530 if ( isProperlyAdded() ) 531 _initAmbient(); 532 533 if ( stream->readFlag() ) 534 { 535 if ( isProperlyAdded() ) 536 { 537 // Destroy immediately if we've already been added 538 // to the scene. 539 destroy(); 540 } 541 else 542 { 543 // Indicate the shape should be destroyed when the 544 // shape is added. 545 mDestroyed = true; 546 } 547 } 548 549 return; 550 } 551 552 if ( stream->readFlag() ) // StateMask 553 { 554 PhysicsState state; 555 556 // Read the encoded and compressed position... commonly only 6.25 bytes. 557 stream->readCompressedPoint( &state.position ); 558 559 // Read the compressed quaternion... 3.5 bytes. 560 stream->readQuat( &state.orientation, 9 ); 561 562 state.sleeping = stream->readFlag(); 563 if ( !state.sleeping ) 564 { 565 stream->readVector( &state.linVelocity, 1000.0f, 16, 9 ); 566 stream->readVector( &state.angVelocity, 10.0f, 10, 9 ); 567 } 568 569 if ( !smNoCorrections && mPhysicsRep && mPhysicsRep->isDynamic() && !mDestroyed ) 570 { 571 // Set the new state on the physics object immediately. 572 mPhysicsRep->applyCorrection( state.getTransform() ); 573 574 mPhysicsRep->setSleeping( state.sleeping ); 575 if ( !state.sleeping ) 576 { 577 mPhysicsRep->setLinVelocity( state.linVelocity ); 578 mPhysicsRep->setAngVelocity( state.angVelocity ); 579 } 580 581 mPhysicsRep->getState( &mState ); 582 } 583 584 // If there is no physics object then just set the 585 // new state... the tick will take care of the 586 // interpolation and extrapolation. 587 if ( !mPhysicsRep || !mPhysicsRep->isDynamic() ) 588 mState = state; 589 } 590 591 if ( stream->readFlag() ) // DamageMask 592 { 593 if ( stream->readFlag() ) 594 destroy(); 595 else 596 restore(); 597 } 598} 599 600bool PhysicsShape::onAdd() 601{ 602 if ( !Parent::onAdd() ) 603 return false; 604 605 // If we don't have a physics plugin active then 606 // we have to fail completely. 607 if ( !PHYSICSMGR ) 608 { 609 Con::errorf( "PhysicsShape::onAdd - No physics plugin is active!" ); 610 return false; 611 } 612 613 // 614 if ( !mPhysicsRep && !_createShape() ) 615 { 616 Con::errorf( "PhysicsShape::onAdd() - Shape creation failed!" ); 617 return false; 618 } 619 620 // The reset position is the transform on the server 621 // at creation time... its not used on the client. 622 if ( isServerObject() ) 623 { 624 storeRestorePos(); 625 PhysicsPlugin::getPhysicsResetSignal().notify( this, &PhysicsShape::_onPhysicsReset ); 626 } 627 628 // Register for the resource change signal. 629 //ResourceManager::get().getChangedSignal().notify( this, &PhysicsShape::_onResourceChanged ); 630 631 // Only add server objects and non-destroyed client objects to the scene. 632 if ( isServerObject() || !mDestroyed) 633 addToScene(); 634 635 if ( isClientObject() && mDestroyed ) 636 { 637 // Disable all simulation of the body... no collision or dynamics. 638 if ( mPhysicsRep ) 639 mPhysicsRep->setSimulationEnabled( false ); 640 641 // Stop doing tick processing for this SceneObject. 642 setProcessTick( false ); 643 } 644 645 return true; 646} 647 648void PhysicsShape::onRemove() 649{ 650 removeFromScene(); 651 652 SAFE_DELETE( mPhysicsRep ); 653 SAFE_DELETE( mShapeInst ); 654 mAmbientThread = NULL; 655 mAmbientSeq = -1; 656 mWorld = NULL; 657 658 if ( isServerObject() ) 659 { 660 PhysicsPlugin::getPhysicsResetSignal().remove( this, &PhysicsShape::_onPhysicsReset ); 661 662 if ( mDestroyedShape ) 663 mDestroyedShape->deleteObject(); 664 } 665 666 // Remove the resource change signal. 667 //ResourceManager::get().getChangedSignal().remove( this, &PhysicsShape::_onResourceChanged ); 668 669 Parent::onRemove(); 670} 671 672bool PhysicsShape::onNewDataBlock( GameBaseData *dptr, bool reload ) 673{ 674 if ( !Parent::onNewDataBlock( dptr, reload ) ) 675 return false; 676 677 if ( !isProperlyAdded() ) 678 return true; 679 680 // If we don't have a physics plugin active then 681 // we have to fail completely. 682 if ( !PHYSICSMGR ) 683 { 684 Con::errorf( "PhysicsShape::onNewDataBlock - No physics plugin is active!" ); 685 return false; 686 } 687 688 // 689 if ( !_createShape() ) 690 { 691 Con::errorf( "PhysicsShape::onNewDataBlock() - Shape creation failed!" ); 692 return false; 693 } 694 695 return true; 696} 697 698bool PhysicsShape::_createShape() 699{ 700 SAFE_DELETE( mPhysicsRep ); 701 SAFE_DELETE( mShapeInst ); 702 mAmbientThread = NULL; 703 mWorld = NULL; 704 mAmbientSeq = -1; 705 706 PhysicsShapeData *db = getDataBlock(); 707 if ( !db || !db->shape) 708 return false; 709 710 // Set the world box. 711 mObjBox = db->shape->mBounds; 712 resetWorldBox(); 713 714 // If this is the server and its a client only simulation 715 // object then disable our tick... the server doesn't do 716 // any work for this shape. 717 if ( isServerObject() && 718 db->simType == PhysicsShapeData::SimType_ClientOnly ) 719 { 720 setProcessTick( false ); 721 return true; 722 } 723 724 // Create the shape instance. 725 mShapeInst = new TSShapeInstance( db->shape, isClientObject() ); 726 727 if ( isClientObject() ) 728 { 729 mAmbientSeq = db->shape->findSequence( "ambient" ); 730 _initAmbient(); 731 } 732 733 // If the shape has a mass then its dynamic... else 734 // its a kinematic shape. 735 // 736 // While a kinematic is less optimal than a static body 737 // it allows for us to enable/disable collision and having 738 // all dynamic actors react correctly... waking up. 739 // 740 const bool isDynamic = db->mass > 0.0f; 741 742 // If we aren't dynamic we don't need to tick. 743 setProcessTick( isDynamic || mPlayAmbient ); 744 745 // If this is the client and we're a server only object then 746 // we don't need any physics representation... we're done. 747 if ( isClientObject() && 748 db->simType == PhysicsShapeData::SimType_ServerOnly ) 749 return true; 750 751 mWorld = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); 752 753 mPhysicsRep = PHYSICSMGR->createBody(); 754 mPhysicsRep->init( db->colShape, 755 db->mass, 756 isDynamic ? 0 : PhysicsBody::BF_KINEMATIC, 757 this, 758 mWorld ); 759 760 mPhysicsRep->setMaterial( db->restitution, db->dynamicFriction, db->staticFriction ); 761 762 if ( isDynamic ) 763 { 764 mPhysicsRep->setDamping( db->linearDamping, db->angularDamping ); 765 mPhysicsRep->setSleepThreshold( db->linearSleepThreshold, db->angularSleepThreshold ); 766 } 767 768 mPhysicsRep->setTransform( getTransform() ); 769 770 return true; 771} 772 773void PhysicsShape::_initAmbient() 774{ 775 if ( isServerObject() ) 776 return; 777 778 bool willPlay = mPlayAmbient && mAmbientSeq != -1; 779 780 if ( willPlay ) 781 { 782 // Create thread if we dont already have. 783 if ( mAmbientThread == NULL ) 784 mAmbientThread = mShapeInst->addThread(); 785 786 // Play the sequence. 787 mShapeInst->setSequence( mAmbientThread, mAmbientSeq, 0); 788 789 setProcessTick(true); 790 } 791 else 792 { 793 if ( mAmbientThread != NULL ) 794 { 795 mShapeInst->destroyThread( mAmbientThread ); 796 mAmbientThread = NULL; 797 } 798 } 799} 800 801void PhysicsShape::_onPhysicsReset( PhysicsResetEvent reset ) 802{ 803 if ( reset == PhysicsResetEvent_Store ) 804 mResetPos = getTransform(); 805 806 else if ( reset == PhysicsResetEvent_Restore ) 807 { 808 setTransform( mResetPos ); 809 810 // Restore to un-destroyed state. 811 restore(); 812 813 // Cheat and reset the client from here. 814 if ( getClientObject() ) 815 { 816 PhysicsShape *clientObj = (PhysicsShape*)getClientObject(); 817 clientObj->setTransform( mResetPos ); 818 clientObj->restore(); 819 } 820 } 821} 822 823void PhysicsShape::setTransform( const MatrixF &newMat ) 824{ 825 Parent::setTransform( newMat ); 826 827 // This is only called to set an absolute position 828 // so we discard the delta state. 829 mState.position = getPosition(); 830 mState.orientation.set( newMat ); 831 mRenderState[0] = mRenderState[1] = mState; 832 setMaskBits( StateMask ); 833 834 if ( mPhysicsRep ) 835 mPhysicsRep->setTransform( newMat ); 836} 837 838void PhysicsShape::setScale( const VectorF &scale ) 839{ 840 // Cannot scale PhysicsShape. 841 return; 842} 843 844void PhysicsShape::storeRestorePos() 845{ 846 mResetPos = getTransform(); 847} 848 849F32 PhysicsShape::getMass() const 850{ 851 const PhysicsShapeData *db = const_cast<PhysicsShape*>( this )->getDataBlock(); 852 return db->mass; 853} 854 855void PhysicsShape::applyImpulse( const Point3F &pos, const VectorF &vec ) 856{ 857 if ( mPhysicsRep && mPhysicsRep->isDynamic() ) 858 mPhysicsRep->applyImpulse( pos, vec ); 859} 860 861void PhysicsShape::applyTorque( const Point3F &torque ) 862{ 863 if (mPhysicsRep && mPhysicsRep->isDynamic()) 864 mPhysicsRep->applyTorque( torque ); 865} 866 867void PhysicsShape::applyForce( const Point3F &force ) 868{ 869 if (mPhysicsRep && mPhysicsRep->isDynamic()) 870 mPhysicsRep->applyForce( force ); 871} 872 873void PhysicsShape::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) 874{ 875 if ( !mPhysicsRep || !mPhysicsRep->isDynamic() ) 876 return; 877 878 // TODO: Find a better approximation of the 879 // force vector using the object box. 880 881 VectorF force = getWorldBox().getCenter() - origin; 882 F32 dist = force.magnitudeSafe(); 883 force.normalize(); 884 885 if ( dist == 0.0f ) 886 force *= magnitude; 887 else 888 force *= mClampF( radius / dist, 0.0f, 1.0f ) * magnitude; 889 890 mPhysicsRep->applyImpulse( origin, force ); 891 892 // TODO: There is no simple way to really sync this sort of an 893 // event with the client. 894 // 895 // The best is to send the current physics snapshot, calculate the 896 // time difference from when this event occured and the time when the 897 // client recieves it, and then extrapolate where it should be. 898 // 899 // Even then its impossible to be absolutely sure its synced. 900 // 901 // Bottom line... you shouldn't use physics over the network like this. 902 // 903 904 // Cheat for single player. 905 //if ( getClientObject() ) 906 //((PhysicsShape*)getClientObject())->mPhysicsRep->applyImpulse( origin, force ); 907} 908 909void PhysicsShape::interpolateTick( F32 delta ) 910{ 911 AssertFatal( !mDestroyed, "PhysicsShape::interpolateTick - Shouldn't be processing a destroyed shape!" ); 912 913 if ( !mPhysicsRep->isDynamic() ) 914 return; 915 916 // Interpolate the position and rotation based on the delta. 917 PhysicsState state; 918 state.interpolate( mRenderState[1], mRenderState[0], delta ); 919 920 // Set the transform to the interpolated transform. 921 setRenderTransform( state.getTransform() ); 922} 923 924void PhysicsShape::processTick( const Move *move ) 925{ 926 AssertFatal( mPhysicsRep && !mDestroyed, "PhysicsShape::processTick - Shouldn't be processing a destroyed shape!" ); 927 928 // Note that unlike TSStatic, the serverside PhysicsShape does not 929 // need to play the ambient animation because even if the animation were 930 // to move collision shapes it would not affect the physx representation. 931 932 if ( !mPhysicsRep->isDynamic() ) 933 return; 934 935 // SINGLE PLAYER HACK!!!! 936 if ( PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject() ) 937 { 938 PhysicsShape *servObj = (PhysicsShape*)getServerObject(); 939 setTransform( servObj->mState.getTransform() ); 940 mRenderState[0] = servObj->mRenderState[0]; 941 mRenderState[1] = servObj->mRenderState[1]; 942 943 return; 944 } 945 946 // Store the last render state. 947 mRenderState[0] = mRenderState[1]; 948 949 // If the last render state doesn't match the last simulation 950 // state then we got a correction and need to 951 Point3F errorDelta = mRenderState[1].position - mState.position; 952 const bool doSmoothing = !errorDelta.isZero() && !smNoSmoothing; 953 954 const bool wasSleeping = mState.sleeping; 955 956 // Get the new physics state. 957 if ( mPhysicsRep ) 958 { 959 mPhysicsRep->getState( &mState ); 960 _updateContainerForces(); 961 } 962 else 963 { 964 // This is where we could extrapolate. 965 } 966 967 // Smooth the correction back into the render state. 968 mRenderState[1] = mState; 969 if ( doSmoothing ) 970 { 971 F32 correction = mClampF( errorDelta.len() / 20.0f, 0.1f, 0.9f ); 972 mRenderState[1].position.interpolate( mState.position, mRenderState[0].position, correction ); 973 mRenderState[1].orientation.interpolate( mState.orientation, mRenderState[0].orientation, correction ); 974 } 975 976 // If we haven't been sleeping then update our transform 977 // and set ourselves as dirty for the next client update. 978 if ( !wasSleeping || !mState.sleeping ) 979 { 980 // Set the transform on the parent so that 981 // the physics object isn't moved. 982 Parent::setTransform( mState.getTransform() ); 983 984 // If we're doing server simulation then we need 985 // to send the client a state update. 986 if ( isServerObject() && mPhysicsRep && !smNoCorrections && 987 988 !PHYSICSMGR->isSinglePlayer() // SINGLE PLAYER HACK!!!! 989 990 ) 991 setMaskBits( StateMask ); 992 } 993} 994 995void PhysicsShape::advanceTime( F32 timeDelta ) 996{ 997 if ( isClientObject() && mPlayAmbient && mAmbientThread != NULL ) 998 mShapeInst->advanceTime( timeDelta, mAmbientThread ); 999} 1000 1001void PhysicsShape::_updateContainerForces() 1002{ 1003 PROFILE_SCOPE( PhysicsShape_updateContainerForces ); 1004 1005 // If we're not simulating don't update forces. 1006 if ( !mWorld->isEnabled() ) 1007 return; 1008 1009 ContainerQueryInfo info; 1010 info.box = getWorldBox(); 1011 info.mass = getDataBlock()->mass; 1012 1013 // Find and retreive physics info from intersecting WaterObject(s) 1014 getContainer()->findObjects( getWorldBox(), WaterObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a1af121084d5760a7eaef4bb8828ce1cf">PhysicalZoneObjectType, findRouter, &info ); 1015 1016 // Calculate buoyancy and drag 1017 F32 angDrag = getDataBlock()->angularDamping; 1018 F32 linDrag = getDataBlock()->linearDamping; 1019 F32 buoyancy = 0.0f; 1020 Point3F cmass = mPhysicsRep->getCMassPosition(); 1021 1022 F32 density = getDataBlock()->buoyancyDensity; 1023 if ( density > 0.0f ) 1024 { 1025 if ( info.waterCoverage > 0.0f ) 1026 { 1027 F32 waterDragScale = info.waterViscosity * getDataBlock()->waterDampingScale; 1028 F32 powCoverage = mPow( info.waterCoverage, 0.25f ); 1029 1030 angDrag = mLerp( angDrag, angDrag * waterDragScale, powCoverage ); 1031 linDrag = mLerp( linDrag, linDrag * waterDragScale, powCoverage ); 1032 } 1033 1034 buoyancy = ( info.waterDensity / density ) * mPow( info.waterCoverage, 2.0f ); 1035 1036 // A little hackery to prevent oscillation 1037 // Based on this blog post: 1038 // (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html) 1039 // JCF: disabled! 1040 Point3F buoyancyForce = buoyancy * -mWorld->getGravity() * TickSec * getDataBlock()->mass; 1041 mPhysicsRep->applyImpulse( cmass, buoyancyForce ); 1042 } 1043 1044 // Update the dampening as the container might have changed. 1045 mPhysicsRep->setDamping( linDrag, angDrag ); 1046 1047 // Apply physical zone forces. 1048 if ( !info.appliedForce.isZero() ) 1049 mPhysicsRep->applyImpulse( cmass, info.appliedForce ); 1050} 1051 1052void PhysicsShape::prepRenderImage( SceneRenderState *state ) 1053{ 1054 AssertFatal( !mDestroyed, "PhysicsShape::prepRenderImage - Shouldn't be processing a destroyed shape!" ); 1055 1056 PROFILE_SCOPE( PhysicsShape_prepRenderImage ); 1057 1058 if( !mShapeInst ) 1059 return; 1060 1061 Point3F cameraOffset; 1062 getRenderTransform().getColumn(3,&cameraOffset); 1063 cameraOffset -= state->getDiffuseCameraPosition(); 1064 F32 dist = cameraOffset.len(); 1065 if (dist < 0.01f) 1066 dist = 0.01f; 1067 1068 F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); 1069 if ( mShapeInst->setDetailFromDistance( state, dist * invScale ) < 0 ) 1070 return; 1071 1072 GFXTransformSaver saver; 1073 1074 // Set up our TS render state. 1075 TSRenderState rdata; 1076 rdata.setSceneState( state ); 1077 rdata.setFadeOverride( 1.0f ); 1078 1079 // We might have some forward lit materials 1080 // so pass down a query to gather lights. 1081 LightQuery query; 1082 query.init( getWorldSphere() ); 1083 rdata.setLightQuery( &query ); 1084 1085 MatrixF mat = getRenderTransform(); 1086 mat.scale( mObjScale ); 1087 GFX->setWorldMatrix( mat ); 1088 1089 mShapeInst->animate(); 1090 mShapeInst->render( rdata ); 1091} 1092 1093void PhysicsShape::destroy() 1094{ 1095 if ( mDestroyed ) 1096 return; 1097 1098 mDestroyed = true; 1099 setMaskBits( DamageMask ); 1100 1101 const Point3F lastLinVel = mPhysicsRep->isDynamic() ? mPhysicsRep->getLinVelocity() : Point3F::Zero; 1102 1103 // Disable all simulation of the body... no collision or dynamics. 1104 mPhysicsRep->setSimulationEnabled( false ); 1105 1106 // On the client side we remove it from the scene graph 1107 // to disable rendering and volume queries. 1108 if ( isClientObject() ) 1109 removeFromScene(); 1110 1111 // Stop doing tick processing for this SceneObject. 1112 setProcessTick( false ); 1113 1114 PhysicsShapeData *db = getDataBlock(); 1115 if ( !db ) 1116 return; 1117 1118 const MatrixF &mat = getTransform(); 1119 if ( isServerObject() ) 1120 { 1121 // We only create the destroyed object on the server 1122 // and let ghosting deal with updating the client. 1123 1124 if ( db->destroyedShape ) 1125 { 1126 mDestroyedShape = new PhysicsShape(); 1127 mDestroyedShape->setDataBlock( db->destroyedShape ); 1128 mDestroyedShape->setTransform( mat ); 1129 if ( !mDestroyedShape->registerObject() ) 1130 delete mDestroyedShape.getObject(); 1131 } 1132 1133 return; 1134 } 1135 1136 // Let the physics debris create itself. 1137 PhysicsDebris::create( db->debris, mat, lastLinVel ); 1138 1139 if ( db->explosion ) 1140 { 1141 Explosion *splod = new Explosion(); 1142 splod->setDataBlock( db->explosion ); 1143 splod->setTransform( mat ); 1144 splod->setInitialState( getPosition(), mat.getUpVector(), 1.0f ); 1145 if ( !splod->registerObject() ) 1146 delete splod; 1147 } 1148} 1149 1150void PhysicsShape::restore() 1151{ 1152 if ( !mDestroyed ) 1153 return; 1154 1155 PhysicsShapeData *db = getDataBlock(); 1156 const bool isDynamic = db && db->mass > 0.0f; 1157 1158 if ( mDestroyedShape ) 1159 mDestroyedShape->deleteObject(); 1160 1161 // Restore tick processing, add it back to 1162 // the scene, and enable collision and simulation. 1163 setProcessTick( isDynamic || mPlayAmbient ); 1164 if ( isClientObject() ) 1165 addToScene(); 1166 mPhysicsRep->setSimulationEnabled( true ); 1167 1168 mDestroyed = false; 1169 setMaskBits( DamageMask ); 1170} 1171 1172DefineEngineMethod( PhysicsShape, isDestroyed, bool, (),, 1173 "@brief Returns if a PhysicsShape has been destroyed or not.\n\n" ) 1174{ 1175 return object->isDestroyed(); 1176} 1177 1178DefineEngineMethod( PhysicsShape, destroy, void, (),, 1179 "@brief Disables rendering and physical simulation.\n\n" 1180 "Calling destroy() will also spawn any explosions, debris, and/or destroyedShape " 1181 "defined for it, as well as remove it from the scene graph.\n\n" 1182 "Destroyed objects are only created on the server. Ghosting will later update the client.\n\n" 1183 "@note This does not actually delete the PhysicsShape." ) 1184{ 1185 object->destroy(); 1186} 1187 1188DefineEngineMethod( PhysicsShape, restore, void, (),, 1189 "@brief Restores the shape to its state before being destroyed.\n\n" 1190 "Re-enables rendering and physical simulation on the object and " 1191 "adds it to the client's scene graph. " 1192 "Has no effect if the shape is not destroyed.\n\n") 1193{ 1194 object->restore(); 1195} 1196 1197DefineEngineMethod( PhysicsShape, applyTorque, void, (Point3F torque), , 1198 "@brief Add a torque to a dynamic physics shape.\n\n" 1199 "@param torque to apply to the dynamic physics shape\n" 1200 "@note This value is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape if it is sleeping.\n") 1201{ 1202 object->applyTorque( torque ); 1203} 1204 1205DefineEngineMethod(PhysicsShape, applyForce, void, (Point3F force), , 1206 "@brief Add a force to a dynamic physics shape.\n\n" 1207 "@param force to apply to the dynamic physics shape\n" 1208 "@note This value is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape if it is sleeping.\n") 1209{ 1210 object->applyForce( force ); 1211} 1212