Use mesh shapes

Create complex rigid body shapes based on .obj meshes. Uses PyChrono.

Learn how to:

  • load a .obj mesh file and use it for visualization of the shape
  • load a .obj mesh file and use it for collision
  • adjust position of center of mass respect to reference in ChBodyAuxRef
  • change inertia properties.
Collision detection with generic concave meshes is slower and less robust than any other options for collision shapes, so use it only if defining the collision shape via primitives like spheres boxes cylinders or their clusters is too complex. (In fact, a good trade-off often is the following: use a detailed mesh for visualization, and few simple primitives for collision.)
The mesh shape is a .obj file in Wavefront file format, you can generate it from 3D modelers such as Blender, Maya, etc., as well as from some CAD.
For collision purposes, the .obj mesh must be "watertight", i.e. having no gaps in edges, no repeated vertexes, etc. If you are not sure about this, the free tool MeshLab, for example, has tools to check the topological correctness of the mesh.
For visualization purposes only, i.e. if you do not use the mesh also for collision, the mesh does not need to be watertight. (btw. If the visualization does not look good, check if the normals are correct in your .obj file.)
1 #------------------------------------------------------------------------------
2 # Name: pychrono example
3 # Purpose:
4 #
5 # Author: Alessandro Tasora
6 #
7 # Created: 1/01/2019
8 # Copyright: (c) ProjectChrono 2019
9 #------------------------------------------------------------------------------
10 
11 
12 import pychrono.core as chrono
13 import pychrono.irrlicht as chronoirr
14 
15 
16 
17 print ("Example: create a rigid body based on a .obj mesh file");
18 
19 # Change this path to asset path, if running from other working dir.
20 # It must point to the data folder, containing GUI assets (textures, fonts, meshes, etc.)
21 chrono.SetChronoDataPath("../../../data/")
22 
23 # ---------------------------------------------------------------------
24 #
25 # Create the simulation system and add items
26 #
27 
28 mysystem = chrono.ChSystemNSC()
29 
30 
31 
32 # Set the global collision margins. This is expecially important for very large or
33 # very small objects. Set this before creating shapes. Not before creating mysystem.
34 chrono.ChCollisionModel.SetDefaultSuggestedEnvelope(0.001);
35 chrono.ChCollisionModel.SetDefaultSuggestedMargin(0.001);
36 
37 # ---------------------------------------------------------------------
38 #
39 # Create the simulation system and add items
40 #
41 
42 # Create a floor
43 mfloor = chrono.ChBodyEasyBox(3, 0.2, 3, 1000,True,True)
44 mfloor.SetBodyFixed(True)
45 mysystem.Add(mfloor)
46 
47 
48 # Now we will create a falling object whose shape is defined by a .obj mesh.
49 #
50 # NOTE: collision detection with generic concave meshes is slower and less
51 # robust than any other options for collision shapes, so use it if defining
52 # collision shapes via primitives like spheres boxes cylinders or their
53 # clusters is too complex.
54 #
55 # NOTE: the mesh shape is a .obj file in Wavefront file format,
56 # you can generate it from 3D modelers such as Blender, Maya, etc.
57 #
58 # NOTE: for collision purposes, the .obj mesh must be "watertight", i.e. having
59 # no gaps in edges, no repeated vertexes, etc.
60 #
61 # NOTE: for visualization purposes only, i.e. if you do not use the mesh also for
62 # collision, the mesh does not need to be watertight.
63 
64 
65 # Method A:
66 # - use the ChBodyEasyMesh
67 # This will automatically create the visualization mesh, the collision mesh,
68 # and will automatically compute the mass property (COG position respect to REF,
69 # mass and inertia tensor) given an uniform density.
70 
71 body_A= chrono.ChBodyEasyMesh(chrono.GetChronoDataPath() +'shoe_view.obj', # mesh filename
72  7000, # density kg/m^3
73  True, # use mesh for visualization?
74  True) # use mesh for collision?
75 body_A.SetPos(chrono.ChVectorD(0.5,0.5,0))
76 mysystem.Add(body_A)
77 
78 
79 
80 # Method B:
81 # - create a ChBodyAuxRef,
82 # - set mass and inertia tensor as you like
83 # - set COG center of mass position respect to REF reference as you like
84 # - attach a visualization shape based on a .obj triangle mesh
85 # - add contact shape based on a .obj triangle mesh
86 # This is more complicate than method A, yet this can be still preferred if you
87 # need deeper control, ex. you want to provide two different meshes, one
88 # with high level of detail just for the visualization and a coarse one for
89 # collision, or if you want to set custom COG and inertia values, etc.
90 
91 # Rigid body part
92 body_B= chrono.ChBodyAuxRef()
93 body_B.SetPos(chrono.ChVectorD(0,0.5,0))
94 body_B.SetMass(16)
95 body_B.SetInertiaXX(chrono.ChVectorD(0.270,0.400,0.427))
96 body_B.SetInertiaXY(chrono.ChVectorD(0.057,0.037,-0.062))
97 body_B.SetFrame_COG_to_REF(chrono.ChFrameD(
98  chrono.ChVectorD( 0.12,0.0,0),
99  chrono.ChQuaternionD(1,0,0,0)))
100 
101 # Attach a visualization shape .
102 # First load a .obj from disk into a ChTriangleMeshConnected:
103 mesh_for_visualization = chrono.ChTriangleMeshConnected()
104 mesh_for_visualization.LoadWavefrontMesh(chrono.GetChronoDataPath() +'shoe_view.obj')
105 # Optionally: you can scale/shrink/rotate the mesh using this:
106 mesh_for_visualization.Transform(chrono.ChVectorD(0.01,0,0), chrono.ChMatrix33D(1))
107 # Now the triangle mesh is inserted in a ChTriangleMeshShape visualization asset,
108 # and added to the body
109 visualization_shape = chrono.ChTriangleMeshShape()
110 visualization_shape.SetMesh(mesh_for_visualization)
111 body_B.AddAsset(visualization_shape)
112 
113 
114 # Add the collision shape.
115 # Again load a .obj file in Wavefront file format. NOTE: in this
116 # example we use the same .obj file as for visualization, but here one
117 # could do a better thing: using a different low-level-of-detail mesh for the
118 # collision, so the simulation performance is not affected by many details such
119 # as bolts and chamfers that may be wanted only for visualization.
120 mesh_for_collision = chrono.ChTriangleMeshConnected()
121 mesh_for_collision.LoadWavefrontMesh(chrono.GetChronoDataPath() +'shoe_view.obj')
122 # Optionally: you can scale/shrink/rotate the mesh using this:
123 mesh_for_collision.Transform(chrono.ChVectorD(0.01,0,0), chrono.ChMatrix33D(1))
124 body_B.GetCollisionModel().ClearModel()
125 body_B.GetCollisionModel().AddTriangleMesh(
126  mesh_for_collision, # the mesh
127  False, # is it static?
128  False) # is it convex?
129  # , mpos, mr, # pos of mesh respect to REF and rotation matr.respect to REF
130  # 0.01) # 'inflating' radiust for triangles for increased robustness
131 body_B.GetCollisionModel().BuildModel()
132 body_B.SetCollide(True)
133 
134 mysystem.Add(body_B)
135 
136 
137 
138 
139 
140 
141 
142 
143 # ---------------------------------------------------------------------
144 #
145 # Create an Irrlicht application to visualize the system
146 #
147 
148 myapplication = chronoirr.ChIrrApp(mysystem, 'PyChrono example', chronoirr.dimension2du(1024,768))
149 
150 myapplication.AddTypicalSky()
151 myapplication.AddTypicalLogo(chrono.GetChronoDataPath() + 'logo_pychrono_alpha.png')
152 myapplication.AddTypicalCamera(chronoirr.vector3df(0.5,0.5,1), chronoirr.vector3df(0,0,0))
153 #myapplication.AddTypicalLights()
154 myapplication.AddLightWithShadow(chronoirr.vector3df(3,6,2), # point
155  chronoirr.vector3df(0,0,0), # aimpoint
156  12, # radius (power)
157  1,11, # near, far
158  55) # angle of FOV
159 
160  # ==IMPORTANT!== Use this function for adding a ChIrrNodeAsset to all items
161  # in the system. These ChIrrNodeAsset assets are 'proxies' to the Irrlicht meshes.
162  # If you need a finer control on which item really needs a visualization proxy in
163  # Irrlicht, just use application.AssetBind(myitem); on a per-item basis.
164 
165 myapplication.AssetBindAll();
166 
167  # ==IMPORTANT!== Use this function for 'converting' into Irrlicht meshes the assets
168  # that you added to the bodies into 3D shapes, they can be visualized by Irrlicht!
169 
170 myapplication.AssetUpdateAll();
171 
172  # If you want to show shadows because you used "AddLightWithShadow()'
173  # you must remember this:
174 myapplication.AddShadowAll();
175 
176 
177 # ---------------------------------------------------------------------
178 #
179 # Run the simulation
180 #
181 
182 
183 myapplication.SetTimestep(0.005)
184 
185 while(myapplication.GetDevice().run()):
186  myapplication.BeginScene()
187  myapplication.DrawAll()
188  myapplication.DoStep()
189  myapplication.EndScene()
190 
191