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

Apply unique physical material to each object #572

Draft
wants to merge 2 commits into
base: og-develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion omnigibson/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def cleanup(*args, **kwargs):
shutil.rmtree(tempdir)
except PermissionError:
log.info("Permission error when removing temp files. Ignoring")
from omnigibson.simulator import logo_small
from omnigibson.utils.ui_utils import logo_small
log.info(f"{'-' * 10} Shutting Down {logo_small()} {'-' * 10}")

def shutdown(due_to_signal=False):
Expand Down
6 changes: 3 additions & 3 deletions omnigibson/objects/dataset_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def recursive_light_update(child_prim):
recursive_light_update(self._prim)

# Apply any forced roughness updates
for material in self.materials:
for material in self.visual_materials:
if ("reflection_roughness_texture_influence" in material.shader_input_names and
"reflection_roughness_constant" in material.shader_input_names):
material.reflection_roughness_texture_influence = 0.0
Expand Down Expand Up @@ -255,7 +255,7 @@ def _post_load(self):
# We explicitly provide the root_path to update all the asset paths: the asset paths are relative to the
# original USD folder, i.e. <category>/<model>/usd.
root_path = os.path.dirname(self._usd_path)
for material in self.materials:
for material in self.visual_materials:
material.shader_update_asset_paths_with_root_path(root_path)

# Assign realistic density and mass based on average object category spec
Expand Down Expand Up @@ -289,7 +289,7 @@ def _update_texture_change(self, object_state):
# TODO: uncomment these once our dataset has the object state-conditioned texture maps
# DEFAULT_ALBEDO_MAP_SUFFIX = frozenset({"DIFFUSE", "COMBINED", "albedo"})
# state_name = object_state.__class__.__name__ if object_state is not None else None
for material in self.materials:
for material in self.visual_materials:
# texture_path = material.diffuse_texture
# assert texture_path is not None, f"DatasetObject [{self.prim_path}] has invalid diffuse texture map."
#
Expand Down
4 changes: 2 additions & 2 deletions omnigibson/objects/object_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def _initialize(self):
self._highlighted = False
self._highlight_cached_values = dict()

for material in self.materials:
for material in self.visual_materials:
self._highlight_cached_values[material] = {
"enable_emission": material.enable_emission,
"emissive_color": material.emissive_color,
Expand Down Expand Up @@ -312,7 +312,7 @@ def highlighted(self, enabled):
if enabled == self._highlighted:
return

for material in self.materials:
for material in self.visual_materials:
if enabled:
# Store values before swapping
self._highlight_cached_values[material] = {
Expand Down
4 changes: 2 additions & 2 deletions omnigibson/objects/stateful_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ def get_textures(self):
Returns:
list of str: List of texture file paths
"""
return [material.diffuse_texture for material in self.materials if material.diffuse_texture is not None]
return [material.diffuse_texture for material in self.visual_materials if material.diffuse_texture is not None]

def update_visuals(self):
"""
Expand Down Expand Up @@ -418,7 +418,7 @@ def _update_texture_change(self, object_state):
Args:
object_state (BooleanStateMixin or None): the object state that the diffuse color should match to
"""
for material in self.materials:
for material in self.visual_materials:
self._update_albedo_value(object_state, material)

@staticmethod
Expand Down
6 changes: 2 additions & 4 deletions omnigibson/objects/usd_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,14 @@ def _load(self):
if self._encrypted:
# Create a temporary file to store the decrytped asset, load it, and then delete it
encrypted_filename = self._usd_path.replace(".usd", ".encrypted.usd")
decrypted_fd, usd_path = tempfile.mkstemp(os.path.basename(self._usd_path), dir=og.tempdir)
decrypt_file(encrypted_filename, usd_path)
decrypt_file(encrypted_filename, self._usd_path)

prim = add_asset_to_stage(asset_path=usd_path, prim_path=self._prim_path)

if self._encrypted:
os.close(decrypted_fd)
# On Windows, Isaac Sim won't let go of the file until the prim is removed, so we can't delete it.
if os.name == "posix":
os.remove(usd_path)
os.remove(self._usd_path)

return prim

Expand Down
31 changes: 24 additions & 7 deletions omnigibson/prims/entity_prim.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def __init__(
self._links = None
self._joints = None
self._materials = None
self._visual_materials = None
self._physical_materials = None
self._visual_only = None
self._articulation_tree = None
self._articulation_view_direct = None
Expand All @@ -66,11 +68,8 @@ def _initialize(self):
super()._initialize()

# Force populate inputs and outputs of the shaders of all materials
# We suppress errors from omni.usd if we're using encrypted assets, because we're loading from tmp location,
# not the original location
with suppress_omni_log(channels=["omni.usd"]):
for material in self.materials:
material.shader_force_populate(render=False)
for material in self.visual_materials:
material.shader_force_populate(render=False)

# Initialize all the links
for link in self._links.values():
Expand Down Expand Up @@ -154,6 +153,8 @@ def _post_load(self):
material_paths.add(mat_path)

self._materials = materials
self._visual_materials = {mat for mat in self._materials if mat.visual}
self._physical_materials = {mat for mat in self._materials if mat.physical}

def remove(self):
# First remove all joints
Expand Down Expand Up @@ -525,13 +526,29 @@ def articulation_tree(self):
@property
def materials(self):
"""
Loop through each link and their visual meshes to gather all the materials that belong to this object

Returns:
set of MaterialPrim: a set of MaterialPrim that belongs to this object
"""
return self._materials

@property
def visual_materials(self):
"""
Returns:
set of MaterialPrim: a set of visual MaterialPrim that belongs to this object
"""
return self._visual_materials

@property
def physical_materials(self):
"""
Loop through each link and their visual meshes to gather all the materials that belong to this object

Returns:
set of MaterialPrim: a set of physical MaterialPrim that belongs to this object
"""
return self._visual_materials

@property
def visual_only(self):
"""
Expand Down
158 changes: 142 additions & 16 deletions omnigibson/prims/material_prim.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@
import os

import omnigibson as og
from omnigibson.macros import create_module_macros
import omnigibson.lazy as lazy
from omnigibson.utils.physx_utils import bind_material
from omnigibson.prims.prim_base import BasePrim


# Create settings for this module
m = create_module_macros(module_path=__file__)

m.DEFAULT_STATIC_FRICTION = 0.5
m.DEFAULT_DYNAMIC_FRICTION = 0.5
m.DEFAULT_RESTITUTION = 0.8


class MaterialPrim(BasePrim):
"""
Provides high level functions to deal with a material prim and its attributes/ properties.
Expand All @@ -23,10 +32,20 @@ class MaterialPrim(BasePrim):
@prim_path -- it will be ignored if it already exists. Subclasses should define the exact keys expected
for their class. For this material prim, the below values can be specified:

visual (bool): Whether the generated material should include visual params or not. Default is True
physical (bool): Whether the generated material should include physical params or not. Default is False
mdl_name (None or str): If specified, should be the name of the mdl preset to load (including .mdl).
None results in default, "OmniPBR.mdl"
None results in default, "OmniPBR.mdl". Only relevant if @physical=False
mtl_name (None or str): If specified, should be the name of the mtl preset to load.
None results in default, "OmniPBR"
None results in default, "OmniPBR". Only relevant if @physical=False
density (float): If specified, should be the density for the generated material. Only relevant
if @physical=True. Default is 0.0, which means it is ignored
static_friction (float): If specified, should be the static friction for the generated material.
Only relevant if @physical=True. Default is m.DEFAULT_STATIC_FRICTION
dynamic_friction (None or float): If specified, should be the dynamic friction for the generated material.
Only relevant if @physical=True. Default is m.DEFAULT_DYNAMIC_FRICTION
restitution (None or float): If specified, should be the restitution for the generated material.
Only relevant if @physical=True. Default is m.DEFAULT_RESTITUTION
"""
def __init__(
self,
Expand All @@ -36,6 +55,8 @@ def __init__(
):
# Other values that will be filled in at runtime
self._shader = None
self._visual = None
self._physical = None

# Run super init
super().__init__(
Expand All @@ -45,18 +66,28 @@ def __init__(
)

def _load(self):
# We create a new material at the specified path
mtl_created = []
lazy.omni.kit.commands.execute(
"CreateAndBindMdlMaterialFromLibrary",
mdl_name="OmniPBR.mdl" if self._load_config.get("mdl_name", None) is None else self._load_config["mdl_name"],
mtl_name="OmniPBR" if self._load_config.get("mtl_name", None) is None else self._load_config["mtl_name"],
mtl_created_list=mtl_created,
)
material_path = mtl_created[0]

# Move prim to desired location
lazy.omni.kit.commands.execute("MovePrim", path_from=material_path, path_to=self._prim_path)
self._visual = self._load_config.get("visual", True)
if self._visual:
# We create a new material at the specified path
mtl_created = []
lazy.omni.kit.commands.execute(
"CreateAndBindMdlMaterialFromLibrary",
mdl_name="OmniPBR.mdl" if self._load_config.get("mdl_name", None) is None else self._load_config["mdl_name"],
mtl_name="OmniPBR" if self._load_config.get("mtl_name", None) is None else self._load_config["mtl_name"],
mtl_created_list=mtl_created,
)
material_path = mtl_created[0]

# Move prim to desired location
lazy.omni.kit.commands.execute("MovePrim", path_from=material_path, path_to=self._prim_path)

# Optionally add physical material
if self._load_config.get("physical", False):
self.add_physical_material(
static_friction=self._load_config.get("static_friction", m.DEFAULT_STATIC_FRICTION),
dynamic_friction=self._load_config.get("dynamic_friction", m.DEFAULT_DYNAMIC_FRICTION),
restitution=self._load_config.get("restitution", m.DEFAULT_RESTITUTION),
)

# Return generated material
return lazy.omni.isaac.core.utils.prims.get_prim_at_path(self._prim_path)
Expand All @@ -66,7 +97,32 @@ def _post_load(self):
super()._post_load()

# Generate shader reference
self._shader = lazy.omni.usd.get_shader_from_material(self._prim)
if self._visual:
self._shader = lazy.omni.usd.get_shader_from_material(self._prim)

def add_physical_material(
self,
static_friction=m.DEFAULT_STATIC_FRICTION,
dynamic_friction=m.DEFAULT_DYNAMIC_FRICTION,
restitution=m.DEFAULT_RESTITUTION,
):
"""
Adds a physical material to this material prim

Args:
static_friction (None or float): If specified, should be the static friction for the generated material
dynamic_friction (None or float): If specified, should be the dynamic friction for the generated material
restitution (None or float): If specified, should be the restitution for the generated material
"""
lazy.omni.kit.commands.execute(
"AddRigidBodyMaterialCommand",
stage=og.sim.stage,
path=self._prim_path,
staticFriction=static_friction,
dynamicFriction=dynamic_friction,
restitution=restitution,
)
self._physical = True

def bind(self, target_prim_path):
"""
Expand All @@ -86,6 +142,7 @@ async def _load_mdl_parameters(self, render=True):
Note that a rendering step is necessary to load these parameters, though if a step has already
occurred externally, no additional rendering step is needed
"""
assert self._visual, "Can only load mdl parameters if this material is visual!"
if render:
og.sim.render()
await lazy.omni.usd.get_context().load_mdl_parameters_for_prim_async(self._shader)
Expand All @@ -109,7 +166,6 @@ def shader_update_asset_paths_with_root_path(self, root_path):
Args:
root_path (str): root to be pre-appended to the original asset paths
"""

for inp_name in self.shader_input_names_by_type("SdfAssetPath"):
inp = self.get_input(inp_name)
# If the input doesn't have any path, skip
Expand Down Expand Up @@ -149,6 +205,22 @@ def set_input(self, inp, val):
f"Got invalid shader input to set! Current inputs are: {self.shader_input_names}. Got: {inp}"
self._shader.GetInput(inp).Set(val)

@property
def visual(self):
"""
Returns:
bool: Whether this material is visual or not
"""
return self._visual

@property
def physical(self):
"""
Returns:
bool: Whether this material is physical or not
"""
return self._physical

@property
def is_glass(self):
"""
Expand Down Expand Up @@ -1068,3 +1140,57 @@ def glass_color(self, color):
assert self.is_glass, f"Tried to set glass_color shader input, " \
f"but material at {self.prim_path} is not an OmniGlass material!"
self.set_input(inp="glass_color", val=lazy.pxr.Gf.Vec3f(*np.array(color, dtype=float)))

@property
def static_friction(self):
"""
Returns:
float: static friction of this rigid body
"""
assert self.physical
return self.get_attribute(attr="physics:staticFriction")

@static_friction.setter
def static_friction(self, friction):
"""
Args:
friction (float): static friction of this rigid body
"""
assert self.physical
self.set_attribute(attr="physics:staticFriction", val=friction)

@property
def dynamic_friction(self):
"""
Returns:
float: dynamic friction of this rigid body
"""
assert self.physical
return self.get_attribute(attr="physics:dynamicFriction")

@dynamic_friction.setter
def dynamic_friction(self, friction):
"""
Args:
friction (float): dynamic friction of this rigid body
"""
assert self.physical
self.set_attribute(attr="physics:dynamicFriction", val=friction)

@property
def restitution(self):
"""
Returns:
float: restitution of this rigid body
"""
assert self.physical
return self.get_attribute(attr="physics:restitution")

@restitution.setter
def restitution(self, val):
"""
Args:
val (float): restitution of this rigid body
"""
assert self.physical
self.set_attribute(attr="physics:restitution", val=val)
Loading
Loading