Repository: Genesis-Embodied-AI/Genesis
File: tests/test_rigid_physics.py (Python)

Code before:
    qvel = gs_sim.rigid_solver.dofs_state.vel.to_numpy()[:, 0]
    qpos = gs_sim.rigid_solver.dofs_state.pos.to_numpy()[:, 0]
    qpos[0], qpos[1], qpos[2] = 0.1, 0.1, 0.1
    simulate_and_check_mujoco_consistency(gs_sim, mj_sim, qpos, qvel, num_steps=300, atol=atol)


@pytest.mark.parametrize("backend", [gs.cpu])
def test_mesh_to_heightfield(show_viewer):
    from genesis.utils.terrain import mesh_to_heightfield

    ########################## create a scene ##########################
    scene = gs.Scene(
        show_viewer=show_viewer,
        sim_options=gs.options.SimOptions(
            gravity=(2, 0, -2),
        ),
        viewer_options=gs.options.ViewerOptions(
            camera_pos=(0, -50, 0),
            camera_lookat=(0, 0, 0),
        ),
    )

    horizontal_scale = 2.0
    gs_root = os.path.dirname(os.path.abspath(gs.__file__))
    path_terrain = os.path.join(gs_root, "assets", "meshes", "terrain_45.obj")
    hf_terrain, xs, ys = mesh_to_heightfield(path_terrain, spacing=horizontal_scale, oversample=1)

    # default heightfield starts at 0, 0, 0
    # translate to the center of the mesh
    translation = np.array([np.nanmin(xs), np.nanmin(ys), 0])

    terrain_heightfield = scene.add_entity(
        morph=gs.morphs.Terrain(
            horizontal_scale=horizontal_scale,
            vertical_scale=1.0,
            height_field=hf_terrain,
            pos=translation,
        ),
        vis_mode="collision",
    )

    ball = scene.add_entity(
        gs.morphs.Sphere(
            pos=(10, 15, 10),
            radius=1,
        ),
        vis_mode="collision",
    )

    scene.build()


Review comment (suggestion):
Ideally, we could like to automate this process, as for `convexify` but for terrains. So that mesh terrain are automatically converted to heightfield unless specified otherwise. That would be a neat API!

Code after:
    qvel = gs_sim.rigid_solver.dofs_state.vel.to_numpy()[:, 0]
    qpos = gs_sim.rigid_solver.dofs_state.pos.to_numpy()[:, 0]
    qpos[0], qpos[1], qpos[2] = 0.1, 0.1, 0.1
    simulate_and_check_mujoco_consistency(gs_sim, mj_sim, qpos, qvel, num_steps=300, atol=atol)


@pytest.mark.parametrize("backend", [gs.cpu])
def test_mesh_to_heightfield(show_viewer):

    ########################## create a scene ##########################
    scene = gs.Scene(
        show_viewer=show_viewer,
        sim_options=gs.options.SimOptions(
            gravity=(2, 0, -2),
        ),
        viewer_options=gs.options.ViewerOptions(
            camera_pos=(0, -50, 0),
            camera_lookat=(0, 0, 0),
        ),
    )

    horizontal_scale = 2.0
    gs_root = os.path.dirname(os.path.abspath(gs.__file__))
    path_terrain = os.path.join(gs_root, "assets", "meshes", "terrain_45.obj")
    hf_terrain, xs, ys = gs.utils.terrain.mesh_to_heightfield(path_terrain, spacing=horizontal_scale, oversample=1)

    # default heightfield starts at 0, 0, 0
    # translate to the center of the mesh
    translation = np.array([np.nanmin(xs), np.nanmin(ys), 0])

    terrain_heightfield = scene.add_entity(
        morph=gs.morphs.Terrain(
            horizontal_scale=horizontal_scale,
            vertical_scale=1.0,
            height_field=hf_terrain,
            pos=translation,
        ),
        vis_mode="collision",
    )

    ball = scene.add_entity(
        gs.morphs.Sphere(
            pos=(10, 15, 10),
            radius=1,
        ),
        vis_mode="collision",
    )

    scene.build()

    for i in range(1000):