From 5e5bb272192043c18612942d660c1c98c18af583 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 9 Dec 2021 14:53:35 -0600 Subject: [PATCH] Procedural Prefabs: Python documentation cleanup (#6136) * Auto LOD script setup Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Working auto LODs Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Correctly selected LODs and added default prefab Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Cleanup code Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Cleanup code Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add missing legal header, move name cleanup to scene_helpers, add documentation Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add PhysX mesh group support. Updated example script to show usage Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add a physics collider component Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Update DefaultOrValue call Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Document remaining methods in scene_data.py Add enums where appropriate Add type hints and default values Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Remove unused import Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Convert docstring to numpy style Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix return types Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Remove empty returns Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add docs on enums Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../Editor/Scripts/scene_helpers.py | 38 +- .../Editor/Scripts/scene_mesh_to_prefab.py | 8 +- .../Editor/Scripts/scene_api/scene_data.py | 628 ++++++++++++------ 3 files changed, 474 insertions(+), 200 deletions(-) diff --git a/AutomatedTesting/Editor/Scripts/scene_helpers.py b/AutomatedTesting/Editor/Scripts/scene_helpers.py index 761068e796..cae4488abc 100644 --- a/AutomatedTesting/Editor/Scripts/scene_helpers.py +++ b/AutomatedTesting/Editor/Scripts/scene_helpers.py @@ -14,30 +14,44 @@ from scene_api.scene_data import SceneGraphName def log_exception_traceback(): - """ - Outputs an exception stacktrace. - """ + """Outputs an exception stacktrace.""" data = traceback.format_exc() logger = logging.getLogger('python') logger.error(data) -def sanitize_name_for_disk(name: str): - """ - Removes illegal filename characters from a string. +def sanitize_name_for_disk(name: str) -> str: + """Removes illegal filename characters from a string. + + Parameters + ---------- + name : + String to clean. + + + Returns + ------- + str + Name with illegal characters removed. - :param name: String to clean. - :return: Name with illegal characters removed. """ return "".join(char for char in name if char not in "|<>:\"/?*\\") def get_mesh_node_names(scene_graph: sceneData.SceneGraph) -> Tuple[List[SceneGraphName], List[str]]: - """ - Returns a tuple of all the mesh nodes as well as all the node paths + """Returns a tuple of all the mesh nodes as well as all the node paths + + Parameters + ---------- + scene_graph : + Scene graph to search + + + Returns + ------- + Tuple[List[SceneGraphName], List[str]] + Tuple of [Mesh Nodes, All Node Paths] - :param scene_graph: Scene graph to search - :return: Tuple of [Mesh Nodes, All Node Paths] """ import azlmbr.scene as sceneApi import azlmbr.scene.graph diff --git a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py index db8df09091..f9c1e08558 100644 --- a/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py +++ b/AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py @@ -8,7 +8,7 @@ import azlmbr.bus import azlmbr.math -from scene_api.scene_data import PrimitiveShape, DecompositionMode +from scene_api.scene_data import PrimitiveShape, DecompositionMode, ColorChannel, TangentSpaceSource, TangentSpaceMethod from scene_helpers import * @@ -71,6 +71,7 @@ def add_physx_meshes(scene_manifest: sceneData.SceneManifest, source_file_name: triangle = scene_manifest.add_physx_triangle_mesh_group(source_file_name + "_triangle", False, True, True, True, True, True) scene_manifest.physx_mesh_group_add_selected_unselected_nodes(triangle, [first_mesh], all_except_first_mesh) + def update_manifest(scene): import uuid, os import azlmbr.scene.graph @@ -114,10 +115,11 @@ def update_manifest(scene): if node != mesh_path: scene_manifest.mesh_group_unselect_node(mesh_group, node) - scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", 1, "Col0", 2, "Col0", 2, 3) + scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", ColorChannel.GREEN, "Col0", + ColorChannel.BLUE, "Col0", ColorChannel.BLUE, ColorChannel.ALPHA) scene_manifest.mesh_group_add_advanced_mesh_rule(mesh_group, True, False, True, "Col0") scene_manifest.mesh_group_add_skin_rule(mesh_group, 3, 0.002) - scene_manifest.mesh_group_add_tangent_rule(mesh_group, 1, 0) + scene_manifest.mesh_group_add_tangent_rule(mesh_group, TangentSpaceSource.MIKKT_GENERATION, TangentSpaceMethod.TSPACE_BASIC) # Create an editor entity entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name) diff --git a/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py b/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py index 173558d784..66b836f171 100755 --- a/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py +++ b/Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py @@ -7,13 +7,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT import typing import json import azlmbr.scene as sceneApi -from enum import Enum, IntEnum +from enum import IntEnum # Wraps the AZ.SceneAPI.Containers.SceneGraph.NodeIndex internal class class SceneGraphNodeIndex: - def __init__(self, sceneGraphNodeIndex) -> None: - self.nodeIndex = sceneGraphNodeIndex + def __init__(self, scene_graph_node_index) -> None: + self.nodeIndex = scene_graph_node_index def as_number(self): return self.nodeIndex.AsNumber() @@ -29,9 +29,9 @@ class SceneGraphNodeIndex: # Wraps AZ.SceneAPI.Containers.SceneGraph.Name internal class -class SceneGraphName(): - def __init__(self, sceneGraphName) -> None: - self.name = sceneGraphName +class SceneGraphName: + def __init__(self, scene_graph_name) -> None: + self.name = scene_graph_name def get_path(self) -> str: return self.name.GetPath() @@ -41,9 +41,9 @@ class SceneGraphName(): # Wraps AZ.SceneAPI.Containers.SceneGraph class -class SceneGraph(): - def __init__(self, sceneGraphInstance) -> None: - self.sceneGraph = sceneGraphInstance +class SceneGraph: + def __init__(self, scene_graph_instance) -> None: + self.sceneGraph = scene_graph_instance @classmethod def is_valid_name(cls, name): @@ -96,53 +96,161 @@ class SceneGraph(): return self.sceneGraph.GetNodeContent(node) +class ColorChannel(IntEnum): + RED = 0 + """ Red color channel """ + GREEN = 1 + """ Green color channel """ + BLUE = 2 + """ Blue color channel """ + ALPHA = 3 + """ Alpha color channel """ + + +class TangentSpaceSource(IntEnum): + SCENE = 0 + """ Extract the tangents and bitangents directly from the source scene file. """ + MIKKT_GENERATION = 1 + """ Use MikkT algorithm to generate tangents """ + + +class TangentSpaceMethod(IntEnum): + TSPACE = 0 + """ Generates the tangents and bitangents with their true magnitudes which can be used for relief mapping effects. + It calculates the 'real' bitangent which may not be perpendicular to the tangent. + However, both, the tangent and bitangent are perpendicular to the vertex normal. + """ + TSPACE_BASIC = 1 + """ Calculates unit vector tangents and bitangents at pixel/vertex level which are sufficient for basic normal mapping. """ + + class PrimitiveShape(IntEnum): BEST_FIT = 0 + """ The algorithm will determine which of the shapes fits best. """ SPHERE = 1 + """ Sphere shape """ BOX = 2 + """ Box shape """ CAPSULE = 3 + """ Capsule shape """ class DecompositionMode(IntEnum): VOXEL = 0 + """ Voxel-based approximate convex decomposition """ TETRAHEDRON = 1 + """ Tetrahedron-based approximate convex decomposition """ # Contains a dictionary to contain and export AZ.SceneAPI.Containers.SceneManifest -class SceneManifest(): +class SceneManifest: def __init__(self): self.manifest = {'values': []} def add_mesh_group(self, name: str) -> dict: - meshGroup = {} - meshGroup['$type'] = '{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup' - meshGroup['name'] = name - meshGroup['nodeSelectionList'] = {'selectedNodes': [], 'unselectedNodes': []} - meshGroup['rules'] = {'rules': [{'$type': 'MaterialRule'}]} - self.manifest['values'].append(meshGroup) - return meshGroup + """Adds a Mesh Group to the scene manifest. + + Parameters + ---------- + name : + Name of the mesh group. This will become a file on disk and be usable as a Mesh in the editor. + + + Returns + ------- + dict + Newly created mesh group. + + """ + mesh_group = { + '$type': '{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup', + 'name': name, + 'nodeSelectionList': {'selectedNodes': [], 'unselectedNodes': []}, + 'rules': {'rules': [{'$type': 'MaterialRule'}]} + } + self.manifest['values'].append(mesh_group) + return mesh_group def add_prefab_group(self, name: str, id: str, json: dict) -> dict: - prefabGroup = {} - prefabGroup['$type'] = '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup' - prefabGroup['name'] = name - prefabGroup['id'] = id - prefabGroup['prefabDomData'] = json - self.manifest['values'].append(prefabGroup) - return prefabGroup + """Adds a Prefab Group to the scene manifest. This will become a file on disk and be usable as a ProceduralPrefab in the editor. + + Parameters + ---------- + name : + Name of the prefab. + id : + Unique ID for this prefab group. + json : + The prefab template data. + + + Returns + ------- + dict + The newly created Prefab group + + """ + prefab_group = { + '$type': '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup', + 'name': name, + 'id': id, + 'prefabDomData': json + } + self.manifest['values'].append(prefab_group) + return prefab_group def mesh_group_select_node(self, mesh_group: dict, node_name: str) -> None: + """Adds a node as a selected node. + + Parameters + ---------- + mesh_group : + Mesh group to apply the selection to. + node_name : + Path of the node. + + """ mesh_group['nodeSelectionList']['selectedNodes'].append(node_name) def mesh_group_unselect_node(self, mesh_group: dict, node_name: str) -> None: + """Adds a node as an unselected node. + + Parameters + ---------- + mesh_group : + Mesh group to apply the selection to. + node_name : + Path of the node. + + """ mesh_group['nodeSelectionList']['unselectedNodes'].append(node_name) - def mesh_group_add_advanced_coordinate_system(self, mesh_group: dict, origin_node_name: str, translation: object, - rotation: object, scale: float) -> None: + def mesh_group_add_advanced_coordinate_system(self, mesh_group: dict, + origin_node_name: str = '', + translation: typing.Optional[object] = None, + rotation: typing.Optional[object] = None, + scale: float = 1.0) -> None: + """Adds an Advanced Coordinate System rule which modifies the target coordinate system, + applying a transformation to all data (transforms and vertex data if it exists). + + Parameters + ---------- + mesh_group : + Mesh group to add the Advanced Coordinate System rule to. + origin_node_name : + Path of the node to use as the origin. + translation : + Moves the group along the given vector. + rotation : + Sets the orientation offset of the processed mesh in degrees. Rotates the group after translation. + scale : + Sets the scale offset of the processed mesh. + + """ origin_rule = { '$type': 'CoordinateSystemRule', 'useAdvancedData': True, - 'originNodeName': '' if origin_node_name is None else origin_node_name + 'originNodeName': self.__default_or_value(origin_node_name, '') } if translation is not None: origin_rule['translation'] = translation @@ -153,31 +261,57 @@ class SceneManifest(): mesh_group['rules']['rules'].append(origin_rule) def mesh_group_add_comment(self, mesh_group: dict, comment: str) -> None: - commentRule = { + """Adds a Comment rule. + + Parameters + ---------- + mesh_group : + Mesh group to add the comment rule to. + comment : + Text for the comment rule. + + """ + comment_rule = { '$type': 'CommentRule', 'comment': comment } - mesh_group['rules']['rules'].append(commentRule) + mesh_group['rules']['rules'].append(comment_rule) def __default_or_value(self, val, default): return default if val is None else val - def mesh_group_add_cloth_rule(self, mesh_group: dict, cloth_node_name: str, - inverse_masses_stream_name: str, inverse_masses_channel: int, - motion_constraints_stream_name: str, motion_constraints_channel: int, - backstop_stream_name: str, backstop_offset_channel: int, - backstop_radius_channel: int) -> None: - """ - Adds a Cloth rule. 0 = Red, 1 = Green, 2 = Blue, 3 = Alpha - :param mesh_group: Mesh Group to add the cloth rule to - :param cloth_node_name: Name of the node that the rule applies to - :param inverse_masses_stream_name: Name of the color stream to use for inverse masses - :param inverse_masses_channel: Color channel (index) for inverse masses - :param motion_constraints_stream_name: Name of the color stream to use for motion constraints - :param motion_constraints_channel: Color channel (index) for motion constraints - :param backstop_stream_name: Name of the color stream to use for backstop - :param backstop_offset_channel: Color channel (index) for backstop offset value - :param backstop_radius_channel: Color chnanel (index) for backstop radius value + def mesh_group_add_cloth_rule(self, mesh_group: dict, + cloth_node_name: str, + inverse_masses_stream_name: typing.Optional[str], + inverse_masses_channel: typing.Optional[ColorChannel], + motion_constraints_stream_name: typing.Optional[str], + motion_constraints_channel: typing.Optional[ColorChannel], + backstop_stream_name: typing.Optional[str], + backstop_offset_channel: typing.Optional[ColorChannel], + backstop_radius_channel: typing.Optional[ColorChannel]) -> None: + """Adds a Cloth rule. + + Parameters + ---------- + mesh_group : + Mesh Group to add the cloth rule to + cloth_node_name : + Name of the node that the rule applies to + inverse_masses_stream_name : + Name of the color stream to use for inverse masses + inverse_masses_channel : + Color channel (index) for inverse masses + motion_constraints_stream_name : + Name of the color stream to use for motion constraints + motion_constraints_channel : + Color channel (index) for motion constraints + backstop_stream_name : + Name of the color stream to use for backstop + backstop_offset_channel : + Color channel (index) for backstop offset value + backstop_radius_channel : + Color channel (index) for backstop radius value + """ cloth_rule = { '$type': 'ClothRule', @@ -186,22 +320,31 @@ class SceneManifest(): } if inverse_masses_channel is not None: - cloth_rule['inverseMassesChannel'] = inverse_masses_channel + cloth_rule['inverseMassesChannel'] = int(inverse_masses_channel) cloth_rule['motionConstraintsStreamName'] = self.__default_or_value(motion_constraints_stream_name, 'Default: 1.0') if motion_constraints_channel is not None: - cloth_rule['motionConstraintsChannel'] = motion_constraints_channel + cloth_rule['motionConstraintsChannel'] = int(motion_constraints_channel) cloth_rule['backstopStreamName'] = self.__default_or_value(backstop_stream_name, 'None') if backstop_offset_channel is not None: - cloth_rule['backstopOffsetChannel'] = backstop_offset_channel + cloth_rule['backstopOffsetChannel'] = int(backstop_offset_channel) if backstop_radius_channel is not None: - cloth_rule['backstopRadiusChannel'] = backstop_radius_channel + cloth_rule['backstopRadiusChannel'] = int(backstop_radius_channel) mesh_group['rules']['rules'].append(cloth_rule) def mesh_group_add_lod_rule(self, mesh_group: dict) -> dict: - """ - Adds an LOD rule - :param mesh_group: Mesh Group to add the rule to - :return: LOD rule + """Adds an LOD rule. + + Parameters + ---------- + mesh_group : + Mesh Group to add the rule to. + + + Returns + ------- + dict + LOD rule. + """ lod_rule = { '$type': '{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule', @@ -212,47 +355,76 @@ class SceneManifest(): return lod_rule def lod_rule_add_lod(self, lod_rule: dict) -> dict: - """ - Adds an LOD level to the LOD rule. Nodes are added in order. The first node added represents LOD1, 2nd LOD2, etc - :param lod_rule: LOD rule to add the LOD level to - :return: LOD level + """Adds an LOD level to the LOD rule. Nodes are added in order. The first node added represents LOD1, 2nd LOD2, etc. + + Parameters + ---------- + lod_rule : + LOD rule to add the LOD level to. + + + Returns + ------- + dict + LOD level. + """ lod = {'selectedNodes': [], 'unselectedNodes': []} lod_rule['nodeSelectionList'].append(lod) return lod def lod_select_node(self, lod: dict, selected_node: str) -> None: - """ - Adds a node as a selected node - :param lod: LOD level to add the node to - :param selected_node: Path of the node + """Adds a node as a selected node. + + Parameters + ---------- + lod : + LOD level to add the node to. + selected_node : + Path of the node. + """ lod['selectedNodes'].append(selected_node) def lod_unselect_node(self, lod: dict, unselected_node: str) -> None: - """ - Adds a node as an unselected node - :param lod: LOD rule to add the node to - :param unselected_node: Path of the node + """Adds a node as an unselected node. + + Parameters + ---------- + lod : + LOD rule to add the node to. + unselected_node : + Path of the node. + """ lod['unselectedNodes'].append(unselected_node) - def mesh_group_add_advanced_mesh_rule(self, mesh_group: dict, use_32bit_vertices: bool, merge_meshes: bool, - use_custom_normals: bool, - vertex_color_stream: str) -> None: - """ - Adds an Advanced Mesh rule - :param mesh_group: Mesh Group to add the rule to - :param use_32bit_vertices: False = 16bit vertex position precision. True = 32bit vertex position precision - :param merge_meshes: Merge all meshes into a single mesh - :param use_custom_normals: True = use normals from DCC tool. False = average normals - :param vertex_color_stream: Color stream name to use for Vertex Coloring + def mesh_group_add_advanced_mesh_rule(self, mesh_group: dict, + use_32bit_vertices: bool = False, + merge_meshes: bool = True, + use_custom_normals: bool = True, + vertex_color_stream: typing.Optional[str] = None) -> None: + """Adds an Advanced Mesh rule. + + Parameters + ---------- + mesh_group : + Mesh Group to add the rule to. + use_32bit_vertices : + False = 16bit vertex position precision. True = 32bit vertex position precision. + merge_meshes : + Merge all meshes into a single mesh. + use_custom_normals : + True = use normals from DCC tool. False = average normals. + vertex_color_stream : + Color stream name to use for Vertex Coloring. + """ rule = { '$type': 'StaticMeshAdvancedRule', - 'use32bitVertices': self.__default_or_value(use_32bit_vertices, False), - 'mergeMeshes': self.__default_or_value(merge_meshes, True), - 'useCustomNormals': self.__default_or_value(use_custom_normals, True) + 'use32bitVertices': use_32bit_vertices, + 'mergeMeshes': merge_meshes, + 'useCustomNormals': use_custom_normals } if vertex_color_stream is not None: @@ -260,37 +432,51 @@ class SceneManifest(): mesh_group['rules']['rules'].append(rule) - def mesh_group_add_skin_rule(self, mesh_group: dict, max_weights_per_vertex: int, weight_threshold: float) -> None: - """ - Adds a Skin rule - :param mesh_group: Mesh Group to add the rule to - :param max_weights_per_vertex: Max number of joints that can influence a vertex - :param weight_threshold: Weight values below this value will be treated as 0 + def mesh_group_add_skin_rule(self, mesh_group: dict, max_weights_per_vertex: int = 4, weight_threshold: float = 0.001) -> None: + """Adds a Skin rule. + + Parameters + ---------- + mesh_group : + Mesh Group to add the rule to. + max_weights_per_vertex : + Max number of joints that can influence a vertex. + weight_threshold : + Weight values below this value will be treated as 0. + """ rule = { '$type': 'SkinRule', - 'maxWeightsPerVertex': self.__default_or_value(max_weights_per_vertex, 4), - 'weightThreshold': self.__default_or_value(weight_threshold, 0.001) + 'maxWeightsPerVertex': max_weights_per_vertex, + 'weightThreshold': weight_threshold } mesh_group['rules']['rules'].append(rule) - def mesh_group_add_tangent_rule(self, mesh_group: dict, tangent_space: int, tspace_method: int) -> None: - """ - Adds a Tangent rule to control tangent space generation - :param mesh_group: Mesh Group to add the rule to - :param tangent_space: Tangent space source. 0 = Scene, 1 = MikkT Tangent Generation - :param tspace_method: MikkT Generation method. 0 = TSpace, 1 = TSpaceBasic + def mesh_group_add_tangent_rule(self, mesh_group: dict, + tangent_space: TangentSpaceSource = TangentSpaceSource.SCENE, + tspace_method: TangentSpaceMethod = TangentSpaceMethod.TSPACE) -> None: + """Adds a Tangent rule to control tangent space generation. + + Parameters + ---------- + mesh_group : + Mesh Group to add the rule to. + tangent_space : + Tangent space source. 0 = Scene, 1 = MikkT Tangent Generation. + tspace_method : + MikkT Generation method. 0 = TSpace, 1 = TSpaceBasic. + """ rule = { '$type': 'TangentsRule', - 'tangentSpace': self.__default_or_value(tangent_space, 1), - 'tSpaceMethod': self.__default_or_value(tspace_method, 0) + 'tangentSpace': int(tangent_space), + 'tSpaceMethod': int(tspace_method) } mesh_group['rules']['rules'].append(rule) - def __add_physx_base_mesh_group(self, name: str, physics_material: typing.Optional[str]) -> dict: + def __add_physx_base_mesh_group(self, name: str, physics_material: typing.Optional[str] = None) -> dict: import azlmbr.math group = { '$type': '{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup', @@ -314,7 +500,9 @@ class SceneManifest(): return group - def add_physx_triangle_mesh_group(self, name: str, merge_meshes: bool = True, weld_vertices: bool = False, + def add_physx_triangle_mesh_group(self, name: str, + merge_meshes: bool = True, + weld_vertices: bool = False, disable_clean_mesh: bool = False, force_32bit_indices: bool = False, suppress_triangle_mesh_remap_table: bool = False, @@ -322,26 +510,42 @@ class SceneManifest(): mesh_weld_tolerance: float = 0.0, num_tris_per_leaf: int = 4, physics_material: typing.Optional[str] = None) -> dict: - """ - Adds a Triangle type PhysX Mesh Group to the scene. + """Adds a Triangle type PhysX Mesh Group to the scene. + + Parameters + ---------- + name : + Name of the mesh group. + merge_meshes : + When true, all selected nodes will be merged into a single collision mesh. + weld_vertices : + When true, mesh welding is performed. Clean mesh must be enabled. + disable_clean_mesh : + When true, mesh cleaning is disabled. This makes cooking faster. + force_32bit_indices : + When true, 32-bit indices will always be created regardless of triangle count. + suppress_triangle_mesh_remap_table : + When true, the face remap table is not created. + This saves a significant amount of memory, but the SDK will not be able to provide the remap + information for internal mesh triangles returned by collisions, sweeps or raycasts hits. + build_triangle_adjacencies : + When true, the triangle adjacency information is created. + mesh_weld_tolerance : + If mesh welding is enabled, this controls the distance at + which vertices are welded. If mesh welding is not enabled, this value defines the + acceptance distance for mesh validation. Provided no two vertices are within this + distance, the mesh is considered to be clean. If not, a warning will be emitted. + num_tris_per_leaf : + Mesh cooking hint for max triangles per leaf limit. Fewer triangles per leaf + produces larger meshes with better runtime performance and worse cooking performance. + physics_material : + Configure which physics material to use. + + Returns + ------- + dict + The newly created mesh group. - :param name: Name of the mesh group. - :param merge_meshes: When true, all selected nodes will be merged into a single collision mesh. - :param weld_vertices: When true, mesh welding is performed. Clean mesh must be enabled. - :param disable_clean_mesh: When true, mesh cleaning is disabled. This makes cooking faster. - :param force_32bit_indices: When true, 32-bit indices will always be created regardless of triangle count. - :param suppress_triangle_mesh_remap_table: When true, the face remap table is not created. - This saves a significant amount of memory, but the SDK will not be able to provide the remap - information for internal mesh triangles returned by collisions, sweeps or raycasts hits. - :param build_triangle_adjacencies: When true, the triangle adjacency information is created. - :param mesh_weld_tolerance: If mesh welding is enabled, this controls the distance at - which vertices are welded. If mesh welding is not enabled, this value defines the - acceptance distance for mesh validation. Provided no two vertices are within this - distance, the mesh is considered to be clean. If not, a warning will be emitted. - :param num_tris_per_leaf: Mesh cooking hint for max triangles per leaf limit. Fewer triangles per leaf - produces larger meshes with better runtime performance and worse cooking performance. - :param physics_material: Configure which physics material to use. - :return: The newly created mesh group. """ group = self.__add_physx_base_mesh_group(name, physics_material) group["export method"] = 0 @@ -367,32 +571,49 @@ class SceneManifest(): gauss_map_limit: int = 32, build_gpu_data: bool = False, physics_material: typing.Optional[str] = None) -> dict: - """ - Adds a Convex type PhysX Mesh Group to the scene. + """Adds a Convex type PhysX Mesh Group to the scene. + + Parameters + ---------- + name : + Name of the mesh group. + area_test_epsilon : + If the area of a triangle of the hull is below this value, the triangle will be + rejected. This test is done only if Check Zero Area Triangles is used. + plane_tolerance : + The value is used during hull construction. When a new point is about to be added + to the hull it gets dropped when the point is closer to the hull than the planeTolerance. + use_16bit_indices : + Denotes the use of 16-bit vertex indices in Convex triangles or polygons. + check_zero_area_triangles : + Checks and removes almost zero-area triangles during convex hull computation. + The rejected area size is specified in Area Test Epsilon. + quantize_input : + Quantizes the input vertices using the k-means clustering. + use_plane_shifting : + Enables plane shifting vertex limit algorithm. Plane shifting is an alternative + algorithm for the case when the computed hull has more vertices than the specified vertex + limit. + shift_vertices : + Convex hull input vertices are shifted to be around origin to provide better + computation stability + gauss_map_limit : + Vertex limit beyond which additional acceleration structures are computed for each + convex mesh. Increase that limit to reduce memory usage. Computing the extra structures + all the time does not guarantee optimal performance. + build_gpu_data : + When true, additional information required for GPU-accelerated rigid body + simulation is created. This can increase memory usage and cooking times for convex meshes + and triangle meshes. Convex hulls are created with respect to GPU simulation limitations. + Vertex limit is set to 64 and vertex limit per face is internally set to 32. + physics_material : + Configure which physics material to use. + + Returns + ------- + dict + The newly created mesh group. - :param name: Name of the mesh group. - :param area_test_epsilon: If the area of a triangle of the hull is below this value, the triangle will be - rejected. This test is done only if Check Zero Area Triangles is used. - :param plane_tolerance: The value is used during hull construction. When a new point is about to be added - to the hull it gets dropped when the point is closer to the hull than the planeTolerance. - :param use_16bit_indices: Denotes the use of 16-bit vertex indices in Convex triangles or polygons. - :param check_zero_area_triangles: Checks and removes almost zero-area triangles during convex hull computation. - The rejected area size is specified in Area Test Epsilon. - :param quantize_input: Quantizes the input vertices using the k-means clustering. - :param use_plane_shifting: Enables plane shifting vertex limit algorithm. Plane shifting is an alternative - algorithm for the case when the computed hull has more vertices than the specified vertex - limit. - :param shift_vertices: Convex hull input vertices are shifted to be around origin to provide better - computation stability - :param gauss_map_limit: Vertex limit beyond which additional acceleration structures are computed for each - convex mesh. Increase that limit to reduce memory usage. Computing the extra structures - all the time does not guarantee optimal performance. - :param build_gpu_data: When true, additional information required for GPU-accelerated rigid body - simulation is created. This can increase memory usage and cooking times for convex meshes - and triangle meshes. Convex hulls are created with respect to GPU simulation limitations. - Vertex limit is set to 64 and vertex limit per face is internally set to 32. - :param physics_material: Configure which physics material to use. - :return: The newly created mesh group. """ group = self.__add_physx_base_mesh_group(name, physics_material) group["export method"] = 1 @@ -414,17 +635,27 @@ class SceneManifest(): primitive_shape_target: PrimitiveShape = PrimitiveShape.BEST_FIT, volume_term_coefficient: float = 0.0, physics_material: typing.Optional[str] = None) -> dict: - """ - Adds a Primitive Shape type PhysX Mesh Group to the scene + """Adds a Primitive Shape type PhysX Mesh Group to the scene + + Parameters + ---------- + name : + Name of the mesh group. + primitive_shape_target : + The shape that should be fitted to this mesh. If BEST_FIT is selected, the + algorithm will determine which of the shapes fits best. + volume_term_coefficient : + This parameter controls how aggressively the primitive fitting algorithm will try + to minimize the volume of the fitted primitive. A value of 0 (no volume minimization) is + recommended for most meshes, especially those with moderate to high vertex counts. + physics_material : + Configure which physics material to use. + + Returns + ------- + dict + The newly created mesh group. - :param name: Name of the mesh group. - :param primitive_shape_target: The shape that should be fitted to this mesh. If BEST_FIT is selected, the - algorithm will determine which of the shapes fits best. - :param volume_term_coefficient: This parameter controls how aggressively the primitive fitting algorithm will try - to minimize the volume of the fitted primitive. A value of 0 (no volume minimization) is - recommended for most meshes, especially those with moderate to high vertex counts. - :param physics_material: Configure which physics material to use. - :return: The newly created mesh group. """ group = self.__add_physx_base_mesh_group(name, physics_material) group["export method"] = 2 @@ -447,26 +678,40 @@ class SceneManifest(): convex_hull_downsampling: int = 4, pca: bool = False, project_hull_vertices: bool = True) -> None: - """ - Enables and configures mesh decomposition for a PhysX Mesh Group. + """Enables and configures mesh decomposition for a PhysX Mesh Group. Only valid for convex or primitive mesh types. - :param mesh_group: Mesh group to configure decomposition for. - :param max_convex_hulls: Controls the maximum number of hulls to generate. - :param max_num_vertices_per_convex_hull: Controls the maximum number of triangles per convex hull. - :param concavity: Maximum concavity of each approximate convex hull. - :param resolution: Maximum number of voxels generated during the voxelization stage. - :param mode: Select voxel-based approximate convex decomposition or tetrahedron-based - approximate convex decomposition. - :param alpha: Controls the bias toward clipping along symmetry planes. - :param beta: Controls the bias toward clipping along revolution axes. - :param min_volume_per_convex_hull: Controls the adaptive sampling of the generated convex hulls. - :param plane_downsampling: Controls the granularity of the search for the best clipping plane. - :param convex_hull_downsampling: Controls the precision of the convex hull generation process - during the clipping plane selection stage. - :param pca: Enable or disable normalizing the mesh before applying the convex decomposition. - :param project_hull_vertices: Project the output convex hull vertices onto the original source mesh to increase - the floating point accuracy of the results. + Parameters + ---------- + mesh_group : + Mesh group to configure decomposition for. + max_convex_hulls : + Controls the maximum number of hulls to generate. + max_num_vertices_per_convex_hull : + Controls the maximum number of triangles per convex hull. + concavity : + Maximum concavity of each approximate convex hull. + resolution : + Maximum number of voxels generated during the voxelization stage. + mode : + Select voxel-based approximate convex decomposition or tetrahedron-based + approximate convex decomposition. + alpha : + Controls the bias toward clipping along symmetry planes. + beta : + Controls the bias toward clipping along revolution axes. + min_volume_per_convex_hull : + Controls the adaptive sampling of the generated convex hulls. + plane_downsampling : + Controls the granularity of the search for the best clipping plane. + convex_hull_downsampling : + Controls the precision of the convex hull generation process + during the clipping plane selection stage. + pca : + Enable or disable normalizing the mesh before applying the convex decomposition. + project_hull_vertices : + Project the output convex hull vertices onto the original source mesh to increase + the floating point accuracy of the results. """ mesh_group['DecomposeMeshes'] = True mesh_group['ConvexDecompositionParams'] = { @@ -485,41 +730,54 @@ class SceneManifest(): } def physx_mesh_group_add_selected_node(self, mesh_group: dict, node: str) -> None: - """ - Adds a node to the selected nodes list + """Adds a node to the selected nodes list - :param mesh_group: Mesh group to add to. - :param node: Node path to add. + Parameters + ---------- + mesh_group : + Mesh group to add to. + node : + Node path to add. """ mesh_group['NodeSelectionList']['selectedNodes'].append(node) def physx_mesh_group_add_unselected_node(self, mesh_group: dict, node: str) -> None: - """ - Adds a node to the unselected nodes list + """Adds a node to the unselected nodes list - :param mesh_group: Mesh group to add to. - :param node: Node path to add. + Parameters + ---------- + mesh_group : + Mesh group to add to. + node : + Node path to add. """ mesh_group['NodeSelectionList']['unselectedNodes'].append(node) def physx_mesh_group_add_selected_unselected_nodes(self, mesh_group: dict, selected: typing.List[str], unselected: typing.List[str]) -> None: - """ - Adds a set of nodes to the selected/unselected node lists + """Adds a set of nodes to the selected/unselected node lists - :param mesh_group: Mesh group to add to. - :param selected: List of node paths to add to the selected list. - :param unselected: List of node paths to add to the unselected list. + Parameters + ---------- + mesh_group : + Mesh group to add to. + selected : + List of node paths to add to the selected list. + unselected : + List of node paths to add to the unselected list. """ mesh_group['NodeSelectionList']['selectedNodes'].extend(selected) mesh_group['NodeSelectionList']['unselectedNodes'].extend(unselected) def physx_mesh_group_add_comment(self, mesh_group: dict, comment: str) -> None: - """ - Adds a comment rule + """Adds a comment rule - :param mesh_group: Mesh group to add the rule to. - :param comment: Comment string. + Parameters + ---------- + mesh_group : + Mesh group to add the rule to. + comment : + Comment string. """ rule = { "$type": "CommentRule",