= Physics: Gravity, Collisions, Forces :author: :revnumber: :revdate: 2016/03/17 20:48 :keywords: physics, documentation, control :relfileprefix: ../../ :imagesdir: ../.. ifdef::env-github,env-browser[:outfilesuffix: .adoc] A physics simulation is used in games and applications where objects are exposed to physical forces: Think of games like pool billiard and car racing simulators. Massive objects are pulled by gravity, forces cause objects to gain momentum, friction slows them down, solid objects collide and bounce off one another, etc. Action and Adventure games also make use of physics to implement solid obstacles, falling, and jumping. The jMonkeyEngine3 has built-in support for link:http://jbullet.advel.cz[jBullet Physics] (based on link:http://bulletphysics.org[Bullet Physics]) via the `com.jme3.bullet` package. This article focuses mostly on the RigidBodyControl, but also introduces you to others. If you are looking for info on how to respond to physics events such as collisions, read about <>. == Technical Overview jME3 has a complete, slightly adapted but fully wrapped Bullet +++API+++ that uses normal jME math objects (Vector3f, Quaternion etc) as input/output data. All normal bullet objects like RigidBodies, Constraints (called “Joints in jME3) and the various collision shapes are available, all mesh formats can be converted from jME to bullet. The PhysicsSpace object is the central object in bullet and all objects have to be added to it so they are physics-enabled. You can create multiple physics spaces as well to have multiple independent physics simulations or to run simulations in the background that you step at a different pace. You can also create a Bullet PhysicsSpace in jME3 with a `com.jme3.bullet.BulletAppState` which runs a PhysicsSpace along the update loop, which is the easiest way to instantiate a physics space. It can be run in a mode where it runs in parallel to rendering, yet syncs to the update loop so you can apply physics changes safely during the update() calls of Controls and SimpleApplication. The base bullet objects are also available as simple to use controls that can be attached to spatials to directly control these by physics forces and influences. The RigidBodyControl for example includes a simple constructor that automatically creates a hull collision shape or a mesh collision shape based on the given input mass and the mesh of the spatial it is attached to. This makes enabling physics on a Geometry as simple as “spatial.addControl(new RigidBodyControl(1)); Due to some differences in how bullet and jME handle the scene and other objects relations there is some things to remember about the controls implementation: * The collision shape is not automatically updated when the spatial mesh changes ** You can update it by reattaching the control or by using the CollisionShapeFactory yourself. * In bullet the scale parameter is on the collision shape (which equals the mesh in jME3) and not on the RigidBody so you cannot scale a collision shape without scaling any other RigidBody with reference of it ** Note that you should share collision shapes in general and that j3o files loaded from file do that as well when instantiated twice so this is something to consider. * *Physics objects remain in the physics space when their spatials are detached from the scene graph!* ** Use PhysicsSpace.remove(physicsObject) or simply physicsControl.setEnabled(false); to remove them from the PhysicsSpace * If you apply forces to the physics object in an update() call they might not get applied because internally bullet still runs at 60fps while your app might run at 120. ** You can use the PhysicsTickListener interface and register with the physics space and use the preTick() method to be sure that you actually apply the force in the right moment. ** Reading values from the physics objects in the update loop should always yield correct values but they might not change over several fames due to the same reason. * Reading or writing from the physics objects during the render phase is not recommended as this is when the physics space is stepped and would cause data corruption. This is why the debug display does not work properly in a threaded BulletAppState * Bullet always uses world coordinates, there is no such concept as nodes so the object will be moved into a world location with no regard to its parent spatial. ** You can configure this behavior using the setApplyPhysicsLocal() method on physics controls but remember the physics space still runs in world coordinates so you can visually detach things that will actually still collide in the physics space. ** To use the local applying to simulate e.g. the internal physics system of a train passing by, simply create another BulletAppState and add all models with physics controls in local mode to a node. When you move the node the physics will happen all the same but the objects will move along with the node. When you use this physics simulation, values correspond to the following units: * 1 length unit (1.0f) equals 1 meter, * 1 weight unit (1.0f) equals 1 kilogram, * most torque and rotation values are expressed in radians. Bullet physics runs internally at 60fps by default. This rate is not dependent on the actual framerate and it does not lock the framerate at 60fps. Instead, when the actual fps is higher than the physics framerate the system will display interpolated positions for the physics objects. When the framerate is lower than the physics framerate, the physics space will be stepped multiple times per frame to make up for the missing calculations. Internally, the updating and syncing of the actual physics objects in the BulletAppState happens in the following way: . collision callbacks (`BulletAppState.update()`) . user update (`simpleUpdate` in main loop, `update()` in Controls and AppStates) . physics to scenegraph syncing and applying (`updateLogicalState()`) . stepping physics (before or in parallel to `Application.render()`) == Sample Code Full code samples are here: * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestBrickWall.java[TestBrickWall.java] * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestQ3.java[TestQ3.java] * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestSimplePhysics.java[TestSimplePhysics.java] * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java[TestWalkingChar.java] == Physics Application A short overview of how to write a jME application with Physics capabilities: Do the following once per application to gain access to the `physicsSpace` object: . Make your application extend `com.jme3.app.SimpleApplication`. . Create a BulletAppState field: + [source,java] ---- private BulletAppState bulletAppState; ---- . Initialize your bulletAppState and attach it to the state manager: + [source,java] ---- public void simpleInitApp() { bulletAppState = new BulletAppState(); stateManager.attach(bulletAppState); ---- [TIP] ==== In your application, you can always access the `BulletAppState` via the ApplicationStateManager: [source,java] ---- BulletAppState bas = app.getStateManager().getState(BulletAppState.class); ---- ==== For each Spatial that you want to be physical: . Create a CollisionShape. . Create the PhysicsControl from the CollisionShape and a mass value. . Add the PhysicsControl to its Spatial. . Add the PhysicsControl to the PhysicsSpace. . Attach the Spatial to the rootNode (as usual). . (Optional) Implement the `PhysicsCollisionListener` interface to respond to `PhysicsCollisionEvent`pass:[s]. Let's look at the details: == Create a CollisionShape A CollisionShape is a simplified shape for which physics are easier to calculate than for the true shape of the model. This simplication approach speeds up the simulation greatly. Before you can create a Physics Control, you must create a CollisionShape from the `com.jme3.bullet.collision.shapes` package. (Read the tip under "`Physics Controls Code Samples`" on how to use default CollisionShapes for Boxes and Spheres.) [cols="25,40,35", options="header"] |=== >: A cylinder-shaped body does not get stuck at corners and vertical obstacles; the rounded top and bottom do not get stuck on stair steps and ground obstacles. a| Persons, animals. > by adding it to a visible Geometry. a|A monster's “aggro radius, CharacterControl collisions, motion detectors, photo-electric alarm sensors, poisonous or radioactive perimeters, life-draining ghosts, etc. |=== [cols="20,40,40", options="header"] |=== a|Special PhysicsControls a| Usage a| Examples a|VehicleControl + PhysicsVehicleWheel a| Special Control used for <>. a|Cars, tanks, hover crafts, ships, motorcycles… a|CharacterControl a|Special Control used for <>s. a|Upright walking persons, animals, robots… a|BetterCharacterControl a|Special Control used for <>s. a|Upright walking persons, animals, robots. Replaces CharacterControl. a|RagDollControl a|Special Control used for <> a|Falling persons, animals, robots, “Rag dolls |=== Click the links for details on the special PhysicsControls. This article is about RigidBodyControl. === Physics Control Code Samples The most commonly used physics control is RigidBodyControl. The RigidBodyControl constructor takes up to two parameters: a collision shape and a mass (a float in kilograms). The mass parameter also determines whether the object is dynamic (movable) or static (fixed). For a static object such as a floor or wall, specify zero mass. [source,java] ---- RigidBodyControl myThing_phys = new RigidBodyControl( myThing_shape , 123.0f ); // dynamic ---- [source,java] ---- RigidBodyControl myDungeon_phys = new RigidBodyControl( myDungeon_shape , 0.0f ); // static ---- [IMPORTANT] ==== If you give your floor a non-zero mass, it will fall out of the scene! ==== The following creates a box Geometry with the correct default BoxCollisionShape: [source,java] ---- Box b = new Box(1,1,1); Geometry box_geo = new Geometry("Box", b); box_geo.addControl(new RigidBodyControl( 1.0f )); // explicit non-zero mass, implicit BoxCollisionShape ---- The following creates a MeshCollisionShape for a whole loaded (static) scene: [source,java] ---- ... gameLevel.addControl(new RigidBodyControl(0.0f)); // explicit zero mass, implicit MeshCollisionShape ---- [TIP] ==== Spheres and Boxes automatically fall back on the correct default CollisionShape if you do not specify a CollisionShape in the RigidBodyControl constructor. Complex static objects can fall back on MeshCollisionShapes, unless it is a Node, in which case it will become a CompoundCollisionShape containing a MeshCollisionShape. ==== == Add PhysicsControl to Spatial For each physical Spatial in the scene: . Add a PhysicsControl to a Spatial. + [source,java] ---- myThing_geo.addControl(myThing_phys); ---- . Remember to also attach the Spatial to the rootNode, as always! == Add PhysicsControl to PhysicsSpace The PhysicsSpace is an object in BulletAppState that is like a rootNode for Physics Controls. * Just like you add the Geometry to the rootNode, you add its PhysicsControl to the PhysicsSpace. [source,java] ---- bulletAppState.getPhysicsSpace().add(myThing_phys); rootNode.attachChild(myThing_geo); ---- * When you remove a Geometry from the scene and detach it from the rootNode, also remove the PhysicsControl from the PhysicsSpace: [source,java] ---- bulletAppState.getPhysicsSpace().remove(myThing_phys); myThing_geo.removeFromParent(); ---- [TIP] ==== You can either add the _PhysicsControl_ to the PhysicsSpace, or add the PhysicsControl to the Geometry and then add the _Geometry_ to the PhysicsSpace. jME3 understands both and the outcome is the same. ==== == Changing the Scale of a PhysicsControl To change the scale of a PhysicsControl you must change the scale of the collisionshape which belongs to it. MeshCollisionShapes can have a scale correctly set, but it only works when being constructed on a geometry (not a node). CompoundCollisionShapes cannot be scaled at this time(the type obtained when creating a CollisionShape from a Node i.e using imported models). When you import a model from blender, it often comes as a Node (even if it only contains 1 mesh), which is by de-facto automatically converted to a CompoundCollisionShape. So when you try to scale this it won't work! Below illustrates an example, of how to scale an imported model: [source,java] ---- // Doesn't scale // This modified version contains Node -> Geometry (name = "MonkeyHeadGeom") Spatial model = assetManager.loadModel("Models/MonkeyHead.j3o"); model.addControl(new RigidBodyControl(0)); // Won't work as this is now a CompoundCollisionShape containing a MeshCollisionShape model.getControl(RigidBodyControl.class).getCollisionShape().setScale(new Vector3f(2, 2, 2)); bulletAppState.getPhysicsSpace().add(model); // Works fine Spatial model = assetManager.loadModel("Models/MonkeyHead.j3o"); // Same Model // IMPORTANT : You must navigate to the Geometry for this to work Geometry geom = ((Geometry) ((Node) model).getChild("MonkeyHeadGeom")); geom.addControl(new RigidBodyControl(0)); // Works great (scaling of a MeshCollisionShape) geom.getControl(RigidBodyControl.class).getCollisionShape().setScale(new Vector3f(2, 2, 2)); bulletAppState.getPhysicsSpace().add(geom); ---- With the corresponding output below: image:http://i.imgur.com/Josua.png[http://i.imgur.com/fAXlF.png,width='45%'] image:http://i.imgur.com/fAXlF.png[http://i.imgur.com/fAXlF.png,width='45%'] === PhysicsSpace Code Samples The PhysicsSpace also manages global physics settings. Typically, you can leave the defaults, and you don't need to change the following settings, but it's good to know what they are for: [cols="2", options="header"] |=== a|bulletAppState.getPhysicsSpace() Method a|Usage a|setGravity(new Vector3f(0, -9.81f, 0)); a|Specifies the global gravity. a|setAccuracy(1f/60f); a|Specifies physics accuracy. The higher the accuracy, the slower the game. Decrease value if objects are passing through one another, or bounce oddly. (e.g. Change value from 1f/60f to something like 1f/80f.) a|setMaxSubSteps(4); a|Compensates low FPS: Specifies the maximum amount of extra steps that will be used to step the physics when the game fps is below the physics fps. This maintains determinism in physics in slow (low-fps) games. For example a maximum number of 2 can compensate for framerates as low as 30 fps (physics has a default accuracy of 60 fps). Note that setting this value too high can make the physics drive down its own fps in case its overloaded. a|setWorldMax(new Vector3f(10000f, 10000f, 10000f)); + setWorldMin(new Vector3f(-10000f, -10000f, -10000f)); a|Specifies the size of the physics space as two opposite corners (only applies to AXIS_SWEEP broadphase). |=== == Specify Physical Properties After you have registered, attached, and added everything, you can adjust physical properties or apply forces. On a RigidBodyControl, you can set the following physical properties. [cols="3", options="header"] |=== a| RigidBodyControl Method a| Property a| Examples a| setGravity(new Vector3f(0f,-9.81f,0f)) a| You can change the gravity of individual physics objects after they were added to the PhysicsSpace. Gravity is a vector pointing from this Spatial towards the source of gravity. The longer the vector, the stronger is gravity. + If gravity is the same absolute direction for all objects (e.g. on a planet surface), set this vector globally on the PhysicsSpace object and not individually. + If the center of gravity is relative (e.g. towards a black hole) then setGravity() on each Spatial to constantly adjust the gravity vectors at each tick of their update() loops. a|For planet earth: + `new Vector3f(0f,-9.81f,0f)` a| setMass(1f) a| Sets the mass in kilogram. Dynamic objects have masses > 0.0f. Heavy dynamic objects need more force to be moved and light ones move with small amounts of force. + Static immobile objects (walls, floors, including buildings and terrains) must have a mass of zero! a| Person: 60f, ball: 1.0f + Floor: 0.0f (!) a| setFriction(1f) a| Friction. + Slippery objects have low friction. The ground has high friction. a| Ice, slides: 0.0f + Soil, concrete, rock: 1.0f a| setRestitution(0.0f) a| Bounciness. By default objects are not bouncy (0.0f). For a bouncy rubber object set this > 0.0f. + Both the object and the surface must have non-zero restitution for bouncing to occur. + This setting has an impact on performance, so use it sparingly. a| Brick: 0.0f + Rubber ball: 1.0f a|setCcdMotionThreshold() a|The amount of motion in 1 physics tick to trigger the continuous motion detection in moving objects that push one another. Rarely used, but necessary if your moving objects get stuck or roll through one another. a|around 0.5 to 1 * object diameter |=== On a RigidBodyControl, you can apply the following physical forces: [cols="2", options="header"] |=== a| RigidBodyControl Method a| Motion a| setPhysicsLocation() a|Positions the objects. Do not use setLocalTranslation() for physical objects. Important: Make certain not to make CollisionShapes overlap when positioning them. a| setPhysicsRotation() a|Rotates the object. Do not use setLocalRotate() for physical objects. a| setKinematic(true) a| By default, RigidBodyControls are dynamic (kinematic=false) and are affected by forces. If you set kinematic=true, the object is no longer affected by forces, but it still affects others. A kinematic is solid, and must have a mass. + (See detailed explanation below.) |=== === Kinematic vs Dynamic vs Static All physical objects… * must not overlap. * can detect collisions and report several values about the impact. * can respond to collisions dynamically, or statically, or kinematically. [cols="4", options="header"] |=== |Property |Static |Kinematic |Dynamic a|Examples a|Immobile obstacles: Floors, walls, buildings, … a|Remote-controlled solid objects: Airships, meteorites, elevators, doors; networked or remote-controlled NPCs; invisible “airhooks for hinges and joints. a|Interactive objects: Rolling balls, movable crates, falling pillars, zero-g space ship… a|Does it have a mass? a|no, 0.0f a|yesfootnote:[Inertia is calculated for kinematic objects, and you need mass to do that.], >0.0f a|yes, >0.0f a|How does it move? a|never a|setLocalTranslation(); a|setLinearVelocity(); applyForce(); + setWalkDirection(); for CharacterControl a|How to place in scene? a|setPhysicsLocation(); + setPhysicsRotation(); a|setLocalTranslation(); + setLocalRotation(); a|setPhysicsLocation(); + setPhysicsRotation(); a|Can it move and push others? a|no a|yes a|yes a|Is is affected by forces? + (Falls when it mid-air? Can be pushed by others?) a|no a|no a|yes a|How to activate this behaviour? a|setMass(0f); + setKinematic(false); a|setMass(1f); + setKinematic(true); a|setMass(1f); + setKinematic(false); |=== ==== When Do I Use Kinematic Objects? * Kinematics are solid and characters can “stand on them. * When they collide, Kinematics push dynamic objects, but a dynamic object never pushes a Kinematic. * You can hang kinematics up “in mid-air and attach other PhysicsControls to them using <>. Picture them as “air hooks for flying aircraft carriers, floating islands in the clouds, suspension bridges, swings, chains… * You can use Kinematics to create mobile remote-controlled physical objects, such as moving elevator platforms, flying blimps/airships. You have full control how Kinematics move, they never “fall or “topple over. [IMPORTANT] ==== The position of a kinematic RigidBodyControl is updated automatically depending on its spatial's translation. You move Spatials with a kinematic RigidBodyControl programmatically, that means you write translation and rotation code in the update loop. You describe the motion of kinematic objects either by using methods such as `setLocalTranslation()` or `move()`, or by using a <>. ==== == Forces: Moving Dynamic Objects Use the following methods to move dynamic physical objects. [cols="2", options="header"] |=== a| PhysicsControl Method a| Motion a| setLinearVelocity(new Vector3f(0f,0f,1f)) a| Set the linear speed of this object. a| setAngularVelocity(new Vector3f(0f,0f,1f)) a| Set the rotational speed of the object; the x, y and z component are the speed of rotation around that axis. a| applyCentralForce(…) >.