| name | irrlicht |
| description | Irrlicht visualization — visual shapes (mesh, line, spring), materials, GUI overlays, path rendering, and collision debug. |
| compatibility | pychrono >= 8.0 |
| metadata | {"domain":"visualization"} |
Skill: Irrlicht Visualization (Extended)
Purpose
Cover the full Irrlicht visualization surface beyond basic window setup: loading mesh assets,
custom visual materials, GUI text overlays, path/NURBS line rendering, and collision-shape
debug callbacks.
When to Use
When you need richer visuals than simple primitives — loading .obj/.dae mesh files,
applying textures or custom materials, drawing overlay text, rendering geometric paths, or
debugging collision shapes programmatically.
Key Concepts
Import
import pychrono.irrlicht as chronoirr
Do NOT use pychrono.sensor, ChSensorManager, or ChCameraSensor — those are for the
sensor module, not MBS visualization.
Window Setup (summary — full detail in mbs/visualization)
win_w = int
win_h = int
eye_x = float
eye_y = float
eye_z = float
vis = chronoirr.ChVisualSystemIrrlicht()
vis.AttachSystem(sys)
vis.SetWindowSize(win_w, win_h)
vis.SetWindowTitle('My Simulation')
vis.SetCameraVertical(chrono.CameraVerticalDir_Z)
vis.Initialize()
vis.AddLogo(chrono.GetChronoDataFile('logo_chrono_alpha.png'))
vis.AddSkyBox()
vis.AddCamera(chrono.ChVector3d(eye_x, eye_y, eye_z))
vis.AddTypicalLights()
Camera Vertical Direction (SetCameraVertical)
Irrlicht defaults to Y-up camera rendering. If your physics uses Z-up (gravity along -Z, ground in XY plane), the scene will appear tilted — e.g. horizontal motion looks "upward". Call vis.SetCameraVertical() before vis.Initialize() to match the physics coordinate system:
vis.SetCameraVertical(chrono.CameraVerticalDir_Z)
vis.SetCameraVertical(chrono.CameraVerticalDir_Y)
Rule: set the vertical dir to the axis anti-parallel to gravity (gravity=-z → Z is up → CameraVerticalDir_Z).
Visual Shape Types
| Target | Shape | API |
|---|
| Body | Mesh, triangle, NURBS, path | body.AddVisualShape(shape, frame) |
| Link (ChLinkTSDA) | Spring | spring.AddVisualShape(ChVisualShapeSpring(...)) |
Load a mesh file (ChVisualShapeModelFile)
Use for .obj or .dae (Collada) assets from disk.
mesh_file = str
texture_file = str
offset_x = float
offset_y = float
offset_z = float
objmesh = chrono.ChVisualShapeModelFile()
objmesh.SetFilename(mesh_file)
objmesh.SetTexture(texture_file)
body.AddVisualShape(objmesh, chrono.ChFramed(chrono.ChVector3d(offset_x, offset_y, offset_z), chrono.QUNIT))
Programmatic triangle mesh (ChVisualShapeTriangleMesh)
Build geometry vertex-by-vertex; attach a material for color.
v0_x = float; v0_y = float; v0_z = float
v1_x = float; v1_y = float; v1_z = float
v2_x = float; v2_y = float; v2_z = float
offset_x = float
offset_y = float
offset_z = float
mesh = chrono.ChVisualShapeTriangleMesh()
mesh.GetMesh().AddTriangle(
chrono.ChVector3d(v0_x, v0_y, v0_z),
chrono.ChVector3d(v1_x, v1_y, v1_z),
chrono.ChVector3d(v2_x, v2_y, v2_z))
mesh.AddMaterial(some_material)
body.AddVisualShape(mesh, chrono.ChFramed(chrono.ChVector3d(offset_x, offset_y, offset_z), chrono.QUNIT))
NURBS curve (ChVisualShapeLine + ChLineNurbs)
nurbs_order = int
cp1_x = float; cp1_y = float; cp1_z = float
cp2_x = float; cp2_y = float; cp2_z = float
cp3_x = float; cp3_y = float; cp3_z = float
cp4_x = float; cp4_y = float; cp4_z = float
nurbs = chrono.ChLineNurbs()
v1 = chrono.ChVector3d(cp1_x, cp1_y, cp1_z)
v2 = chrono.ChVector3d(cp2_x, cp2_y, cp2_z)
v3 = chrono.ChVector3d(cp3_x, cp3_y, cp3_z)
v4 = chrono.ChVector3d(cp4_x, cp4_y, cp4_z)
control_pts = chrono.vector_ChVector3d([v1, v2, v3, v4])
nurbs.Setup(nurbs_order, control_pts)
nurbs_asset = chrono.ChVisualShapeLine()
nurbs_asset.SetLineGeometry(nurbs)
body.AddVisualShape(nurbs_asset)
Composite path (ChVisualShapeLine + ChLinePath)
Combine segments and arcs into a single closed or open path.
seg1_ax = float; seg1_ay = float; seg1_az = float
seg1_bx = float; seg1_by = float; seg1_bz = float
arc1_cx = float; arc1_cy = float; arc1_cz = float
arc1_r = float
seg2_ax = float; seg2_ay = float; seg2_az = float
seg2_bx = float; seg2_by = float; seg2_bz = float
arc2_cx = float; arc2_cy = float; arc2_cz = float
arc2_r = float
mpath = chrono.ChLinePath()
seg1 = chrono.ChLineSegment(
chrono.ChVector3d(seg1_ax, seg1_ay, seg1_az),
chrono.ChVector3d(seg1_bx, seg1_by, seg1_bz))
arc1 = chrono.ChLineArc(
chrono.ChCoordsysd(chrono.ChVector3d(arc1_cx, arc1_cy, arc1_cz)),
arc1_r,
-chrono.CH_PI_2,
chrono.CH_PI_2,
True)
seg2 = chrono.ChLineSegment(
chrono.ChVector3d(seg2_ax, seg2_ay, seg2_az),
chrono.ChVector3d(seg2_bx, seg2_by, seg2_bz))
arc2 = chrono.ChLineArc(
chrono.ChCoordsysd(chrono.ChVector3d(arc2_cx, arc2_cy, arc2_cz)),
arc2_r, chrono.CH_PI_2, -chrono.CH_PI_2, True)
mpath.AddSubLine(seg1)
mpath.AddSubLine(arc1)
mpath.AddSubLine(seg2)
mpath.AddSubLine(arc2)
mpath.SetClosed(True)
path_asset = chrono.ChVisualShapeLine()
path_asset.SetLineGeometry(mpath)
body.AddVisualShape(path_asset)
Spring (ChVisualShapeSpring)
Use for ChLinkTSDA (translational spring-damper-actuator) links. The spring visual is attached to the link, not to a body. It automatically updates between the two connected points as the simulation runs.
coil_radius = float
resolution = int
turns = float
spring = chrono.ChLinkTSDA()
spring.Initialize(body1, body2, True,
chrono.ChVector3d(0, 0, 0),
chrono.ChVector3d(0, 0, 0))
spring.SetRestLength(rest_length)
spring.SetSpringCoefficient(k)
spring.SetDampingCoefficient(c)
sys.AddLink(spring)
spring.AddVisualShape(chrono.ChVisualShapeSpring(coil_radius, resolution, turns))
Optional color:
vis = chrono.ChVisualShapeSpring(0.02, 65, 10)
vis.SetColor(chrono.ChColor(0.2, 0.6, 0.9))
spring.AddVisualShape(vis)
Rule: Do not share one ChVisualShapeSpring instance across multiple links — create a separate instance per spring.
Visual Materials (ChVisualMaterial)
Create a shared material and apply it to one or more shapes.
r = float
g = float
b = float
texture_file = str
mat = chrono.ChVisualMaterial()
mat.SetDiffuseColor(chrono.ChColor(r, g, b))
mat.SetKdTexture(texture_file)
shape.AddMaterial(mat)
Shortcut for solid color (no texture):
r = float
g = float
b = float
shape.SetColor(chrono.ChColor(r, g, b))
GUI Text Overlays
Call between BeginScene() and EndScene() every frame.
overlay_x1 = int
overlay_y1 = int
overlay_x2 = int
overlay_y2 = int
while vis.Run():
vis.BeginScene()
vis.Render()
vis.GetGUIEnvironment().addStaticText(
'Hello World!',
chronoirr.recti(overlay_x1, overlay_y1, overlay_x2, overlay_y2))
vis.EndScene()
sys.DoStepDynamics(dt)
addStaticText redraws on every call, so place it inside the loop to keep the text live.
Collision Debug Visualization (Programmatic)
Subclass chrono.VisualizationCallback to receive line-draw calls that describe collision
shape boundaries.
class DebugDrawer(chrono.VisualizationCallback):
def __init__(self):
super().__init__()
def DrawLine(self, pA, pB, color):
print(f" A=({pA.x:.3f}, {pA.y:.3f}, {pA.z:.3f})"
f" B=({pB.x:.3f}, {pB.y:.3f}, {pB.z:.3f})")
drawer = DebugDrawer()
sys.GetCollisionSystem().RegisterVisualizationCallback(drawer)
while vis.Run():
vis.BeginScene()
vis.Render()
vis.EndScene()
sys.DoStepDynamics(dt)
sys.GetCollisionSystem().Visualize(chrono.ChCollisionSystem.VIS_Shapes)
Visualize() triggers DrawLine for every collision shape boundary in the scene.
Built-in Debug Shortcut
While the simulation window is open, press **i** to open the Irrlicht debug panel, then
tick "Draw Collision Shapes" to overlay collision wireframes without writing any callback.