3 from pxr
import Usd, Sdf, UsdShade, UsdGeom, Gf, UsdLux, UsdUtils
5 self.logger.info(
"Error: Python module 'pxr' not found. Please ensure that the USD Python bindings are installed.")
8import materialxusd_custom
as mxcust
12 @brief Class that converts a MaterialX file to a USD file with an appropriate scene.
16 @brief Constructor for the MaterialxUSDConverter class.
18 logging.basicConfig(level=logging.INFO)
19 self.
logger = logging.getLogger(
'MX2USD')
24 @brief This function validates a USD file using the ComplianceChecker.
25 @param file: The path to the USD file to validate.
26 @param verboseOutput: If True, the compliance check will output verbose information. Default is False.
27 @return: A tuple containing the errors, warnings, and failed checks.
30 compliance_checker = UsdUtils.ComplianceChecker(
31 rootPackageOnly=
False,
37 compliance_checker.CheckCompliance(file)
40 errors = compliance_checker.GetErrors()
41 warnings = compliance_checker.GetWarnings()
42 failed_checks = compliance_checker.GetFailedChecks()
43 return errors, warnings, failed_checks
47 @brief This function finds the first valid prim in root layer of a stage.
48 @param stage: The stage to search for the first valid prim.
49 @return: The first valid prim found in the stage. If no valid prim is found, None is returned.
52 root_layer = stage.GetRootLayer()
56 for prim
in stage.Traverse():
65 @brief This function sets the required validation attributes for the stage.
66 For now this function sets the upAxis and metersPerUnit. to Y and 1.0 respectively.
67 @param stage: The stage to set the required validation attributes.
70 UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.y)
71 UsdGeom.SetStageMetersPerUnit(stage, 1.0)
75 @brief This function finds the first material in the stage. Assumes MaterialX
76 materials are stored under the "/MaterialX/Materials" scope.
77 @param stage: The stage to search for the first material.
78 @param find_first: If True, only the first material found is returned. Default is True.
79 @return: The first material found in the stage. If no material is found, None is returned.
84 materialx_prim = stage.GetPrimAtPath(
"/MaterialX")
85 if not materialx_prim:
86 self.
logger.info(
"> Warning: Could not find /MaterialX scope in the USDA file.")
87 return found_materials
89 materials_prim = materialx_prim.GetPrimAtPath(
"Materials")
90 if not materials_prim:
91 self.
logger.info(
"> Warning: Could not find /MaterialX/Materials scope in the USDA file.")
92 return found_materials
94 for child_prim
in materials_prim.GetAllChildren():
95 if child_prim.GetTypeName() ==
"Material":
96 found_materials.append(child_prim)
100 return found_materials
102 def add_skydome_light(self, stage: Usd.Stage, environment_path:str, root_path:str =
"/TestScene/Lights", light_name:str =
"EnvironmentLight", xform_scale=Gf.Vec3f(1.3, 1.3, 1.3), xform_rotate=Gf.Vec3f(0, 0, 0)):
104 @brief This function adds a skydome light to the stage.
105 @param stage: The stage to add the skydome light.
106 @param environment_path: The path to the environment light file.
107 @param root_path: The root path to add the skydome light.
108 @param light_name: The name of the skydome light.
109 @param xform_scale: The scale of the skydome light.
110 @param xform_rotate: The rotation of the skydome light.
111 @return: The skydome light added to the stage.
113 skydome_prim = stage.DefinePrim(root_path,
"Xform")
115 xformable = UsdGeom.Xformable(skydome_prim)
118 scale_op = xformable.GetScaleOp()
120 scale_op = xformable.AddScaleOp(UsdGeom.XformOp.PrecisionFloat)
121 scale_value = xform_scale
122 scale_op.Set(scale_value)
124 dome_light = UsdLux.DomeLight.Define(stage, f
"{root_path}/{light_name}")
127 dome_light.CreateIntensityAttr().Set(1.0)
128 dome_light.CreateTextureFileAttr().Set(environment_path)
131 dome_light.CreateGuideRadiusAttr().Set(1.0)
134 xformable = UsdGeom.Xformable(dome_light)
135 xform_op = xformable.GetXformOp(UsdGeom.XformOp.TypeRotateXYZ)
137 xform_op = xformable.AddXformOp(UsdGeom.XformOp.TypeRotateXYZ, UsdGeom.XformOp.PrecisionFloat)
138 xform_op.Set(xform_rotate)
141 xformable.SetXformOpOrder([xform_op])
147 @brief This function adds a geometry reference to the stage.
148 @param stage: The stage to add the geometry reference.
149 @param geometry_path: The path to the geometry file.
150 @param root_path: The root path to add the geometry reference.
152 geom_prim = stage.DefinePrim(root_path,
"Xform")
153 geom_prim.GetReferences().AddReference(geometry_path)
158 @brief This function finds the first camera in the stage.
159 @param stage: The stage to search for the first camera.
160 @return: The first camera found in the stage. If no camera is found, None is returned.
163 for prim
in stage.Traverse():
165 if prim.IsA(UsdGeom.Camera):
169 def add_camera(self, stage : Usd.Stage, camera_path : str, root_path : str=
"/TestScene/Camera", geometry_path : str=
"/TestScene/Geometry"):
171 @brief This function adds a camera to the stage.
172 @param stage: The stage to add the camera.
173 @param camera_path: The path to the camera file.
174 @param root_path: The root path to add the camera.
175 @param geometry_path: The path to the geometry file.
178 camera = stage.DefinePrim(root_path,
"Xform")
179 camera.GetReferences().AddReference(camera_path)
183 geometry_path = Sdf.Path(geometry_path)
186 geometry_prim = stage.GetPrimAtPath(geometry_path)
188 camera_path = Sdf.Path(root_path)
189 camera = UsdGeom.Camera.Define(stage, camera_path)
192 bbox_cache = UsdGeom.BBoxCache(Usd.TimeCode.Default(), [UsdGeom.Tokens.default_])
193 bbox = bbox_cache.ComputeWorldBound(geometry_prim)
194 bbox_range = bbox.ComputeAlignedRange()
197 bbox_center = bbox_range.GetMidpoint()
198 bbox_size = bbox_range.GetSize()
202 distance = max(bbox_size) * 1.5
205 camera_position = Gf.Vec3f(bbox_center) + Gf.Vec3f(0, 0, distance)
206 camera.AddTranslateOp().Set(camera_position)
209 camera.AddRotateYOp().Set(0)
210 look_at = UsdGeom.XformCommonAPI(camera)
211 look_at.SetRotate((0, 0, 0))
215 camera.GetFocalLengthAttr().Set(focal_length)
218 horizontal_aperture = bbox_size[0] / bbox_size[1] * 20.0
219 vertical_aperture = horizontal_aperture
220 camera.GetHorizontalApertureAttr().Set(horizontal_aperture)
221 camera.GetVerticalApertureAttr().Set(vertical_aperture)
224 near_clip = distance - max(bbox_size) * 0.5
225 far_clip = distance + max(bbox_size) * 0.5
226 camera.GetClippingRangeAttr().Set(Gf.Vec2f(near_clip, far_clip))
233 def mtlx_to_usd(self, input_usd_path : str, shaderball_path : str, environment_path : str, material_file_path : str, camera_path : str,
236 @brief This function reads the input usd file and adds the shaderball geometry and environment light
237 to the scene. It also binds the first material to the shaderball geometry. The final stage is returned.
238 @param input_usd_path: Path to the input usd file
239 @param shaderball_path: Path to the shaderball geometry file
240 @param environment_path: Path to the environment light file
241 @param material_file_path: Path to the material file. If specified will save the material file.
242 @param camera_path: Path to the camera file
243 @return: The final stage with all the elements added
249 mtlx_to_usd = mxcust.MtlxToUsd(self.
logger)
250 stage = mtlx_to_usd.emit(input_usd_path,
False)
253 stage = Usd.Stage.Open(input_usd_path)
254 except Exception
as e:
255 self.
logger.info(f
"> Error: Could not open file at {input_usd_path}. Error: {e}")
256 return stage,
None,
None,
None,
None
259 self.
logger.info(f
"> Error: Could not open file at {input_usd_path}")
260 return stage,
None,
None,
None,
None
265 if material_file_path:
267 self.
logger.info(f
"> Saving MaterialX content to: {material_file_path}")
268 stage.GetRootLayer().documentation = f
"MaterialX content from {input_usd_path}"
269 stage.GetRootLayer().Export(material_file_path)
275 first_material = found_materials[0]
if found_materials
else None
278 SCENE_ROOT =
"/TestScene"
279 GEOM_ROOT =
"/TestScene/Geometry"
280 LIGHTS_ROOT =
"/TestScene/Lights"
281 SKYDOME_LIGHT_NAME =
"EnvironmentLight"
284 test_scene_prim = stage.DefinePrim(SCENE_ROOT,
"Xform")
286 stage.SetDefaultPrim(stage.GetPrimAtPath(SCENE_ROOT))
289 test_geom_prim =
None
292 if test_geom_prim
and first_material:
293 material_binding_api = UsdShade.MaterialBindingAPI.Apply(test_geom_prim)
294 material_binding_api.Bind(UsdShade.Material(first_material))
295 self.
logger.info(f
"> Geometry reference '{shaderball_path} added under: {test_scene_prim.GetPath()}.")
301 dome_light = self.
add_skydome_light(stage, environment_path, LIGHTS_ROOT, SKYDOME_LIGHT_NAME)
303 self.
logger.info(f
"> Light '{environment_path}' added at path: {dome_light.GetPath()}.")
307 camera_prim = self.
add_camera(stage, camera_path)
310 self.
logger.info(f
"> Camera '{camera_path}' added at path: {camera_prim.GetPath()}.")
312 self.
logger.info(f
"> Camera added at path: {camera_prim.GetPath()}.")
314 return stage, found_materials, test_geom_prim, dome_light, camera_prim
318 @brief This function flattens the stage and returns the flattened layer.
319 @param stage: The stage to flatten.
320 @return: The flattened layer.
322 return stage.Flatten()
326 @brief This function saves the flattened stage to a new USD file.
327 @param flattened_layer: The flattened layer to save.
328 @param output_path: The path to save the flattened stage.
330 flatten_path = output_path.replace(
".usda",
"_flattened.usda")
331 flattened_layer.documentation = f
"Flattened USD file for {output_path}"
332 flattened_layer.Export(flatten_path)
337 @brief This function creates a new USDZ package from a flattened layer.
338 @param usdz_file_path: The path to the USDZ package to create.
339 @param flattened_layer: The flattened layer to save to the USDZ package.
340 @return: True if the USDZ package was successfully created, False otherwise.
345 success = UsdUtils.CreateNewUsdzPackage(flattened_layer.identifier, usdz_file_path)
347 error = (
"Failed to create USDZ package.")
348 except Exception
as e:
349 error = (f
"An exception occurred while creating the USDZ file: {e}")
351 return success, error
Class that converts a MaterialX file to a USD file with an appropriate scene.
create_usdz_package(self, str usdz_file_path, flattened_layer)
This function creates a new USDZ package from a flattened layer.
validate_stage(self, str file, bool verboseOutput=False)
This function validates a USD file using the ComplianceChecker.
set_required_validation_attributes(self, stage)
This function sets the required validation attributes for the stage.
find_first_valid_prim(self, stage)
This function finds the first valid prim in root layer of a stage.
__init__(self)
Constructor for the MaterialxUSDConverter class.
get_flattend_layer(self, stage)
This function flattens the stage and returns the flattened layer.
add_skydome_light(self, Usd.Stage stage, str environment_path, str root_path="/TestScene/Lights", str light_name="EnvironmentLight", xform_scale=Gf.Vec3f(1.3, 1.3, 1.3), xform_rotate=Gf.Vec3f(0, 0, 0))
This function adds a skydome light to the stage.
mtlx_to_usd(self, str input_usd_path, str shaderball_path, str environment_path, str material_file_path, str camera_path, use_custom=False)
This function reads the input usd file and adds the shaderball geometry and environment light to the ...
find_materials(self, stage, bool find_first=True)
This function finds the first material in the stage.
find_first_camera(self, Usd.Stage stage)
This function finds the first camera in the stage.
add_camera(self, Usd.Stage stage, str camera_path, str root_path="/TestScene/Camera", str geometry_path="/TestScene/Geometry")
This function adds a camera to the stage.
save_flattened_layer(self, flattened_layer, str output_path)
This function saves the flattened stage to a new USD file.
add_geometry_reference(self, Usd.Stage stage, str geometry_path, str root_path="/TestScene/Geometry")
This function adds a geometry reference to the stage.