gem-x-motion-capture / gem /utils /vis /o3d_render.py
cs686's picture
Deploy GEM-X ZeroGPU motion capture
49d36c0 verified
Raw
History Blame Contribute Delete
10.7 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import numpy as np
import open3d as o3d
import open3d.visualization.gui as gui
import open3d.visualization.rendering as rendering
import torch
class Settings:
UNLIT = "defaultUnlit"
LIT = "defaultLit"
NORMALS = "normals"
DEPTH = "depth"
LINE = "unlitLine"
Transparency = "defaultLitTransparency"
LitSSR = "defaultLitSSR"
DEFAULT_PROFILE_NAME = "Bright day with sun at +Y [default]"
POINT_CLOUD_PROFILE_NAME = "Cloudy day (no direct sun)"
CUSTOM_PROFILE_NAME = "Custom"
LIGHTING_PROFILES = {
DEFAULT_PROFILE_NAME: {
"ibl_intensity": 45000,
"sun_intensity": 45000,
"sun_dir": [0.577, -0.577, -0.577],
# "ibl_rotation":
"use_ibl": True,
"use_sun": True,
},
"Bright day with sun at -Y": {
"ibl_intensity": 45000,
"sun_intensity": 45000,
"sun_dir": [0.577, 0.577, 0.577],
# "ibl_rotation":[]
"use_ibl": True,
"use_sun": True,
},
"Bright day with sun at +Z": {
"ibl_intensity": 45000,
"sun_intensity": 45000,
"sun_dir": [0.577, 0.577, -0.577],
# "ibl_rotation":
"use_ibl": True,
"use_sun": True,
},
"Less Bright day with sun at +Y": {
"ibl_intensity": 35000,
"sun_intensity": 50000,
"sun_dir": [0.577, -0.577, -0.577],
# "ibl_rotation":
"use_ibl": True,
"use_sun": True,
},
"Less Bright day with sun at -Y": {
"ibl_intensity": 35000,
"sun_intensity": 50000,
"sun_dir": [0.577, 0.577, 0.577],
# "ibl_rotation":
"use_ibl": True,
"use_sun": True,
},
"Less Bright day with sun at +Z": {
"ibl_intensity": 35000,
"sun_intensity": 50000,
"sun_dir": [0.577, 0.577, -0.577],
# "ibl_rotation":
"use_ibl": True,
"use_sun": True,
},
POINT_CLOUD_PROFILE_NAME: {
"ibl_intensity": 60000,
"sun_intensity": 50000,
"use_ibl": True,
"use_sun": False,
# "ibl_rotation":
},
}
DEFAULT_MATERIAL_NAME = "Polished ceramic [default]"
PREFAB = {
DEFAULT_MATERIAL_NAME: {
"metallic": 0.0,
"roughness": 0.7,
"reflectance": 0.5,
"clearcoat": 0.2,
"clearcoat_roughness": 0.2,
"anisotropy": 0.0,
},
"Metal (rougher)": {
"metallic": 1.0,
"roughness": 0.5,
"reflectance": 0.9,
"clearcoat": 0.0,
"clearcoat_roughness": 0.0,
"anisotropy": 0.0,
},
"Metal (smoother)": {
"metallic": 1.0,
"roughness": 0.3,
"reflectance": 0.9,
"clearcoat": 0.0,
"clearcoat_roughness": 0.0,
"anisotropy": 0.0,
},
"Plastic": {
"metallic": 0.0,
"roughness": 0.5,
"reflectance": 0.5,
"clearcoat": 0.5,
"clearcoat_roughness": 0.2,
"anisotropy": 0.0,
},
"Glazed ceramic": {
"metallic": 0.0,
"roughness": 0.5,
"reflectance": 0.9,
"clearcoat": 1.0,
"clearcoat_roughness": 0.1,
"anisotropy": 0.0,
},
"Clay": {
"metallic": 0.0,
"roughness": 1.0,
"reflectance": 0.5,
"clearcoat": 0.1,
"clearcoat_roughness": 0.287,
"anisotropy": 0.0,
},
"Transparency": {
"metallic": 0.0,
"roughness": 0.0,
"reflectance": 0.0,
"clearcoat": 1.0,
"clearcoat_roughness": 0.0,
"anisotropy": 0.0,
},
}
def __init__(self):
# self.mouse_model = gui.SceneWidget.Controls.ROTATE_CAMERA
self.prefab = Settings.DEFAULT_MATERIAL_NAME
self.bg_color = gui.Color(1, 1, 1)
self.show_skybox = True
self.show_ground_plane = True
self.show_axes = False
self.use_ibl = True
self.use_sun = True
self.new_ibl_name = None # clear to None after loading
self.ibl_intensity = 45000
self.sun_intensity = 45000
self.sun_dir = [0.577, -0.577, -0.577]
self.sun_color = gui.Color(1, 1, 1)
self.apply_material = True # clear to False after processing
self._materials = {
Settings.LIT: rendering.MaterialRecord(),
Settings.UNLIT: rendering.MaterialRecord(),
Settings.NORMALS: rendering.MaterialRecord(),
Settings.DEPTH: rendering.MaterialRecord(),
Settings.LINE: rendering.MaterialRecord(),
Settings.Transparency: rendering.MaterialRecord(),
Settings.LitSSR: rendering.MaterialRecord(),
}
self._materials[Settings.LIT].base_color = [0.9, 0.9, 0.9, 1.0]
self._materials[Settings.LIT].shader = Settings.LIT
self._materials[Settings.UNLIT].base_color = [0.9, 0.9, 0.9, 1.0]
self._materials[Settings.UNLIT].shader = Settings.UNLIT
self._materials[Settings.LINE].base_color = [0.9, 0.9, 0.9, 1.0]
self._materials[Settings.LINE].shader = Settings.LINE
self._materials[Settings.LINE].line_width = 3
self._materials[Settings.Transparency].base_color = [0.467, 0.467, 0.467, 0.2]
self._materials[Settings.Transparency].base_color = [0.9, 0.9, 0.9, 0.5]
self._materials[Settings.Transparency].shader = Settings.Transparency
self._materials[Settings.Transparency].thickness = 1.0
self._materials[Settings.Transparency].transmission = 1.0
self._materials[Settings.Transparency].absorption_distance = 10
self._materials[Settings.Transparency].absorption_color = [0.5, 0.5, 0.5]
self._materials[Settings.LitSSR].base_color = [0.467, 0.467, 0.467, 0.2]
self._materials[Settings.LitSSR].shader = Settings.LitSSR
self._materials[Settings.LitSSR].thickness = 1.0
self._materials[Settings.LitSSR].transmission = 1.0
self._materials[Settings.LitSSR].absorption_distance = 10
self._materials[Settings.LitSSR].absorption_color = [0.5, 0.5, 0.5]
self._materials[Settings.NORMALS].shader = Settings.NORMALS
self._materials[Settings.DEPTH].shader = Settings.DEPTH
# Conveniently, assigning from self._materials[...] assigns a reference,
# not a copy, so if we change the property of a material, then switch
# to another one, then come back, the old setting will still be there.
self.material = self._materials[Settings.LIT]
def set_material(self, name):
self.material = self._materials[name]
self.apply_material = True
def apply_material_prefab(self, name):
# assert (self.material.shader == Settings.LIT)
self.prefab = name
for key, val in Settings.PREFAB[name].items():
setattr(self.material, "base_" + key, val)
def apply_lighting_profile(self, name):
"""
It takes a string as an argument, and then sets the attributes of the object to the values in the
dictionary that is associated with that string
Args:
name: The name of the lighting profile.
"""
profile = Settings.LIGHTING_PROFILES[name]
for key, val in profile.items():
setattr(self, key, val)
def get_ground(
scale,
center_x,
center_z,
grid_size=0.5,
color1=None,
color2=None,
):
if color1 is None:
color1 = [0.8, 0.9, 0.9]
if color2 is None:
color2 = [0.6, 0.7, 0.7]
half_size = scale / 2
min_x = center_x - half_size
min_z = center_z - half_size
max_x = center_x + half_size
max_z = center_z + half_size
# Snap to grid
min_x = grid_size * np.floor(min_x / grid_size)
min_z = grid_size * np.floor(min_z / grid_size)
max_x = grid_size * np.ceil(max_x / grid_size)
max_z = grid_size * np.ceil(max_z / grid_size)
vertices = []
faces = []
vertex_colors = []
eps = 1e-4 # Small offset to prevent z-fighting
for i, x in enumerate(np.arange(min_x, max_x, grid_size)):
for j, z in enumerate(np.arange(min_z, max_z, grid_size)):
# Add small offset to prevent z-fighting
x_offset = ((i % 2 * 2) - 1) * eps
z_offset = ((j % 2 * 2) - 1) * eps
# Create vertices for this grid cell
v1 = np.array([x + x_offset, 0, z + z_offset])
v2 = np.array([x + x_offset, 0, z + grid_size + z_offset])
v3 = np.array([x + grid_size + x_offset, 0, z + grid_size + z_offset])
v4 = np.array([x + grid_size + x_offset, 0, z + z_offset])
# Add vertices for both sides of the cell
offset = np.array([0, -eps, 0]) # For bottom face
vertices.extend([v1, v2, v3, v4, v1 + offset, v2 + offset, v3 + offset, v4 + offset])
# Create faces (triangles)
idx = len(vertices) - 8
faces.extend(
[
[idx, idx + 1, idx + 2], # Top face triangle 1
[idx + 2, idx + 3, idx], # Top face triangle 2
[idx + 4, idx + 7, idx + 6], # Bottom face triangle 1
[idx + 6, idx + 5, idx + 4], # Bottom face triangle 2
]
)
# Set vertex colors based on checkerboard pattern
vertex_color = color1 if (i + j) % 2 == 0 else color2
vertex_colors.extend([vertex_color] * 8)
return (
torch.from_numpy(np.array(vertices)),
torch.from_numpy(np.array(faces)),
torch.from_numpy(np.array(vertex_colors)),
)
def create_meshes(verts, faces, colors):
"""
:param verts (B, V, 3)
:param faces (B, F, 3)
:param colors (B, V, 3)
"""
mesh = o3d.geometry.TriangleMesh(
vertices=o3d.utility.Vector3dVector(verts.cpu().numpy()),
triangles=o3d.utility.Vector3iVector(faces.cpu().numpy()),
)
mesh.compute_vertex_normals()
if len(colors.shape) == 1:
colors = colors[None, :].repeat(len(verts), 1)
mesh.vertex_colors = o3d.utility.Vector3dVector(colors.cpu().numpy())
return mesh