Skip to content

Latest commit

 

History

History
279 lines (216 loc) · 11.5 KB

forces.md

File metadata and controls

279 lines (216 loc) · 11.5 KB
Physics (PhysX)

Apply forces to objects

So far, this tutorial has covered examples of physical interactions that occur due to gravitational force. It is also possible to programmatically apply additional forces to objects.

Apply forces and torques

The simplest way to do this is by sending apply_force_to_object. The "force" parameter is the force vector in Newtons. The force is applied instantly as an impulse.

from tdw.controller import Controller
from tdw.tdw_utils import TDWUtils
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.object_manager import ObjectManager

c = Controller()
camera = ThirdPersonCamera(position={"x": -3, "y": 2.1, "z": 0.5},
                           look_at={"x": 0, "y": 0, "z": 0})
object_manager = ObjectManager(rigidbodies=True)
c.add_ons.extend([object_manager, camera])
object_id = c.get_unique_id()
commands = [TDWUtils.create_empty_room(12, 12)]
commands.extend(c.get_add_physics_object(model_name="small_table_green_marble",
                                         object_id=object_id))

# Apply a force.
commands.append({"$type": "apply_force_to_object",
                 "id": object_id,
                 "force": {"x": -8, "y": 700, "z": 5}})

c.communicate(commands)

while not object_manager.rigidbodies[object_id].sleeping:
    c.communicate([])
c.communicate({"$type": "terminate"})

Result:

You can add a torque by sending apply_torque_to_object:

from tdw.controller import Controller
from tdw.tdw_utils import TDWUtils
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.object_manager import ObjectManager

c = Controller()
camera = ThirdPersonCamera(position={"x": -3, "y": 2.1, "z": 0.5},
                           look_at={"x": 0, "y": 0, "z": 0})
object_manager = ObjectManager(rigidbodies=True)
c.add_ons.extend([object_manager, camera])
object_id = c.get_unique_id()
commands = [TDWUtils.create_empty_room(12, 12)]
commands.extend(c.get_add_physics_object(model_name="small_table_green_marble",
                                         object_id=object_id))

# Apply a force and a torque.
commands.extend([{"$type": "apply_force_to_object",
                  "id": object_id,
                  "force": {"x": -8, "y": 700, "z": 5}},
                 {"$type": "apply_torque_to_object",
                  "id": object_id,
                  "torque": {"x": 50, "y": 120, "z": 1}}])

c.communicate(commands)

while not object_manager.rigidbodies[object_id].sleeping:
    c.communicate([])
c.communicate({"$type": "terminate"})

Result:

To apply a directional force at a specific position, send apply_force_at_position:

from tdw.controller import Controller
from tdw.tdw_utils import TDWUtils
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.object_manager import ObjectManager

c = Controller()
camera = ThirdPersonCamera(position={"x": -3, "y": 2.1, "z": 0.5},
                           look_at={"x": 0, "y": 0, "z": 0})
object_manager = ObjectManager(rigidbodies=True)
c.add_ons.extend([object_manager, camera])
object_id = c.get_unique_id()
commands = [TDWUtils.create_empty_room(12, 12)]
commands.extend(c.get_add_physics_object(model_name="small_table_green_marble",
                                         object_id=object_id))

# Apply a force at a position.
commands.append({"$type": "apply_force_at_position",
                 "id": object_id,
                 "force": {"x": 0, "y": 110, "z": 0},
                 "position": {"x": 0, "y": 0.2, "z": -0.5}})

c.communicate(commands)

while not object_manager.rigidbodies[object_id].sleeping:
    c.communicate([])
c.communicate({"$type": "terminate"})

Result:

Apply a force along a forward directional vector

Send apply_force_magnitude_to_object to apply a force of a given magnitude along an objects forward directional vector. This can be convenient for aiming an object at a position.

In this example, we'll add a table. Using an ObjectManager, we'll get the bounds of the table and its top position. Then we'll add a ball; on the same frame that the ball is added, we'll orient the ball using the command object_look_at_position and apply a force using apply_force_magnitude_to_object.

The simulation ends when the ball stops moving. Note that we also need to re-initialize the ObjectManager in order to include the ball's rigidbody data because the ball was added after the ObjectManager was initialized.

from tdw.controller import Controller
from tdw.tdw_utils import TDWUtils
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.object_manager import ObjectManager

c = Controller()
camera = ThirdPersonCamera(position={"x": -4.5, "y": 2.1, "z": 0.5},
                           look_at={"x": 0, "y": 0, "z": 0})
object_manager = ObjectManager(transforms=False, bounds=True, rigidbodies=True)
c.add_ons.extend([camera, object_manager])
table_id = c.get_unique_id()
commands = [TDWUtils.create_empty_room(12, 12)]
commands.extend(c.get_add_physics_object(model_name="small_table_green_marble",
                                         object_id=table_id))
c.communicate(commands)

# Get the top of the table.
table_top = object_manager.bounds[table_id].top

ball_id = c.get_unique_id()
# Add a ball. Note that this is from the models_special.json model library.
commands = c.get_add_physics_object(model_name="prim_sphere",
                                    library="models_special.json",
                                    position={"x": 0.5, "y": 4, "z": -1.3},
                                    scale_factor={"x": 0.2, "y": 0.2, "z": 0.2},
                                    default_physics_values=False,
                                    mass=10,
                                    dynamic_friction=0.3,
                                    static_friction=0.3,
                                    bounciness=0.7,
                                    object_id=ball_id)
# Orient the ball to look at the top of the table. Apply a force.
commands.extend([{"$type": "object_look_at_position",
                 "position": TDWUtils.array_to_vector3(table_top),
                 "id": ball_id},
                 {"$type": "apply_force_magnitude_to_object",
                  "magnitude": 60,
                  "id": ball_id}])
# Re-initialize the object manager.
object_manager.initialized = False
c.communicate(commands)

# Wait until the ball stops moving.
while not object_manager.rigidbodies[ball_id].sleeping:
    c.communicate([])
c.communicate({"$type": "terminate"})

Result:

Apply constant forces and torques

You can apply a constant directional force and torque to an object with add_constant_force. Unlike other force commands, the physics engine will continuously apply force per physics step:

from tdw.controller import Controller
from tdw.tdw_utils import TDWUtils
from tdw.add_ons.third_person_camera import ThirdPersonCamera

c = Controller()
camera = ThirdPersonCamera(position={"x": -3, "y": 2.1, "z": 0.5},
                           look_at={"x": 0, "y": 0, "z": 0})
c.add_ons.append(camera)
object_id = c.get_unique_id()
commands = [TDWUtils.create_empty_room(12, 12)]
commands.extend(c.get_add_physics_object(model_name="small_table_green_marble",
                                         object_id=object_id))

# Apply a constant force.
commands.append({"$type": "add_constant_force",
                 "id": object_id,
                 "force": {"x": 0, "y": 0, "z": 0},
                 "relative_force": {"x": 0, "y": 1200, "z": 0},
                 "torque": {"x": 0, "y": 0, "z": 0},
                 "relative_torque": {"x": 0, "y": 0, "z": 0}})

c.communicate(commands)

for i in range(200):
    c.communicate([])
c.communicate({"$type": "terminate"})

Result:

Set an object's velocity

You can directly set an object's velocity and angular velocity by sending set_velocity and set_angular_velocity, respectively. However, you should only send these commands on the same communicate() call in which the object is added to the scene. If you send these commands after the object is in motion, it can lead to glitchy physics behavior.

from tdw.controller import Controller
from tdw.tdw_utils import TDWUtils
from tdw.add_ons.third_person_camera import ThirdPersonCamera
from tdw.add_ons.image_capture import ImageCapture
from tdw.backend.paths import EXAMPLE_CONTROLLER_OUTPUT_PATH

c = Controller()
path = EXAMPLE_CONTROLLER_OUTPUT_PATH.joinpath("set_velocity")
print(f"Images will be saved to: {path}")
camera = ThirdPersonCamera(position={"x": 0.5, "y": 1.1, "z": -4},
                           look_at={"x": 0, "y": 0, "z": 0},
                           avatar_id="a")
capture = ImageCapture(avatar_ids=["a"], path=path)
c.add_ons.extend([camera, capture])
commands = [TDWUtils.create_empty_room(12, 12)]
object_id = Controller.get_unique_id()
commands.extend(Controller.get_add_physics_object(model_name="chair_billiani_doll",
                                                  object_id=object_id,
                                                  position={"x": 0, "y": 1, "z": 0}))
# Set the object's velocity.
commands.append({"$type": "set_velocity",
                 "id": object_id,
                 "velocity": {"x": 2, "y": 0.5, "z": 0}})
c.communicate(commands)
for i in range(200):
    c.communicate([])
c.communicate({"$type": "terminate"})

Result:


Next: Skip physics frames

Return to the README


Example controllers:

Python API

Command API: