scene_export_actor.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. import traceback, sys, uuid, os, json
  9. #
  10. # Example for exporting ActorGroup scene rules
  11. #
  12. def log_exception_traceback():
  13. exc_type, exc_value, exc_tb = sys.exc_info()
  14. data = traceback.format_exception(exc_type, exc_value, exc_tb)
  15. print(str(data))
  16. def get_node_names(sceneGraph, nodeTypeName, testEndPoint = False, validList = None):
  17. import azlmbr.scene.graph
  18. import scene_api.scene_data
  19. node = sceneGraph.get_root()
  20. nodeList = []
  21. children = []
  22. paths = []
  23. while node.IsValid():
  24. # store children to process after siblings
  25. if sceneGraph.has_node_child(node):
  26. children.append(sceneGraph.get_node_child(node))
  27. nodeName = scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node))
  28. paths.append(nodeName.get_path())
  29. include = True
  30. if (validList is not None):
  31. include = False # if a valid list filter provided, assume to not include node name
  32. name_parts = nodeName.get_path().split('.')
  33. for valid in validList:
  34. if (valid in name_parts[-1]):
  35. include = True
  36. break
  37. # store any node that has provides specifc data content
  38. nodeContent = sceneGraph.get_node_content(node)
  39. if include and nodeContent.CastWithTypeName(nodeTypeName):
  40. if testEndPoint is not None:
  41. include = sceneGraph.is_node_end_point(node) is testEndPoint
  42. if include:
  43. if (len(nodeName.get_path())):
  44. nodeList.append(scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node)))
  45. # advance to next node
  46. if sceneGraph.has_node_sibling(node):
  47. node = sceneGraph.get_node_sibling(node)
  48. elif children:
  49. node = children.pop()
  50. else:
  51. node = azlmbr.scene.graph.NodeIndex()
  52. return nodeList, paths
  53. def generate_mesh_group(scene, sceneManifest, meshDataList, paths):
  54. # Compute the name of the scene file
  55. clean_filename = scene.sourceFilename.replace('.', '_')
  56. mesh_group_name = os.path.basename(clean_filename)
  57. # make the mesh group
  58. mesh_group = sceneManifest.add_mesh_group(mesh_group_name)
  59. mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, clean_filename)) + '}'
  60. # add all nodes to this mesh group
  61. for activeMeshIndex in range(len(meshDataList)):
  62. mesh_name = meshDataList[activeMeshIndex]
  63. mesh_path = mesh_name.get_path()
  64. sceneManifest.mesh_group_select_node(mesh_group, mesh_path)
  65. def create_shape_configuration(nodeName):
  66. import scene_api.physics_data
  67. if(nodeName in ['_foot_','_wrist_']):
  68. shapeConfiguration = scene_api.physics_data.BoxShapeConfiguration()
  69. shapeConfiguration.scale = [1.1, 1.1, 1.1]
  70. shapeConfiguration.dimensions = [2.1, 3.1, 4.1]
  71. return shapeConfiguration
  72. else:
  73. shapeConfiguration = scene_api.physics_data.CapsuleShapeConfiguration()
  74. shapeConfiguration.scale = [1.0, 1.0, 1.0]
  75. shapeConfiguration.height = 1.0
  76. shapeConfiguration.radius = 1.0
  77. return shapeConfiguration
  78. def create_collider_configuration(nodeName):
  79. import scene_api.physics_data
  80. colliderConfiguration = scene_api.physics_data.ColliderConfiguration()
  81. colliderConfiguration.Position = [0.1, 0.1, 0.2]
  82. colliderConfiguration.Rotation = [45.0, 35.0, 25.0]
  83. return colliderConfiguration
  84. def generate_physics_nodes(actorPhysicsSetupRule, nodeNameList):
  85. import scene_api.physics_data
  86. hitDetectionConfig = scene_api.physics_data.CharacterColliderConfiguration()
  87. simulatedObjectColliderConfig = scene_api.physics_data.CharacterColliderConfiguration()
  88. clothConfig = scene_api.physics_data.CharacterColliderConfiguration()
  89. ragdollConfig = scene_api.physics_data.RagdollConfiguration()
  90. for nodeName in nodeNameList:
  91. shapeConfiguration = create_shape_configuration(nodeName)
  92. colliderConfiguration = create_collider_configuration(nodeName)
  93. hitDetectionConfig.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
  94. simulatedObjectColliderConfig.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
  95. clothConfig.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
  96. #
  97. ragdollNode = scene_api.physics_data.RagdollNodeConfiguration()
  98. ragdollNode.JointConfig.Name = nodeName
  99. ragdollConfig.add_ragdoll_node_configuration(ragdollNode)
  100. ragdollConfig.colliders.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
  101. actorPhysicsSetupRule.set_simulated_object_collider_config(simulatedObjectColliderConfig)
  102. actorPhysicsSetupRule.set_hit_detection_config(hitDetectionConfig)
  103. actorPhysicsSetupRule.set_cloth_config(clothConfig)
  104. actorPhysicsSetupRule.set_ragdoll_config(ragdollConfig)
  105. def generate_actor_group(scene, sceneManifest, meshDataList, paths):
  106. import scene_api.scene_data
  107. import scene_api.physics_data
  108. import scene_api.actor_group
  109. # fetch bone data
  110. validNames = ['_neck_','_pelvis_','_leg_','_knee_','_spine_','_arm_','_clavicle_','_head_','_elbow_','_wrist_']
  111. graph = scene_api.scene_data.SceneGraph(scene.graph)
  112. nodeList, allNodePaths = get_node_names(graph, 'BoneData', validList = validNames)
  113. nodeNameList = []
  114. for activeMeshIndex, nodeName in enumerate(nodeList):
  115. nodeNameList.append(nodeName.get_name())
  116. # add comment
  117. commentRule = scene_api.actor_group.CommentRule()
  118. commentRule.text = str(nodeNameList)
  119. # ActorPhysicsSetupRule
  120. actorPhysicsSetupRule = scene_api.actor_group.ActorPhysicsSetupRule()
  121. generate_physics_nodes(actorPhysicsSetupRule, nodeNameList)
  122. # add scale of the Actor rule
  123. actorScaleRule = scene_api.actor_group.ActorScaleRule()
  124. actorScaleRule.scaleFactor = 2.0
  125. # add coordinate system rule
  126. coordinateSystemRule = scene_api.actor_group.CoordinateSystemRule()
  127. coordinateSystemRule.useAdvancedData = False
  128. # add morph target rule
  129. morphTargetRule = scene_api.actor_group.MorphTargetRule()
  130. morphTargetRule.targets.select_targets([nodeNameList[0]], nodeNameList)
  131. # add skeleton optimization rule
  132. skeletonOptimizationRule = scene_api.actor_group.SkeletonOptimizationRule()
  133. skeletonOptimizationRule.autoSkeletonLOD = True
  134. skeletonOptimizationRule.criticalBonesList.select_targets([nodeNameList[0:2]], nodeNameList)
  135. # add LOD rule
  136. lodRule = scene_api.actor_group.LodRule()
  137. lodRule0 = lodRule.add_lod_level(0)
  138. lodRule0.select_targets([nodeNameList[1:4]], nodeNameList)
  139. actorGroup = scene_api.actor_group.ActorGroup()
  140. actorGroup.name = os.path.basename(scene.sourceFilename)
  141. actorGroup.add_rule(actorScaleRule)
  142. actorGroup.add_rule(coordinateSystemRule)
  143. actorGroup.add_rule(skeletonOptimizationRule)
  144. actorGroup.add_rule(morphTargetRule)
  145. actorGroup.add_rule(lodRule)
  146. actorGroup.add_rule(actorPhysicsSetupRule)
  147. actorGroup.add_rule(commentRule)
  148. sceneManifest.manifest['values'].append(actorGroup.to_dict())
  149. def update_manifest(scene):
  150. import json, uuid, os
  151. import azlmbr.scene.graph
  152. import scene_api.scene_data
  153. graph = scene_api.scene_data.SceneGraph(scene.graph)
  154. mesh_name_list, all_node_paths = get_node_names(graph, 'MeshData')
  155. scene_manifest = scene_api.scene_data.SceneManifest()
  156. generate_actor_group(scene, scene_manifest, mesh_name_list, all_node_paths)
  157. generate_mesh_group(scene, scene_manifest, mesh_name_list, all_node_paths)
  158. # Convert the manifest to a JSON string and return it
  159. return scene_manifest.export()
  160. sceneJobHandler = None
  161. def on_update_manifest(args):
  162. try:
  163. scene = args[0]
  164. return update_manifest(scene)
  165. except RuntimeError as err:
  166. print (f'ERROR - {err}')
  167. log_exception_traceback()
  168. except:
  169. log_exception_traceback()
  170. finally:
  171. global sceneJobHandler
  172. # do not delete or set sceneJobHandler to None, just disconnect from it.
  173. # this call is occuring while the scene Job Handler itself is in the callstack, so deleting it here
  174. # would cause a crash.
  175. sceneJobHandler.disconnect()
  176. # try to create SceneAPI handler for processing
  177. try:
  178. import azlmbr.scene
  179. sceneJobHandler = azlmbr.scene.ScriptBuildingNotificationBusHandler()
  180. sceneJobHandler.connect()
  181. sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
  182. except:
  183. sceneJobHandler = None