Newton Viewer

KangEngine can be used as a viewer for Newton simulations. Newton owns the simulation while ViewerKE handles rendering and interaction.

Newton mjcf-test

Install

uv pip install newton

Basic example

import newton

from kangengine.adapters.newton import ViewerKE

builder = newton.ModelBuilder()
body = builder.add_body()
builder.add_shape_box(body, hx=0.5, hy=0.5, hz=0.5)

model = builder.finalize(device="cpu")  # or "cuda:0"
state = model.state()

viewer = ViewerKE()
viewer.set_model(model)
try:
    while viewer.is_running():
        # Step Newton and update state here.
        viewer.begin_frame(0.0)
        viewer.log_state(state)
        viewer.end_frame()
finally:
    viewer.close()

Run the complete rigid-body example:

python ./python/examples/adapters/newton/newton_basic_shapes.py
Complete source: newton_basic_shapes.py
  1"""Run a Newton rigid-body simulation with KangEngine as its viewer.
  2
  3Newton owns the model, contacts, solver, and state. KangEngine receives the
  4Newton ViewerBase calls and only handles rendering, input, and the window.
  5Use Shift + Left drag to apply a Newton-owned picking force.
  6"""
  7
  8from __future__ import annotations
  9
 10import argparse
 11
 12import numpy as np
 13
 14from kangengine.adapters.newton import ViewerKE
 15
 16
 17def main():
 18    parser = argparse.ArgumentParser()
 19    parser.add_argument("--width", type=int, default=1920)
 20    parser.add_argument("--height", type=int, default=1080)
 21    parser.add_argument("--headless", action="store_true")
 22    parser.add_argument(
 23        "--frames",
 24        type=int,
 25        default=0,
 26        help="Stop after this many rendered frames; zero runs until closed.",
 27    )
 28    args = parser.parse_args()
 29
 30    import newton
 31    import warp as wp
 32
 33    newton.use_coord_layout_targets = True
 34    builder = newton.ModelBuilder()
 35    builder.add_ground_plane()
 36
 37    sphere = builder.add_body(
 38        xform=wp.transform(p=wp.vec3(-2.0, -2.0, 2.0), q=wp.quat_identity()),
 39        label="sphere",
 40    )
 41    builder.add_shape_sphere(sphere, radius=0.5)
 42
 43    capsule = builder.add_body(
 44        xform=wp.transform(p=wp.vec3(0.0, -2.0, 2.0), q=wp.quat_identity()),
 45        label="capsule",
 46    )
 47    builder.add_shape_capsule(capsule, radius=0.3, half_height=0.7)
 48
 49    box = builder.add_body(
 50        xform=wp.transform(p=wp.vec3(2.0, -2.0, 2.0), q=wp.quat_identity()),
 51        label="box",
 52    )
 53    builder.add_shape_box(box, hx=0.5, hy=0.35, hz=0.25)
 54
 55    cylinder = builder.add_body(
 56        xform=wp.transform(p=wp.vec3(-2.0, 0.0, 2.0), q=wp.quat_identity()),
 57        label="cylinder",
 58    )
 59    builder.add_shape_cylinder(cylinder, radius=0.35, half_height=0.65)
 60
 61    cone = builder.add_body(
 62        xform=wp.transform(p=wp.vec3(0.0, 0.0, 2.0), q=wp.quat_identity()),
 63        label="cone",
 64    )
 65    builder.add_shape_cone(cone, radius=0.5, half_height=0.65)
 66
 67    tetra_mesh = newton.Mesh(
 68        vertices=np.array(
 69            [
 70                [-0.6, -0.5, -0.4],
 71                [0.6, -0.5, -0.4],
 72                [0.0, 0.6, -0.4],
 73                [0.0, 0.0, 0.7],
 74            ],
 75            dtype=np.float32,
 76        ),
 77        indices=np.array([0, 2, 1, 0, 1, 3, 1, 2, 3, 2, 0, 3], dtype=np.int32),
 78    )
 79    mesh_body = builder.add_body(
 80        xform=wp.transform(p=wp.vec3(2.0, 0.0, 2.0), q=wp.quat_identity()),
 81        label="mesh",
 82    )
 83    builder.add_shape_mesh(mesh_body, mesh=tetra_mesh)
 84
 85    model = builder.finalize()
 86    state_0 = model.state()
 87    state_1 = model.state()
 88    control = model.control()
 89    collision_pipeline = newton.CollisionPipeline(model)
 90    contacts = collision_pipeline.contacts()
 91    solver = newton.solvers.SolverXPBD(model, iterations=10)
 92
 93    viewer = ViewerKE(
 94        width=args.width,
 95        height=args.height,
 96        headless=args.headless,
 97    )
 98    viewer.show_ground = False
 99    viewer.app.scene.add_ground("/Ground", scale=20.0)
100    viewer.set_model(model)
101    viewer.set_camera(wp.vec3(8.0, -8.0, 4.0), pitch=-10.0, yaw=135.0)
102
103    frame_dt = 1.0 / 60.0
104    substeps = 4
105    sim_dt = frame_dt / substeps
106    sim_time = 0.0
107    rendered_frames = 0
108
109    try:
110        while viewer.is_running():
111            if viewer.should_step():
112                for _ in range(substeps):
113                    state_0.clear_forces()
114                    viewer.apply_forces(state_0)
115                    collision_pipeline.collide(state_0, contacts)
116                    solver.step(
117                        state_0,
118                        state_1,
119                        control,
120                        contacts,
121                        sim_dt,
122                    )
123                    state_0, state_1 = state_1, state_0
124                sim_time += frame_dt
125
126            viewer.begin_frame(sim_time)
127            viewer.log_state(state_0)
128            viewer.log_contacts(contacts, state_0)
129            viewer.end_frame()
130            rendered_frames += 1
131            if args.frames > 0 and rendered_frames >= args.frames:
132                break
133    finally:
134        viewer.close()
135
136
137if __name__ == "__main__":
138    main()

The example simulates sphere, capsule, box, cylinder, cone, and mesh shapes in Newton and displays them through ViewerKE. Shift + left drag applies a picking force.

Newton rigid-body shapes