diff --git a/omnigibson/__init__.py b/omnigibson/__init__.py index decb9c84a..ae35a472c 100644 --- a/omnigibson/__init__.py +++ b/omnigibson/__init__.py @@ -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): diff --git a/omnigibson/objects/dataset_object.py b/omnigibson/objects/dataset_object.py index 08ae5a4f3..f32ee8ca5 100644 --- a/omnigibson/objects/dataset_object.py +++ b/omnigibson/objects/dataset_object.py @@ -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 @@ -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. //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 @@ -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." # diff --git a/omnigibson/objects/object_base.py b/omnigibson/objects/object_base.py index 2f6a585e2..d3375637d 100644 --- a/omnigibson/objects/object_base.py +++ b/omnigibson/objects/object_base.py @@ -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, @@ -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] = { diff --git a/omnigibson/objects/stateful_object.py b/omnigibson/objects/stateful_object.py index 9eaec129e..e9fb4414e 100644 --- a/omnigibson/objects/stateful_object.py +++ b/omnigibson/objects/stateful_object.py @@ -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): """ @@ -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 diff --git a/omnigibson/objects/usd_object.py b/omnigibson/objects/usd_object.py index 4e07eb1d7..837fd28f2 100644 --- a/omnigibson/objects/usd_object.py +++ b/omnigibson/objects/usd_object.py @@ -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 diff --git a/omnigibson/prims/entity_prim.py b/omnigibson/prims/entity_prim.py index 981deb286..99d17e24e 100644 --- a/omnigibson/prims/entity_prim.py +++ b/omnigibson/prims/entity_prim.py @@ -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 @@ -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(): @@ -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 @@ -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): """ diff --git a/omnigibson/prims/material_prim.py b/omnigibson/prims/material_prim.py index 39d34eb01..4d17b7d85 100644 --- a/omnigibson/prims/material_prim.py +++ b/omnigibson/prims/material_prim.py @@ -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. @@ -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, @@ -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__( @@ -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) @@ -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): """ @@ -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) @@ -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 @@ -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): """ @@ -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) \ No newline at end of file diff --git a/omnigibson/prims/rigid_prim.py b/omnigibson/prims/rigid_prim.py index 9bfcf24de..ac73d8590 100644 --- a/omnigibson/prims/rigid_prim.py +++ b/omnigibson/prims/rigid_prim.py @@ -3,6 +3,7 @@ import omnigibson as og import omnigibson.lazy as lazy from omnigibson.macros import gm, create_module_macros +from omnigibson.prims.material_prim import MaterialPrim from omnigibson.prims.xform_prim import XFormPrim from omnigibson.prims.geom_prim import CollisionGeomPrim, VisualGeomPrim from omnigibson.utils.constants import GEOM_TYPES @@ -121,6 +122,21 @@ def _post_load(self): self._cs = lazy.omni.isaac.sensor._sensor.acquire_contact_sensor_interface() # self._create_contact_sensor() + # Add physical material to this prim + if self.has_material(): + self._material.add_physical_material() + else: + self._material = MaterialPrim( + prim_path=f"{self._prim_path}/physical_mat", + name=f"{self.name}:material", + load_config={ + "visual": False, + "physical": True, + }, + ) + self._material.load() + self._material.bind(self._prim_path) + def _initialize(self): # Run super method first super()._initialize() @@ -336,6 +352,14 @@ def body_name(self): """ return self._body_name + @property + def has_collision_meshes(self): + """ + Returns: + bool: Whether this link has any collision mesh + """ + return len(self._collision_meshes) > 0 + @property def collision_meshes(self): """ @@ -362,14 +386,6 @@ def visual_only(self): """ return self._visual_only - @property - def has_collision_meshes(self): - """ - Returns: - bool: Whether this link has any collision mesh - """ - return len(self._collision_meshes) > 0 - @visual_only.setter def visual_only(self, val): """ @@ -459,6 +475,54 @@ def density(self, density): """ self._rigid_prim_view.set_densities([density]) + @property + def static_friction(self): + """ + Returns: + float: static friction of this rigid body + """ + return self.material.static_friction + + @static_friction.setter + def static_friction(self, friction): + """ + Args: + friction (float): static friction of this rigid body + """ + self.material.static_friction = friction + + @property + def dynamic_friction(self): + """ + Returns: + float: dynamic friction of this rigid body + """ + return self.material.dynamic_friction + + @dynamic_friction.setter + def dynamic_friction(self, friction): + """ + Args: + friction (float): dynamic friction of this rigid body + """ + self.material.dynamic_friction = friction + + @property + def restitution(self): + """ + Returns: + float: restitution of this rigid body + """ + return self.material.restitution + + @restitution.setter + def restitution(self, val): + """ + Args: + val (float): restitution of this rigid body + """ + self.material.restitution = val + @property def kinematic_only(self): """