diff --git a/doc/source/transform_manager.rst b/doc/source/transform_manager.rst
index 97e8aa86b..b10870469 100644
--- a/doc/source/transform_manager.rst
+++ b/doc/source/transform_manager.rst
@@ -50,3 +50,10 @@ The same class can be used to display collision objects or visuals from URDF
files. The library `trimesh `_ will be used to load
meshes. Here is a simple example with one visual that is used for two links:
:ref:`sphx_glr__auto_examples_plots_plot_urdf_with_meshes.py`.
+
+Note that we can use Matplotlib or Open3D to visualize URDFs or any graph of
+transformations. In previous versions of this library, an example using
+pyrender was also included. This has been removed, as there is now another
+software package that does this:
+`urdf_viz `_. urdf_viz uses
+pytransform3d to load URDFs and displays them in pyrender.
diff --git a/examples/visualizations/render_urdf.py b/examples/visualizations/render_urdf.py
deleted file mode 100644
index 2a2628d9a..000000000
--- a/examples/visualizations/render_urdf.py
+++ /dev/null
@@ -1,212 +0,0 @@
-"""
-===========
-Render URDF
-===========
-
-Use pyrender to show URDF file. You can get pyrender from
-
- https://github.com/mmatl/pyrender
-
-or from pip. Make sure to run this from the main folder of pytransform3d.
-We will load a simple URDF file but the script is able to display other
-URDF files, too. Just change the paths at the end of this file.
-
-Read pyrenders readme for a list of commands that you can use to control
-the viewer.
-"""
-try:
- import pyrender as pr
-except ImportError:
- print("This example needs 'pyrender'")
- exit(1)
-import numpy as np
-import trimesh
-from pytransform3d import urdf
-from pytransform3d.transformations import transform
-
-
-def show_urdf_transform_manager(tm, frame, collision_objects=False,
- visuals=False, frames=False, s=1.0):
- """Render URDF file with pyrender.
-
- Parameters
- ----------
- tm : UrdfTransformManager
- Transformation manager
-
- frame : str
- Base frame for rendering
-
- collision_objects : bool, optional (default: False)
- Render collision objects
-
- visuals : bool, optional (default: False)
- Render visuals
-
- frames : bool, optional (default: False)
- Render frames
-
- s : float, optional (default: 1)
- Axis scale
- """
- scene = pr.Scene()
- if collision_objects:
- if hasattr(tm, "collision_objects"):
- _add_objects(scene, tm, tm.collision_objects, frame)
- if visuals:
- if hasattr(tm, "visuals"):
- _add_objects(scene, tm, tm.visuals, frame)
- if frames:
- for node in tm.nodes:
- _add_frame(scene, tm, node, frame, s)
- pr.Viewer(scene, use_raymond_lighting=True)
-
-
-def _add_objects(scene, tm, objects, frame):
- for obj in objects:
- obj.show(scene, tm, frame)
-
-
-def _add_frame(scene, tm, from_frame, to_frame, s=1.0):
- axis_mesh = pr.Mesh.from_trimesh(
- trimesh.creation.axis(
- origin_size=s * 0.1, axis_radius=s * 0.05, axis_length=s),
- smooth=False)
- n = pr.node.Node(
- mesh=axis_mesh, matrix=tm.get_transform(from_frame, to_frame),
- scale=np.ones(3) * s)
- scene.add_node(n)
-
-
-# We modify the shape objects to include a function that renders them
-
-
-def box_show(self, scene, tm, frame):
- """Render box."""
- A2B = tm.get_transform(self.frame, frame)
-
- corners = np.array([
- [0, 0, 0],
- [0, 0, 1],
- [0, 1, 0],
- [0, 1, 1],
- [1, 0, 0],
- [1, 0, 1],
- [1, 1, 0],
- [1, 1, 1]
- ])
- corners = (corners - 0.5) * self.size
- corners = transform(
- A2B, np.hstack((corners, np.ones((len(corners), 1)))))[:, :3]
-
- mesh = trimesh.Trimesh(
- vertices=corners,
- faces=[[0, 1, 2], [2, 3, 4], [4, 5, 6], [6, 7, 0]]).bounding_box
-
- mesh = pr.Mesh.from_trimesh(mesh)
- scene.add(mesh)
-
-
-urdf.Box.show = box_show
-
-
-def sphere_show(self, scene, tm, frame):
- """Render sphere."""
- A2B = tm.get_transform(self.frame, frame)
-
- center = A2B[:3, 3]
- phi, theta = np.mgrid[0.0:np.pi:100j, 0.0:2.0 * np.pi:100j]
- X = center[0] + self.radius * np.sin(phi) * np.cos(theta)
- Y = center[1] + self.radius * np.sin(phi) * np.sin(theta)
- Z = center[2] + self.radius * np.cos(phi)
-
- vertices = []
- faces = []
- for i in range(X.shape[0] - 1):
- for j in range(X.shape[1] - 1):
- v1 = [X[i, j], Y[i, j], Z[i, j]]
- v2 = [X[i, j + 1], Y[i, j + 1], Z[i, j + 1]]
- v3 = [X[i + 1, j], Y[i + 1, j], Z[i + 1, j]]
- vertices.extend([v1, v2, v3])
- faces.append(list(range(len(vertices) - 3, len(vertices))))
- mesh = trimesh.Trimesh(vertices=vertices, faces=faces).convex_hull
-
- mesh = pr.Mesh.from_trimesh(mesh)
- scene.add(mesh)
-
-
-urdf.Sphere.show = sphere_show
-
-
-def cylinder_show(self, scene, tm, frame):
- """Render cylinder."""
- A2B = tm.get_transform(self.frame, frame)
-
- axis_start = A2B.dot(np.array([0, 0, -0.5 * self.length, 1]))[:3]
- axis_end = A2B.dot(np.array([0, 0, 0.5 * self.length, 1]))[:3]
- axis = axis_end - axis_start
- axis /= self.length
-
- not_axis = np.array([1, 0, 0])
- if (axis == not_axis).all():
- not_axis = np.array([0, 1, 0])
-
- n1 = np.cross(axis, not_axis)
- n1 /= np.linalg.norm(n1)
- n2 = np.cross(axis, n1)
-
- t = np.linspace(0, self.length, 3)
- theta = np.linspace(0, 2 * np.pi, 50)
- t, theta = np.meshgrid(t, theta)
- X, Y, Z = [axis_start[i] + axis[i] * t +
- self.radius * np.sin(theta) * n1[i] +
- self.radius * np.cos(theta) * n2[i] for i in [0, 1, 2]]
-
- vertices = []
- faces = []
- for i in range(X.shape[0] - 1):
- for j in range(X.shape[1] - 1):
- v1 = [X[i, j], Y[i, j], Z[i, j]]
- v2 = [X[i, j + 1], Y[i, j + 1], Z[i, j + 1]]
- v3 = [X[i + 1, j], Y[i + 1, j], Z[i + 1, j]]
- vertices.extend([v1, v2, v3])
- faces.append(list(range(len(vertices) - 3, len(vertices))))
- mesh = trimesh.Trimesh(vertices=vertices, faces=faces).convex_hull
-
- mesh = pr.Mesh.from_trimesh(mesh)
- scene.add(mesh)
-
-
-urdf.Cylinder.show = cylinder_show
-
-
-def mesh_show(self, scene, tm, frame):
- """Render mesh."""
- if self.mesh_path is None:
- print("No mesh path given")
- return
- A2B = tm.get_transform(self.frame, frame)
-
- scale = self.scale
- mesh = trimesh.load(self.filename)
- mesh.vertices *= scale
-
- mesh = pr.Mesh.from_trimesh(mesh)
- scene.add(mesh, pose=A2B)
-
-
-urdf.Mesh.show = mesh_show
-
-
-# Load your own URDF here:
-# (this script should be started from the main directory)
-mesh_path = "test/test_data/"
-filename = "test/test_data/simple_mechanism.urdf"
-frame = "lower_cone"
-
-tm = urdf.UrdfTransformManager()
-with open(filename, "r") as f:
- tm.load_urdf(f.read(), mesh_path=mesh_path)
-tm.set_joint("joint", 0.25 * np.pi)
-show_urdf_transform_manager(
- tm, frame, visuals=True, collision_objects=False, frames=True, s=0.05)