@@ -270,25 +270,48 @@ def _generate_curriculum_terrains(self):
270270 """
271271
272272 def _add_terrain_border (self ):
273- """Add a surrounding border over all the sub-terrains into the terrain meshes."""
274- # border parameters
275- border_size = (
276- self .cfg .num_rows * self .cfg .size [0 ] + 2 * self .cfg .border_width ,
277- self .cfg .num_cols * self .cfg .size [1 ] + 2 * self .cfg .border_width ,
278- )
279- inner_size = (self .cfg .num_rows * self .cfg .size [0 ], self .cfg .num_cols * self .cfg .size [1 ])
280- border_center = (
281- self .cfg .num_rows * self .cfg .size [0 ] / 2 ,
282- self .cfg .num_cols * self .cfg .size [1 ] / 2 ,
283- - self .cfg .border_height / 2 ,
284- )
285- # border mesh
286- border_meshes = make_border (border_size , inner_size , height = abs (self .cfg .border_height ), position = border_center )
287- border = trimesh .util .concatenate (border_meshes )
288- # update the faces to have minimal triangles
289- selector = ~ (np .asarray (border .triangles )[:, :, 2 ] < - 0.1 ).any (1 )
290- border .update_faces (selector )
291- # add the border to the list of meshes
273+ """Add a surrounding border over all the sub-terrains into the terrain meshes.
274+
275+ The border is built as a subdivided grid mesh (matching ``horizontal_scale``)
276+ rather than large box primitives. Large triangles on the border can cause
277+ collision detection failures in some physics backends (e.g. Newton/MuJoCo).
278+ """
279+ bw = self .cfg .border_width
280+ if bw <= 0 :
281+ return
282+
283+ inner_w = self .cfg .num_rows * self .cfg .size [0 ]
284+ inner_l = self .cfg .num_cols * self .cfg .size [1 ]
285+ hs = self .cfg .horizontal_scale
286+
287+ def _make_grid_strip (x0 : float , y0 : float , width : float , length : float ) -> trimesh .Trimesh :
288+ """Create a flat subdivided mesh strip at z=0 with grid spacing ``hs``."""
289+ nx = max (int (np .ceil (width / hs )), 1 ) + 1
290+ ny = max (int (np .ceil (length / hs )), 1 ) + 1
291+ xs = np .linspace (x0 , x0 + width , nx )
292+ ys = np .linspace (y0 , y0 + length , ny )
293+ xx , yy = np .meshgrid (xs , ys , indexing = "ij" )
294+ vertices = np .zeros ((nx * ny , 3 ))
295+ vertices [:, 0 ] = xx .flatten ()
296+ vertices [:, 1 ] = yy .flatten ()
297+ faces = []
298+ for i in range (nx - 1 ):
299+ for j in range (ny - 1 ):
300+ v0 = i * ny + j
301+ v1 = v0 + 1
302+ v2 = (i + 1 ) * ny + j
303+ v3 = v2 + 1
304+ faces .append ([v0 , v2 , v1 ])
305+ faces .append ([v1 , v2 , v3 ])
306+ return trimesh .Trimesh (vertices = vertices , faces = np .array (faces , dtype = np .uint32 ))
307+
308+ strips = [
309+ _make_grid_strip (- bw , - bw , inner_w + 2 * bw , bw ), # bottom
310+ _make_grid_strip (- bw , inner_l , inner_w + 2 * bw , bw ), # top
311+ _make_grid_strip (- bw , 0.0 , bw , inner_l ), # left
312+ _make_grid_strip (inner_w , 0.0 , bw , inner_l ), # right
313+ ]
314+ border = trimesh .util .concatenate (strips )
292315 self .terrain_meshes .append (border )
293316
294317 def _add_sub_terrain (
0 commit comments