Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programatic Terrain selection #5

Open
Danfoa opened this issue Jul 4, 2024 · 0 comments
Open

Programatic Terrain selection #5

Danfoa opened this issue Jul 4, 2024 · 0 comments
Assignees
Labels
enhancement New feature or request

Comments

@Danfoa
Copy link
Collaborator

Danfoa commented Jul 4, 2024

Define a terrain optional callback method defining the assets, bodies and geometries composing the ground.

We can define a suit of reference terrains, perlin, flat, ...., and enable the user to define its own. Something similar to what is done here:

def add_perlin_heightfield(
asset: xml_et.Element,
worldbody: xml_et.Element,
position: List[float] = (0.0, 0.0, 0.0), # position
euler_xyz: List[float] = (0.0, 0.0, 0.0), # attitude
size: List[float] = (1.0, 1.0), # width and length of the generated field in meters
max_height: float = 0.4, # Maximum height value of the heightfield in meters
min_height: float = 0.001, # Minimum height value of the heightfield in meters
image_width: int = 128, # height field image size
img_height: int = 128,
smooth: float = 100.0, # smooth scale
perlin_octaves: int = 6, # perlin noise parameter
perlin_persistence: float = 0.5,
perlin_lacunarity: float = 2.0,
output_hfield_image: str = "height_field.png") -> None:
"""
Adds a Perlin noise-based heightfield to the scene.
Args:
asset (xml_et.Element): The asset element of the XML.
worldbody (xml_et.Element): The worldbody element of the XML.
position (List[float]): Position of the heightfield [x, y, z].
euler_xyz (List[float]): Euler angles for the heightfield orientation [roll, pitch, yaw].
size (List[float]): Size of the heightfield [width, length].
max_height (float): Maximum height of the heightfield in meters.
min_height (float): Height in the negative direction of the z axis in meters.
image_width (int): Width of the heightfield image.
img_height (int): Height of the heightfield image.
smooth (float): Smoothing scale for Perlin noise.
perlin_octaves (int): Number of octaves for Perlin noise. Higher values add more detail.
perlin_persistence (float): Persistence value for Perlin noise. Controls amplitude of octaves.
perlin_lacunarity (float): Lacunarity value for Perlin noise. Controls frequency of octaves.
output_hfield_image (str): Filename for the output heightfield image.
Octaves, Persistence, and Lacunarity:
- Octaves: Successive layers of Perlin noise added together for complexity and detail.
- Persistence: Controls the amplitude decrease of higher octaves (e.g., 0.5 reduces amplitude by half).
- Lacunarity: Controls the frequency increase of higher octaves (e.g., 2.0 doubles the frequency).
"""
# Ensure output directory exists
output_path = Path(__file__).resolve().parent / f"assets"
assert output_path.exists(), f"Output path {output_path.absolute().resolve()} does not exist."
file_path = (output_path / output_hfield_image).with_suffix(".png")
# Generating height field based on Perlin noise
terrain_image = np.zeros((img_height, image_width), dtype=np.uint8)
for y in range(image_width):
for x in range(image_width):
noise_value = noise.pnoise2(x / smooth, y / smooth,
octaves=perlin_octaves,
persistence=perlin_persistence,
lacunarity=perlin_lacunarity)
terrain_image[y, x] = int((noise_value + 1) / 2 * 255)
cv2.imwrite(str(file_path.resolve()), terrain_image)
hfield = xml_et.SubElement(asset, "hfield")
hfield.attrib["name"] = "perlin_hfield"
hfield.attrib["size"] = list_to_str([size[0] / 2.0, size[1] / 2.0, max_height, min_height])
hfield.attrib["file"] = str(output_path.resolve())
geo = xml_et.SubElement(worldbody, "geom")
geo.attrib["type"] = "hfield"
geo.attrib["hfield"] = "perlin_hfield"
geo.attrib["pos"] = list_to_str(position)
quat_xyzw = Rotation.from_euler("xyz", euler_xyz).as_quat(canonical=True)
quat_wxyz = np.roll(quat_xyzw, 1)
geo.attrib["quat"] = list_to_str(quat_wxyz)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant