adding mujoco uds bridge

This commit is contained in:
Abhishek Joshi
2024-02-01 02:47:45 -06:00
parent 08bdc9f691
commit 75c0647bcc
5 changed files with 926 additions and 597 deletions
BIN
View File
Binary file not shown.
+581 -356
View File
@@ -1,407 +1,632 @@
import os
from enum import Enum
import numpy as np
import open3d as o3d
from usd_utils import *
from mujoco import mjtGeom
from pxr import Gf, Sdf, Vt
from typing import Optional, List
from mujoco import _structs, _enums
from pxr import Usd, UsdGeom, UsdShade, UsdLux
import mujoco
from mujoco.usd_utilities import *
from pxr import Usd, UsdGeom, UsdLux, UsdShade, Vt, Gf, Sdf
from scipy.spatial.transform import Rotation as R
class USDMesh:
# TODO: clean this up and remove the if statements
def create_usd_geom_primitive(geom, stage, texture_file):
geom_type = geom.type
if geom_type==USDGeomType.Plane.value:
return USDPlane(geom, stage, texture_file)
elif geom_type==USDGeomType.Sphere.value:
return USDSphere(geom, stage, texture_file)
elif geom_type==USDGeomType.Capsule.value:
return USDCapsule(geom, stage, texture_file)
elif geom_type==USDGeomType.Cylinder.value:
return USDCylinder(geom, stage, texture_file)
elif geom_type==USDGeomType.Cube.value:
return USDCube(geom, stage, texture_file)
elif geom_type==USDGeomType.Cube.value:
return USDCube(geom, stage, texture_file)
else:
return None
class USDGeomType(Enum):
def __init__(
self,
stage: Usd.Stage,
model: _structs.MjModel,
geom: _structs.MjvGeom,
objid: int,
dataid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None
):
""" Initializes a new USD mesh
Args:
model: an MjModel instance.
dataid: id of the mesh
texture_file: texture associated with the mesh
"""
Represents different types of geoms we can add to USD
The values match those found by the enum presented here:
https://mujoco.readthedocs.io/en/latest/APIreference/APItypes.html#mjtgeom
"""
Plane = 0
# Hfield = 1
Sphere = 2
Capsule = 3
# Ellipsoid = 4
Cylinder = 5
Cube = 6
Mesh = 7
class USDGeom(object):
"""
Parent class for created geoms
"""
def __init__(self,
geom=None,
stage=None,
texture_file=None):
self.geom = geom
self.stage = stage
self.model = model
self.geom = geom
self.objid = objid
self.rgba = rgba
self.dataid = dataid
self.texture_file = texture_file
self.type = None
self.xform = None
self.prim = None
self.ref = None
# xform operations, set when updating geoms
self.translate_op = None
self.rotate_op = None
self.scale_op = None
xform_path = f'/World/Mesh_Xform_{objid}'
mesh_path= f'{xform_path}/Mesh_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_mesh = UsdGeom.Mesh.Define(stage, mesh_path)
self.usd_prim = stage.GetPrimAtPath(mesh_path)
def set_texture(self):
if self.texture_file:
mtl_path = Sdf.Path(f"/World/Looks/Material_{os.path.splitext(os.path.basename(self.texture_file))[0]}")
# setting mesh structure properties
mesh_vert, mesh_face, mesh_facenum = self._get_mesh_geometry()
self.usd_mesh.GetPointsAttr().Set(mesh_vert)
self.usd_mesh.GetFaceVertexCountsAttr().Set([3 for _ in range(mesh_facenum)])
self.usd_mesh.GetFaceVertexIndicesAttr().Set(mesh_face)
# setting mesh uv properties
mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry()
self.texcoords = UsdGeom.PrimvarsAPI(self.usd_mesh).CreatePrimvar("UVMap",
Sdf.ValueTypeNames.TexCoord2fArray,
UsdGeom.Tokens.faceVarying)
self.texcoords.Set(mesh_texcoord)
self.texcoords.SetIndices(Vt.IntArray(mesh_facetexcoord.tolist()))
self._attach_material()
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
def get_facetexcoord_ranges(self, nmesh, arr):
facetexcoords_ranges = [0]
running_sum = 0
for i in range(nmesh):
running_sum += arr[i] * 3
facetexcoords_ranges.append(running_sum)
return facetexcoords_ranges
def _get_uv_geometry(self):
mesh_texcoord_adr_from = self.model.mesh_texcoordadr[self.dataid]
mesh_texcoord_adr_to = self.model.mesh_texcoordadr[self.dataid+1] if self.dataid < self.model.nmesh - 1 else len(self.model.mesh_texcoord)
mesh_texcoord = self.model.mesh_texcoord[mesh_texcoord_adr_from:mesh_texcoord_adr_to]
mesh_facetexcoord_ranges = self.get_facetexcoord_ranges(self.model.nmesh, self.model.mesh_facenum)
mesh_facetexcoord = self.model.mesh_facetexcoord.flatten()
mesh_facetexcoord = mesh_facetexcoord[mesh_facetexcoord_ranges[self.dataid]:mesh_facetexcoord_ranges[self.dataid+1]]
return mesh_texcoord, mesh_facetexcoord
def _get_mesh_geometry(self):
# get mesh geometry structure from reading the mjModel
mesh_vert_adr_from = self.model.mesh_vertadr[self.dataid]
mesh_vert_adr_to = self.model.mesh_vertadr[self.dataid+1] if self.dataid < self.model.nmesh - 1 else len(self.model.mesh_vert)
mesh_vert = self.model.mesh_vert[mesh_vert_adr_from:mesh_vert_adr_to]
mesh_face_adr_from = self.model.mesh_faceadr[self.dataid]
mesh_face_adr_to = self.model.mesh_faceadr[self.dataid+1] if self.dataid < self.model.nmesh - 1 else len(self.model.mesh_face)
mesh_face = self.model.mesh_face[mesh_face_adr_from:mesh_face_adr_to]
mesh_facenum = self.model.mesh_facenum[self.dataid]
return mesh_vert, mesh_face, mesh_facenum
def _attach_material(self):
mtl_path = Sdf.Path(f"/World/_materials/Material_{self.objid}")
mtl = UsdShade.Material.Define(self.stage, mtl_path)
shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Shader"))
shader.CreateIdAttr("UsdPreviewSurface")
shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set((1.0, 0.0, 0.0))
shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
diffuse_tx = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("DiffuseColorTx"))
diffuse_tx.CreateIdAttr('UsdUVTexture')
if self.texture_file:
bsdf_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Principled_BSDF"))
image_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Image_Texture"))
uvmap_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("uvmap"))
diffuse_tx.CreateInput('file', Sdf.ValueTypeNames.Asset).Set(self.texture_file)
diffuse_tx.CreateOutput('rgb', Sdf.ValueTypeNames.Float3)
shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).ConnectToSource(diffuse_tx.ConnectableAPI(), 'rgb')
mtl.CreateSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), "surface")
# setting the bsdf shader attributes
bsdf_shader.CreateIdAttr("UsdPreviewSurface")
bsdf_shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).ConnectToSource(image_shader.ConnectableAPI(), "rgb")
bsdf_shader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(float(self.rgba[-1]))
bsdf_shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
bsdf_shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
self.prim.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.prim).Bind(mtl)
mtl.CreateSurfaceOutput().ConnectToSource(bsdf_shader.ConnectableAPI(), "surface")
def update_geom(self, new_geom):
self.update_pos(new_geom.pos)
self.update_rotation(new_geom.mat)
self.update_size(new_geom.size)
self.update_color(new_geom.rgba)
self.update_transparency(new_geom.rgba[3])
self.usd_mesh.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.usd_mesh).Bind(mtl)
def update_pos(self, new_pos):
pos = tuple([float(x) for x in new_pos])
if not self.translate_op:
self.translate_op = self.xform.AddTranslateOp()
self.translate_op.Set(pos)
# setting the image texture attributes
image_shader.CreateIdAttr("UsdUVTexture")
image_shader.CreateInput("file", Sdf.ValueTypeNames.Asset).Set(self.texture_file)
image_shader.CreateInput("sourceColorSpace", Sdf.ValueTypeNames.Token).Set("sRGB")
image_shader.CreateInput("wrapS", Sdf.ValueTypeNames.Token).Set("repeat")
image_shader.CreateInput("wrapT", Sdf.ValueTypeNames.Token).Set("repeat")
image_shader.CreateInput("st", Sdf.ValueTypeNames.Float2).ConnectToSource(uvmap_shader.ConnectableAPI(), "result")
image_shader.CreateOutput('rgb', Sdf.ValueTypeNames.Float3)
def update_rotation(self, new_mat):
r = R.from_matrix(new_mat)
euler_rotation = r.as_euler('xyz', degrees=True)
rotation = Gf.Vec3f(float(euler_rotation[0]), float(euler_rotation[1]), float(euler_rotation[2]))
if not self.rotate_op:
self.rotate_op = self.xform.AddRotateXYZOp()
self.rotate_op.Set(rotation)
# setting uvmap shader attributes
uvmap_shader.CreateIdAttr("UsdPrimvarReader_float2")
uvmap_shader.CreateInput("varname", Sdf.ValueTypeNames.Token).Set("UVMap")
uvmap_shader.CreateOutput('results', Sdf.ValueTypeNames.Float2)
else:
bsdf_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Principled_BSDF"))
# TODO: check to make sure scale and size are the same thing
def update_size(self, new_size):
size = tuple([float(x) for x in new_size])
if not self.scale_op:
self.scale_op = self.xform.AddScaleOp()
self.scale_op.Set(value=size)
# settings the bsdf shader attributes
bsdf_shader.CreateIdAttr("UsdPreviewSurface")
bsdf_shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set(tuple(self.rgba[:3]))
bsdf_shader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(float(self.rgba[-1]))
bsdf_shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
bsdf_shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
def update_color(self, new_color):
# new_color is the rgba (we extract first three)
rgba = [(float(x) for x in new_color[:3])]
self.prim.GetDisplayColorAttr().Set(rgba)
mtl.CreateSurfaceOutput().ConnectToSource(bsdf_shader.ConnectableAPI(), "surface")
def update_transparency(self, new_transparency):
self.prim.GetDisplayOpacityAttr().Set([float(abs(new_transparency))])
if new_transparency < 0:
self.prim.MakeInvisible()
self.usd_mesh.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.usd_mesh).Bind(mtl)
def __str__(self):
return f'type = {self.type} \ngeom = {self.geom}'
def update(
self,
pos: np.array,
mat: np.array,
frame: int
):
transformation_mat = create_transform_matrix(rotation_matrix=mat, translation_vector=pos).T
self.transform_op.Set(Gf.Matrix4d(transformation_mat.tolist()), frame)
class USDPlane(USDGeom):
"""
Stores information regarding a plane geom in USD
"""
plane_count = 0
class USDPrimitiveMesh:
def __init__(self,
geom=None,
stage=None,
texture_file=None):
super().__init__(geom, stage, texture_file)
self.type = 0
USDPlane.plane_count += 1
xform_path = f'/World/Plane_Xform_{USDPlane.plane_count}'
plane_path = f'{xform_path}/Plane_{USDPlane.plane_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdGeom.Cube.Define(stage, plane_path) # temporary fix for planes
self.ref = stage.GetPrimAtPath(plane_path)
self.set_texture()
def update_size(self, new_size):
size = tuple([float(new_size[0]), float(new_size[1]), 0.005])
if not self.scale_op:
self.scale_op = self.xform.AddScaleOp()
self.scale_op.Set(value=size)
class USDSphere(USDGeom):
"""
Stores information regarding a sphere geom in USD
"""
sphere_count = 0
def __init__(self,
geom=None,
stage=None,
texture_file=None):
super().__init__(geom, stage, texture_file)
self.type = 2
USDSphere.sphere_count += 1
xform_path = f'/World/Sphere_Xform_{USDSphere.sphere_count}'
sphere_path = f'{xform_path}/Sphere_{USDSphere.sphere_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdGeom.Sphere.Define(stage, sphere_path)
self.ref = stage.GetPrimAtPath(sphere_path)
self.set_texture()
class USDCapsule(USDGeom):
"""
Stores information regarding a capsule geom in USD
"""
capsule_count = 0
def __init__(self,
geom=None,
stage=None,
texture_file=None):
super().__init__(geom, stage, texture_file)
self.type = 3
USDCapsule.capsule_count += 1
xform_path = f'/World/Capsule_Xform_{USDCapsule.capsule_count}'
capsule_path = f'{xform_path}/Capsule_{USDCapsule.capsule_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdGeom.Capsule.Define(stage, capsule_path)
self.ref = stage.GetPrimAtPath(capsule_path)
self.set_texture()
class USDCylinder(USDGeom):
"""
Stores information regarding a capsule geom in USD
"""
cylinder_count = 0
def __init__(self,
geom=None,
stage=None,
texture_file=None):
super().__init__(geom, stage, texture_file)
self.type = 5
USDCylinder.cylinder_count += 1
xform_path = f'/World/Cylinder_Xform_{USDCylinder.cylinder_count}'
cylinder_path = f'{xform_path}/Cylinder_{USDCylinder.cylinder_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdGeom.Cylinder.Define(stage, cylinder_path)
self.ref = stage.GetPrimAtPath(cylinder_path)
self.set_texture()
class USDCube(USDGeom):
"""
Stores information regarding a cube geom in USD
"""
cube_count = 0
def __init__(self,
geom=None,
stage=None,
texture_file=None):
super().__init__(geom, stage, texture_file)
self.type = 6
USDCube.cube_count += 1
xform_path = f'/World/Cube_Xform_{USDCube.cube_count}'
cube_path = f'{xform_path}/Cube_{USDCube.cube_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdGeom.Cube.Define(stage, cube_path)
self.ref = stage.GetPrimAtPath(cube_path)
self.set_texture()
class USDMesh(USDGeom):
"""
Stores information regarding a mesh geom in USD
"""
mesh_count = 0
def __init__(self,
mesh_idx,
geom,
stage,
model,
texture_file):
super().__init__(geom, stage)
assert mesh_idx != -1
mesh_vert_adr_from = model.mesh_vertadr[mesh_idx]
mesh_vert_adr_to = model.mesh_vertadr[mesh_idx+1] if mesh_idx < model.nmesh - 1 else len(model.mesh_vert)
mesh_vert = model.mesh_vert[mesh_vert_adr_from:mesh_vert_adr_to]
mesh_face_adr_from = model.mesh_faceadr[mesh_idx]
mesh_face_adr_to = model.mesh_faceadr[mesh_idx+1] if mesh_idx < model.nmesh - 1 else len(model.mesh_face)
mesh_face = model.mesh_face[mesh_face_adr_from:mesh_face_adr_to]
self.type = 7
xform_path = f'/World/Mesh_Xform_{USDMesh.mesh_count}'
mesh_path= f'{xform_path}/Mesh_{USDMesh.mesh_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdGeom.Mesh.Define(stage, mesh_path)
self.ref = stage.GetPrimAtPath(mesh_path)
self.vertices = mesh_vert
self.prim.GetPointsAttr().Set(self.vertices)
model.mesh_facenum[mesh_idx]
self.prim.GetFaceVertexCountsAttr().Set([3 for _ in range(model.mesh_facenum[mesh_idx])])
self.faces = mesh_face
self.prim.GetFaceVertexIndicesAttr().Set(self.faces)
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
self.stage = stage
self.geom = geom
self.objid = objid
self.rgba = rgba
self.texture_file = texture_file
if texture_file:
mesh_texcoord_adr_from = model.mesh_texcoordadr[mesh_idx]
mesh_texcoord_adr_to = model.mesh_texcoordadr[mesh_idx+1] if mesh_idx < model.nmesh - 1 else len(model.mesh_texcoord)
mesh_texcoord = model.mesh_texcoord[mesh_texcoord_adr_from:mesh_texcoord_adr_to]
self.prim_mesh = None
# texid = geom.texid
# texcoords = model.mesh_texcoord[mesh_texcoord_ranges[texid]:mesh_texcoord_ranges[texid+1]]
def _get_uv_geometry(self):
mesh_facetexcoord_ranges = get_facetexcoord_ranges(model.nmesh, model.mesh_facenum)
assert self.prim_mesh
facetexcoords = model.mesh_facetexcoord.flatten()
facetexcoords = facetexcoords[mesh_facetexcoord_ranges[mesh_idx]:mesh_facetexcoord_ranges[mesh_idx+1]]
self.texcoords = UsdGeom.PrimvarsAPI(self.prim).CreatePrimvar("st",
x_scale, y_scale = self.geom.texrepeat
mesh_texcoord = np.array(self.prim_mesh.triangle_uvs)
mesh_facetexcoord = np.asarray(self.prim_mesh.triangles)
mesh_texcoord[:, 0] *= x_scale
mesh_texcoord[:, 1] *= y_scale
return mesh_texcoord, mesh_facetexcoord.flatten()
def _get_mesh_geometry(self):
assert self.prim_mesh
# get mesh geometry from the open3d mesh model
mesh_vert = np.asarray(self.prim_mesh.vertices)
mesh_face = np.asarray(self.prim_mesh.triangles)
return mesh_vert, mesh_face, len(mesh_face)
def _attach_material(self):
mtl_path = Sdf.Path(f"/World/_materials/Material_{self.objid}")
mtl = UsdShade.Material.Define(self.stage, mtl_path)
if self.texture_file:
# remove all code in this if block
bsdf_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Principled_BSDF"))
image_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Image_Texture"))
uvmap_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("uvmap"))
# setting the bsdf shader attributes
bsdf_shader.CreateIdAttr("UsdPreviewSurface")
bsdf_shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).ConnectToSource(image_shader.ConnectableAPI(), "rgb")
bsdf_shader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(float(self.rgba[-1]))
bsdf_shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
bsdf_shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
mtl.CreateSurfaceOutput().ConnectToSource(bsdf_shader.ConnectableAPI(), "surface")
self.usd_mesh.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.usd_mesh).Bind(mtl)
# setting the image texture attributes
image_shader.CreateIdAttr("UsdUVTexture")
image_shader.CreateInput("file", Sdf.ValueTypeNames.Asset).Set(self.texture_file)
image_shader.CreateInput("sourceColorSpace", Sdf.ValueTypeNames.Token).Set("sRGB")
image_shader.CreateInput("wrapS", Sdf.ValueTypeNames.Token).Set("repeat")
image_shader.CreateInput("wrapT", Sdf.ValueTypeNames.Token).Set("repeat")
image_shader.CreateInput("st", Sdf.ValueTypeNames.Float2).ConnectToSource(uvmap_shader.ConnectableAPI(), "result")
image_shader.CreateOutput('rgb', Sdf.ValueTypeNames.Float3)
# setting uvmap shader attributes
uvmap_shader.CreateIdAttr("UsdPrimvarReader_float2")
uvmap_shader.CreateInput("varname", Sdf.ValueTypeNames.Token).Set("UVMap")
uvmap_shader.CreateOutput('results', Sdf.ValueTypeNames.Float2)
else:
bsdf_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Principled_BSDF"))
# settings the bsdf shader attributes
bsdf_shader.CreateIdAttr("UsdPreviewSurface")
bsdf_shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set(tuple(self.rgba[:3]))
bsdf_shader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(float(self.rgba[-1]))
bsdf_shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
bsdf_shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
mtl.CreateSurfaceOutput().ConnectToSource(bsdf_shader.ConnectableAPI(), "surface")
self.usd_mesh.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.usd_mesh).Bind(mtl)
def update(
self,
pos: np.array,
mat: np.array,
frame: int
):
transformation_mat = create_transform_matrix(rotation_matrix=mat, translation_vector=pos).T
self.transform_op.Set(Gf.Matrix4d(transformation_mat.tolist()), frame)
class USDPrimitive:
def __init__(self,
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
self.stage = stage
self.geom = geom
self.objid = objid
self.rgba = rgba
self.texture_file = texture_file
def _attach_material(self):
mtl_path = Sdf.Path(f"/World/_materials/Material_{self.objid}")
mtl = UsdShade.Material.Define(self.stage, mtl_path)
if self.texture_file:
bsdf_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Principled_BSDF"))
image_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Image_Texture"))
uvmap_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("uvmap"))
# settings the bsdf shader attributes
bsdf_shader.CreateIdAttr("UsdPreviewSurface")
bsdf_shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).ConnectToSource(image_shader.ConnectableAPI(), "rgb")
bsdf_shader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(float(self.rgba[-1]))
bsdf_shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
bsdf_shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
mtl.CreateSurfaceOutput().ConnectToSource(bsdf_shader.ConnectableAPI(), "surface")
self.usd_primitive_shape.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.usd_primitive_shape).Bind(mtl)
# setting the image texture attributes
image_shader.CreateIdAttr("UsdUVTexture")
image_shader.CreateInput("file", Sdf.ValueTypeNames.Asset).Set(self.texture_file)
image_shader.CreateInput("sourceColorSpace", Sdf.ValueTypeNames.Token).Set("sRGB")
image_shader.CreateInput("st", Sdf.ValueTypeNames.Float2).ConnectToSource(uvmap_shader.ConnectableAPI(), "result")
image_shader.CreateOutput('rgb', Sdf.ValueTypeNames.Float3)
# setting uvmap shader attributes
uvmap_shader.CreateIdAttr("UsdPrimvarReader_float2")
uvmap_shader.CreateInput("varname", Sdf.ValueTypeNames.Token).Set("UVMap")
uvmap_shader.CreateOutput('results', Sdf.ValueTypeNames.Float2)
else:
bsdf_shader = UsdShade.Shader.Define(self.stage, mtl_path.AppendPath("Principled_BSDF"))
# settings the bsdf shader attributes
bsdf_shader.CreateIdAttr("UsdPreviewSurface")
bsdf_shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set(tuple(self.rgba[:3]))
bsdf_shader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(float(self.rgba[-1]))
bsdf_shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
bsdf_shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
mtl.CreateSurfaceOutput().ConnectToSource(bsdf_shader.ConnectableAPI(), "surface")
self.usd_primitive_shape.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.usd_primitive_shape).Bind(mtl)
def update(
self,
pos: np.array,
mat: np.array,
frame: int
):
transformation_mat = create_transform_matrix(rotation_matrix=mat, translation_vector=pos).T
self.transform_op.Set(Gf.Matrix4d(transformation_mat.tolist()), frame)
class USDCapsule(USDPrimitive):
def __init__(self,
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
super().__init__(stage,
geom,
objid,
rgba,
texture_file)
xform_path = f'/World/Capsule_Xform_{objid}'
capsule_path = f'{xform_path}/Capsule_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_primitive_shape = UsdGeom.Capsule.Define(stage, capsule_path)
self.usd_prim = stage.GetPrimAtPath(capsule_path)
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
self.scale_op = self.usd_xform.AddScaleOp()
# setting attributes for the shape
self._set_size_attributes()
self._attach_material()
def _set_size_attributes(self):
self.usd_primitive_shape.GetRadiusAttr().Set(float(self.geom.size[0]))
self.usd_primitive_shape.GetHeightAttr().Set(float(self.geom.size[2]*2)) # mujoco gives the half length
class USDEllipsoid(USDPrimitive):
def __init__(self,
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
super().__init__(stage,
geom,
objid,
rgba,
texture_file)
xform_path = f'/World/Ellipsoid_Xform_{objid}'
ellipsoid_path = f'{xform_path}/Ellipsoid_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_primitive_shape = UsdGeom.Sphere.Define(stage, ellipsoid_path)
self.usd_prim = stage.GetPrimAtPath(ellipsoid_path)
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
self.scale_op = self.usd_xform.AddScaleOp()
# setting attributes for the shape
self._set_size_attributes()
self._attach_material()
def _set_size_attributes(self):
self.scale_op.Set(Gf.Vec3d(self.geom.size.tolist()))
class USDCubeMesh(USDPrimitiveMesh):
def __init__(self,
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
super().__init__(stage,
geom,
objid,
rgba,
texture_file)
xform_path = f'/World/CubeMesh_Xform_{objid}'
mesh_path= f'{xform_path}/CubeMesh_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_mesh = UsdGeom.Mesh.Define(stage, mesh_path)
self.usd_prim = stage.GetPrimAtPath(mesh_path)
self.prim_mesh = o3d.geometry.TriangleMesh.create_box(width=self.geom.size[0]*2,
height=self.geom.size[1]*2,
depth=self.geom.size[2]*2,
create_uv_map=True,
map_texture_to_each_face=True)
self.prim_mesh.translate(-self.prim_mesh.get_center())
mesh_vert, mesh_face, mesh_facenum = self._get_mesh_geometry()
self.usd_mesh.GetPointsAttr().Set(mesh_vert)
self.usd_mesh.GetFaceVertexCountsAttr().Set([3 for _ in range(mesh_facenum)])
self.usd_mesh.GetFaceVertexIndicesAttr().Set(mesh_face)
# setting mesh uv properties
mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry()
self.texcoords = UsdGeom.PrimvarsAPI(self.usd_mesh).CreatePrimvar("UVMap",
Sdf.ValueTypeNames.TexCoord2fArray,
UsdGeom.Tokens.faceVarying)
self.texcoords.Set(mesh_texcoord)
self.texcoords.SetIndices(Vt.IntArray(facetexcoords.tolist()));
self.texcoords.SetIndices(Vt.IntArray([i for i in range(mesh_facenum*3)]))
mtl_path = Sdf.Path(f"/World/Looks/Material_{os.path.splitext(os.path.basename(texture_file))[0]}")
mtl = UsdShade.Material.Define(stage, mtl_path)
shader = UsdShade.Shader.Define(stage, mtl_path.AppendPath("Shader"))
shader.CreateIdAttr("UsdPreviewSurface")
shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).Set((1.0, 0.0, 0.0))
shader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(0.5)
shader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
# setting attributes for the shape
self._attach_material()
diffuse_tx = UsdShade.Shader.Define(stage,mtl_path.AppendPath("DiffuseColorTx"))
diffuse_tx.CreateIdAttr('UsdUVTexture')
diffuse_tx.CreateInput('file', Sdf.ValueTypeNames.Asset).Set(texture_file)
diffuse_tx.CreateOutput('rgb', Sdf.ValueTypeNames.Float3)
shader.CreateInput("diffuseColor", Sdf.ValueTypeNames.Color3f).ConnectToSource(diffuse_tx.ConnectableAPI(), 'rgb')
mtl.CreateSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), "surface")
self.prim.GetPrim().ApplyAPI(UsdShade.MaterialBindingAPI)
UsdShade.MaterialBindingAPI(self.prim).Bind(mtl)
USDMesh.mesh_count += 1
def update_geom(self, new_geom):
self.update_pos(new_geom.pos)
self.update_rotation(new_geom.mat)
if not self.texture_file:
self.update_color(new_geom.rgba)
self.update_transparency(new_geom.rgba[3])
class USDLight(object):
"""
Class for the created lights
"""
light_count = 0
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
class USDSphereMesh(USDPrimitiveMesh):
def __init__(self,
stage):
self.stage = stage
USDLight.light_count += 1
xform_path = f'/World/Light_Xform_{USDLight.light_count}'
light_path = f'{xform_path}/Light_{USDLight.light_count}'
self.xform = UsdGeom.Xform.Define(stage, xform_path)
self.prim = UsdLux.SphereLight.Define(stage, light_path)
self.ref = stage.GetPrimAtPath(light_path)
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
self.translate_op = None
super().__init__(stage,
geom,
objid,
rgba,
texture_file)
def update_light(self, new_light):
pos = tuple([float(x) for x in new_light.pos])
if not self.translate_op:
self.translate_op = self.xform.AddTranslateOp()
self.translate_op.Set(pos)
xform_path = f'/World/SphereMesh_Xform_{objid}'
mesh_path= f'{xform_path}/SphereMesh_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_mesh = UsdGeom.Mesh.Define(stage, mesh_path)
self.usd_prim = stage.GetPrimAtPath(mesh_path)
if pos == (0, 0, 0):
self.prim.GetIntensityAttr().Set(0);
else:
self.prim.GetIntensityAttr().Set(20000);
self.prim_mesh = o3d.geometry.TriangleMesh.create_sphere(radius=float(self.geom.size[0]),
create_uv_map=True)
# TODO attributes:
# - direction
# - intensity
# - exposure
# - radius
# - specular
self.prim_mesh.translate(-self.prim_mesh.get_center())
class USDCamera(object):
"""
Class for created cameras
"""
mesh_vert, mesh_face, mesh_facenum = self._get_mesh_geometry()
self.usd_mesh.GetPointsAttr().Set(mesh_vert)
self.usd_mesh.GetFaceVertexCountsAttr().Set([3 for _ in range(mesh_facenum)])
self.usd_mesh.GetFaceVertexIndicesAttr().Set(mesh_face)
camera_count = 0
# setting mesh uv properties
mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry()
self.texcoords = UsdGeom.PrimvarsAPI(self.usd_mesh).CreatePrimvar("UVMap",
Sdf.ValueTypeNames.TexCoord2fArray,
UsdGeom.Tokens.faceVarying)
self.texcoords.Set(mesh_texcoord)
self.texcoords.SetIndices(Vt.IntArray([i for i in range(mesh_facenum*3)]))
# setting attributes for the shape
self._attach_material()
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
class USDCylinderMesh(USDPrimitiveMesh):
def __init__(self,
stage):
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
super().__init__(stage,
geom,
objid,
rgba,
texture_file)
xform_path = f'/World/CylinderMesh_Xform_{objid}'
mesh_path= f'{xform_path}/CylinderMesh_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_mesh = UsdGeom.Mesh.Define(stage, mesh_path)
self.usd_prim = stage.GetPrimAtPath(mesh_path)
self.prim_mesh = o3d.geometry.TriangleMesh.create_cylinder(radius=self.geom.size[0],
height=self.geom.size[2]*2,
create_uv_map=True)
self.prim_mesh.translate(-self.prim_mesh.get_center())
mesh_vert, mesh_face, mesh_facenum = self._get_mesh_geometry()
self.usd_mesh.GetPointsAttr().Set(mesh_vert)
self.usd_mesh.GetFaceVertexCountsAttr().Set([3 for _ in range(mesh_facenum)])
self.usd_mesh.GetFaceVertexIndicesAttr().Set(mesh_face)
# setting mesh uv properties
mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry()
self.texcoords = UsdGeom.PrimvarsAPI(self.usd_mesh).CreatePrimvar("UVMap",
Sdf.ValueTypeNames.TexCoord2fArray,
UsdGeom.Tokens.faceVarying)
self.texcoords.Set(mesh_texcoord)
self.texcoords.SetIndices(Vt.IntArray([i for i in range(mesh_facenum*3)]))
# setting attributes for the shape
self._attach_material()
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
class USDPlaneMesh(USDPrimitiveMesh):
def __init__(self,
stage: Usd.Stage,
geom: _structs.MjvGeom,
objid: int,
rgba: List[int] = [1,1,1,1],
texture_file: Optional[str] = None):
super().__init__(stage,
geom,
objid,
rgba,
texture_file)
xform_path = f'/World/Plane_Xform_{objid}'
plane_path = f'{xform_path}/PlaneMesh_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_mesh = UsdGeom.Mesh.Define(stage, plane_path)
self.usd_prim = stage.GetPrimAtPath(plane_path)
self.prim_mesh = o3d.geometry.TriangleMesh.create_box(width=self.geom.size[0]*2 if self.geom.size[0] > 0 else 100,
height=self.geom.size[1]*2 if self.geom.size[1] > 0 else 100,
depth=0.001,
create_uv_map=True,
map_texture_to_each_face=True)
self.prim_mesh.translate(-self.prim_mesh.get_center())
mesh_vert, mesh_face, mesh_facenum = self._get_mesh_geometry()
self.usd_mesh.GetPointsAttr().Set(mesh_vert)
self.usd_mesh.GetFaceVertexCountsAttr().Set([3 for _ in range(mesh_facenum)])
self.usd_mesh.GetFaceVertexIndicesAttr().Set(mesh_face)
# setting mesh uv properties
mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry()
self.texcoords = UsdGeom.PrimvarsAPI(self.usd_mesh).CreatePrimvar("UVMap",
Sdf.ValueTypeNames.TexCoord2fArray,
UsdGeom.Tokens.faceVarying)
self.texcoords.Set(mesh_texcoord)
self.texcoords.SetIndices(Vt.IntArray([i for i in range(mesh_facenum*3)]))
# setting attributes for the shape
self._attach_material()
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
class USDLight:
def __init__(self,
stage: Usd.Stage,
objid: int,
radius: Optional[float] = 0.7):
self.stage = stage
USDCamera.camera_count += 1
camera_path = f'/World/Camera_{USDCamera.camera_count}'
self.prim = UsdGeom.Camera.Define(stage, camera_path)
self.ref = stage.GetPrimAtPath(camera_path)
def update_camera(self, new_pos, new_quat):
# print("---- Updating camera in USD ----")
xformAPI = UsdGeom.XformCommonAPI(self.prim)
xform_path = f'/World/Light_Xform_{objid}'
light_path = f'{xform_path}/Light_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_light = UsdLux.SphereLight.Define(stage, light_path)
self.usd_prim = stage.GetPrimAtPath(light_path)
pos = tuple([float(x) for x in new_pos])
# convert a quat to euler rotation angles
r = R.from_quat(new_quat)
euler_rotation = r.as_euler('xyz', degrees=True)
rotation = Gf.Vec3f(float(euler_rotation[2]), float(euler_rotation[1]), float(euler_rotation[0]))
# hardcoded values for Robosuite testing and prototype
# TODO: use actual camera values
xformAPI.SetTranslate(pos)
xformAPI.SetRotate(rotation)
# xformAPI.SetScale((1, 1, 1))
self.prim.CreateFocalLengthAttr().Set(24)
self.prim.CreateFocusDistanceAttr().Set(400)
# we assume in mujoco that all lights are point lights
self.usd_light.GetRadiusAttr().Set(radius)
self.usd_light.GetTreatAsPointAttr().Set(False)
self.usd_light.GetNormalizeAttr().Set(True)
# defining ops required by update function
self.translate_op = self.usd_xform.AddTranslateOp()
def update(self,
pos: np.array,
intensity: int,
color: np.array,
frame: int):
self.translate_op.Set(Gf.Vec3d(pos.tolist()), frame)
if not np.any(pos):
intensity = 0
self.usd_light.GetIntensityAttr().Set(intensity)
self.usd_light.GetColorAttr().Set(Gf.Vec3d(color.tolist()))
class USDCamera:
def __init__(self,
stage: Usd.Stage,
objid: int):
self.stage = stage
xform_path = f'/World/Camera_Xform_{objid}'
camera_path = f'{xform_path}/Camera_{objid}'
self.usd_xform = UsdGeom.Xform.Define(stage, xform_path)
self.usd_camera = UsdGeom.Camera.Define(stage, camera_path)
self.usd_prim = stage.GetPrimAtPath(camera_path)
# defining ops required by update function
self.transform_op = self.usd_xform.AddTransformOp()
self.usd_camera.CreateFocalLengthAttr().Set(18.14756) # default in omniverse
self.usd_camera.CreateFocusDistanceAttr().Set(400)
self.usd_camera.GetClippingRangeAttr().Set(Gf.Vec2f(1e-4, 1e6))
def update(self,
cam_pos: np.array,
cam_mat: np.array,
frame: int):
transformation_mat = create_transform_matrix(rotation_matrix=cam_mat, translation_vector=cam_pos).T
self.transform_op.Set(Gf.Matrix4d(transformation_mat.tolist()), frame)
+282 -174
View File
@@ -1,47 +1,87 @@
import os
import shutil
from termcolor import colored
import pprint
import mujoco
import mujoco.viewer as viewer
from mujoco.usd_component import *
from mujoco.usd_utilities import *
from pxr import Usd, UsdGeom
from mujoco import _structs
from PIL import Image as im
from usd_utils import *
from PIL import ImageOps
from mujoco import mjtGeom
from PIL import Image as im
from usd_component import *
from pxr import Usd, UsdGeom
from termcolor import colored
from mujoco import mjv_averageCamera
from typing import Optional, List, Union, Tuple
from mujoco import _structs, _constants, _enums
from scipy.spatial.transform import Rotation as R
class USDRenderer(object):
class USDRenderer:
def __init__(
self,
model: _structs.MjModel,
height: int = 480,
width: int = 480,
max_geom: int = 10000,
output_directory_name: str = "mujoco_usdpkg",
output_directory_root: str = "./",
verbose: bool = True,
light_intensity: int = 10000
):
""" Initializes a new USD Renderer
Args:
model: an MjModel instance.
height: image height in pixels.
width: image width in pixels.
max_geom: Optional integer specifying the maximum number of geoms that can
be rendered in the same scene. If None this will be chosen automatically
based on the estimated maximum number of renderable geoms in the model.
output_directory_name: name of root directory to store outputted frames and assets generated by the USD renderer.
output_directory_root: path to root directory storing generated frames and assets by the USD renderer.
verbose: decides whether to print updates.
"""
Renderer class that creates USD representations for mujoco scenes
"""
def __init__(self,
model,
height=480,
width=480,
root_dir_name="usdpkg",
root_dir_path=None,
verbose=True):
buffer_width = model.vis.global_.offwidth
buffer_height = model.vis.global_.offheight
if width > buffer_width:
raise ValueError(f"""
Image width {width} > framebuffer width {buffer_width}. Either reduce the image
width or specify a larger offscreen framebuffer in the model XML using the
clause:
<visual>
<global offwidth="my_width"/>
</visual>""".lstrip())
if height > buffer_height:
raise ValueError(f"""
Image height {height} > framebuffer height {buffer_height}. Either reduce the
image height or specify a larger offscreen framebuffer in the model XML using
the clause:
<visual>
<global offheight="my_height"/>
</visual>""".lstrip())
self.model = model
self.root_dir_name = root_dir_name
self.root_dir_path = root_dir_path
self.height = height
self.width = width
self.max_geom = max_geom
self.output_directory_name = output_directory_name
self.output_directory_root = output_directory_root
self.verbose = verbose
self.data = None
self.renderer = mujoco.Renderer(model, height, width)
self.reload_scene_info = True
self.frame_count = 0
self.light_intensity = light_intensity
self.create_output_directories()
self.frame_count = 0 # maintains how many times we have saved the scene
self.updates = 0
self.stage = Usd.Stage.CreateInMemory()
# initializing rendering requirements
self.renderer = mujoco.Renderer(model, height, width, max_geom)
self._initialize_usd_stage()
self._scene_option = _structs.MjvOption() # using default scene option
UsdGeom.SetStageUpAxis(self.stage, UsdGeom.Tokens.z)
# initializing output_directories
self._initialize_output_directories()
geom_groups = [0,1,0,0,0,0] # Setting default geom groups for now
self.scene_option = _structs.MjvOption()
self.scene_option.geomgroup = geom_groups
# loading required textures for the scene
self._load_textures()
@property
def usd(self):
@@ -51,167 +91,235 @@ class USDRenderer(object):
def scene(self):
return self.renderer.scene
def create_output_directories(self):
if not self.root_dir_path:
self.root_dir_path = os.getcwd()
def _initialize_usd_stage(self):
self.stage = Usd.Stage.CreateInMemory()
UsdGeom.SetStageUpAxis(self.stage, UsdGeom.Tokens.z)
self.stage.SetStartTimeCode(0)
# add as user imput
self.stage.SetTimeCodesPerSecond(60.0)
self.output_dir = os.path.join(self.root_dir_path, self.root_dir_name)
if not os.path.exists(self.output_dir):
os.makedirs(self.output_dir)
def _initialize_output_directories(self):
self.output_directory_path = os.path.join(self.output_directory_root, self.output_directory_name)
if not os.path.exists(self.output_directory_path):
os.makedirs(self.output_directory_path)
self.scenes_dir = os.path.join(self.output_dir, "scenes")
if not os.path.exists(self.scenes_dir):
os.makedirs(self.scenes_dir)
self.frames_directory = os.path.join(self.output_directory_path, "frames")
if not os.path.exists(self.frames_directory):
os.makedirs(self.frames_directory)
self.assets_dir = os.path.join(self.output_dir, "assets")
if not os.path.exists(self.assets_dir):
os.makedirs(self.assets_dir)
self.assets_directory = os.path.join(self.output_directory_path, "assets")
if not os.path.exists(self.assets_directory):
os.makedirs(self.assets_directory)
if self.verbose:
output_dir_msg = colored(f"Writing files to {self.output_dir}", "green")
print(output_dir_msg)
print(colored(f"Writing output frames and assets to {self.output_directory_path}", "green"))
def update_scene(
self,
data: _structs.MjData,
camera: Union[int, str, _structs.MjvCamera] = -1,
scene_option: Optional[_structs.MjvOption] = None,
):
""" Updates the scene with latest sim data
Args:
data: structure storing current simulation state
scene_option: we use this to determine which geom groups to activate
"""
def save_scene(self):
output_file_path = os.path.join(self.scenes_dir, f'frame_{self.frame_count}_.usd')
with open(output_file_path, "w") as f:
f.write(self.usd)
self.frame_count += 1
def update_geom_groups(self, geom_groups):
self.scene_option.geomgroup = geom_groups
self.reload_scene_info = True
self.update_scene(self.data)
scene_option = scene_option or self._scene_option
def update_scene(self, data):
self.renderer.update_scene(data, scene_option=self.scene_option)
self.data = data
# update the mujoco renderer
self.renderer.update_scene(data,
scene_option=scene_option,
camera=camera)
if self.reload_scene_info:
# loads the initial geoms, lights, and camera information
# from the scene
self._load()
self.reload_scene_info = False
# TODO: update scene options
if self.updates == 0:
self._initialize_usd_stage()
self._update()
self._load_geoms()
self._load_lights()
self._load_cameras()
def _load(self):
"""
Loads and initializes the necessary objects to render the scene
"""
# Create and loads the texture files to the assets directory
# TODO: remove code once added internally to mujoco
data_adr = 0
texture_files = []
for texid in range(self.model.ntex):
height = self.model.tex_height[texid]
width = self.model.tex_width[texid]
pixels = 3*height*width
rgb = self.model.tex_rgb[data_adr:data_adr+pixels]
img = rgb.reshape(height, width, 3)
texture_file_name = f"texture_{texid}.png"
file_path = os.path.join(self.assets_dir, texture_file_name)
img = im.fromarray(img)
img = ImageOps.flip(img)
img.save(file_path)
relative_path = os.path.relpath(self.assets_dir, self.scenes_dir)
img_path = os.path.join(relative_path, texture_file_name)
texture_files.append(img_path)
data_adr += pixels
# initializes an array to store all the geoms in the scene
# populates with "empty" USDGeom objects
self.usd_geoms = []
geoms = self.scene.geoms
self.ngeom = self.scene.ngeom
for i in range(self.ngeom):
geom = geoms[i]
if geom.texid == -1:
texture_file = None
else:
texture_file = texture_files[geom.texid]
if geom.type == USDGeomType.Mesh.value:
self.usd_geoms.append(USDMesh(self.model.geom_dataid[geom.objid],
geom,
self.stage,
self.model,
texture_file))
else:
self.usd_geoms.append(create_usd_geom_primitive(geom,
self.stage,
texture_file))
# initializes an array to store all the lights in the scene
# populates with "empty" USDLight objects
self.usd_lights = []
lights = self.scene.lights
self.nlight = self.scene.nlight
for i in range(self.nlight):
self.usd_lights.append(USDLight(self.stage))
# initializes an array to store all the cameras in the scene
# populates with "empty" USDCamera objects
self.usd_cameras = []
ncam = self.model.ncam
for i in range(ncam):
self.usd_cameras.append(USDCamera(self.stage))
def _update(self):
self._update_geoms()
self._update_lights()
self._update_cameras()
self.updates += 1
def _load_textures(self):
# TODO: remove code once added internally to mujoco
data_adr = 0
self.texture_files = []
for texture_id in range(self.model.ntex):
texture_height = self.model.tex_height[texture_id]
texture_width = self.model.tex_width[texture_id]
pixels = 3*texture_height*texture_width
img = im.fromarray(self.model.tex_rgb[data_adr:data_adr+pixels].reshape(texture_height, texture_width, 3))
img = ImageOps.flip(img)
texture_file_name = f"texture_{texture_id}.png"
img.save(os.path.join(self.assets_directory, texture_file_name))
relative_path = os.path.relpath(self.assets_directory, self.frames_directory)
# img_path = os.path.join(relative_path, texture_file_name) # relative path, TODO: switch back to this
# absolute path for cluster, TODO: remove!
abs_path = os.path.join(os.path.abspath(self.assets_directory), texture_file_name)
self.texture_files.append(abs_path)
data_adr += pixels
if self.verbose:
print(colored(f"Writing texture {texture_id}", "cyan"))
if self.verbose:
print(colored(f"Completed writing {self.model.ntex} textures to {self.assets_directory}", "green"))
def _load_geoms(self):
# stores a list of all the geoms in the scene
self.usd_geoms = []
# initializing the geoms
for i in range(self.scene.ngeom):
geom = self.scene.geoms[i]
if geom.rgba[3] <= 0:
self.usd_geoms.append(None)
continue
# handles meshes in scene
if geom.type == mjtGeom.mjGEOM_MESH:
usd_geom = USDMesh(stage=self.stage,
model=self.model,
geom=geom,
objid=i,
dataid=self.model.geom_dataid[geom.objid],
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
# handles primitives
else:
if geom.type == mjtGeom.mjGEOM_PLANE:
usd_geom = USDPlaneMesh(stage=self.stage,
geom=geom,
objid=i,
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
elif geom.type == mjtGeom.mjGEOM_SPHERE:
usd_geom = USDSphereMesh(stage=self.stage,
geom=geom,
objid=i,
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
elif geom.type == mjtGeom.mjGEOM_CAPSULE:
usd_geom = USDCapsule(stage=self.stage,
geom=geom,
objid=i,
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
elif geom.type == mjtGeom.mjGEOM_ELLIPSOID:
usd_geom = USDEllipsoid(stage=self.stage,
geom=geom,
objid=i,
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
elif geom.type == mjtGeom.mjGEOM_CYLINDER:
usd_geom = USDCylinderMesh(stage=self.stage,
geom=geom,
objid=i,
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
elif geom.type == mjtGeom.mjGEOM_BOX:
usd_geom = USDCubeMesh(stage=self.stage,
geom=geom,
objid=i,
rgba=geom.rgba,
texture_file=self.texture_files[geom.texid] if geom.texid != -1 else None)
else:
usd_geom = None
self.usd_geoms.append(usd_geom)
def _update_geoms(self):
"""
Updates the geoms to match the current scene
"""
geoms = self.scene.geoms
for i in range(self.ngeom):
if self.usd_geoms[i]: # TODO: remove this once all primitives are added
self.usd_geoms[i].update_geom(geoms[i])
# iterate through all geoms in the scene and makes update
for i in range(self.scene.ngeom):
geom = self.scene.geoms[i]
if self.usd_geoms[i]:
self.usd_geoms[i].update(pos=geom.pos,
mat=geom.mat,
frame=self.updates)
def _load_lights(self):
# initializes an usd light object for every light in the scene
self.usd_lights = []
for i in range(self.scene.nlight):
light = self.scene.lights[i]
self.usd_lights.append(USDLight(stage=self.stage,
objid=i))
def _update_lights(self):
"""
Updates the lights to match the current scene
"""
lights = self.scene.lights
nlight = self.scene.nlight
for i in range(nlight):
self.usd_lights[i].update_light(lights[i])
for i in range(self.scene.nlight):
light = self.scene.lights[i]
self.usd_lights[i].update(pos=light.pos,
intensity=self.light_intensity,
color=light.diffuse,
frame=self.updates)
def _load_cameras(self):
self.camera = USDCamera(stage=self.stage,
objid=0)
def _update_cameras(self):
"""
Updates the camera to match the current scene
"""
ncam = self.model.ncam
for i in range(ncam):
self.usd_cameras[i].update_camera(self.model.cam_pos[i], self.model.cam_quat[i])
camera = mjv_averageCamera(self.scene.camera[0], self.scene.camera[1])
def compress(self):
"""
Compresses the output directory to a zip file for easy transfer
"""
forward = camera.forward
up = camera.up
right = np.cross(forward, up)
R = np.eye(3)
R[:, 0] = right
R[:, 1] = up
R[:, 2] = -forward
self.camera.update(cam_pos=camera.pos,
cam_mat=R,
frame=self.updates)
def add_light(self,
pos: List[float],
intensity:int,
radius: Optional[float] = 1.0,
color: Optional[np.array] = np.array([0.3, 0.3, 0.3]),
objid: Optional[int]=1):
new_light = USDLight(stage=self.stage,
objid=objid,
radius=radius)
new_light.update(pos=pos,
intensity=intensity,
color=color,
frame=0)
def add_camera(self,
pos:List[float],
rotation_xyz:List[float],
objid: Optional[int]=1):
# TODO: change this!
new_camera = USDCamera(stage=self.stage,
objid=objid)
r = R.from_euler('xyz', rotation_xyz, degrees=True)
new_camera.update(cam_pos=pos,
cam_mat=r.as_matrix(),
frame=0)
def save_scene(self):
self.stage.SetEndTimeCode(self.frame_count)
# with open(f'./{self.output_directory_name}/frames/frame_{self.frame_count}_.usd', "w") as f:
# f.write(self.usd)
self.stage.Export(f'./{self.output_directory_name}/frames/frame_{self.frame_count}_.usd')
if self.verbose:
output_dir_msg = colored(f"Compressing files at {self.output_dir} and saving at {self.output_dir}", "green")
print(output_dir_msg)
shutil.make_archive(base_name=self.output_dir,
format='zip',
base_dir=self.root_dir_name)
def start_viewer(self):
if self.data:
viewer.launch(self.model)
def render(self):
# should render the usd file given a particular renderer that
# works with USD files
# TODO: determine if this is valid functionality
pass
# TODO: remove later, this is only for debugging purposes
def print_geom_information(self):
for i in range(self.ngeom):
print(self.usd_geoms[i])
print(colored(f"Writing frame_{self.frame_count}", "green"))#
-16
View File
@@ -1,16 +0,0 @@
def get_mesh_ranges(nmesh, arr):
mesh_ranges = [0]
running_sum = 0
for i in range(nmesh):
running_sum += arr[i]
mesh_ranges.append(running_sum)
return mesh_ranges
def get_facetexcoord_ranges(nmesh, arr):
facetexcoords_ranges = [0]
running_sum = 0
for i in range(nmesh):
running_sum += arr[i] * 3
facetexcoords_ranges.append(running_sum)
return facetexcoords_ranges
+12
View File
@@ -0,0 +1,12 @@
import numpy as np
def create_transform_matrix(rotation_matrix, translation_vector):
# Ensure rotation_matrix and translation_vector are NumPy arrays
rotation_matrix = np.array(rotation_matrix)
translation_vector = np.array(translation_vector)
transform_matrix = np.eye(4)
transform_matrix[:3, :3] = rotation_matrix
transform_matrix[:3, 3] = translation_vector
return transform_matrix