{lyn2265} adding a Python API for MotionGroup rules (#6215)

* {lyn2265} adding a Python API for MotionGroup rules

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* updating the license headers
added more proper example code for motion group


Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>

* updating the actor group to use the common rules module

Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
monroegm-disable-blank-issue-2
Allen Jackson 4 years ago committed by GitHub
parent 8f134a82d1
commit 0ff94f4e6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c1d04d687bdce69965965c290c907b3f9a730a7ea73874b734e1c7ff41426d9
size 3832768

@ -0,0 +1,66 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import traceback, sys, uuid, os, json
import scene_export_utils
import scene_api.motion_group
#
# Example for exporting MotionGroup scene rules
#
def update_manifest(scene):
import azlmbr.scene.graph
import scene_api.scene_data
# create a SceneManifest
sceneManifest = scene_api.scene_data.SceneManifest()
# create a MotionGroup
motionGroup = scene_api.motion_group.MotionGroup()
motionGroup.name = os.path.basename(scene.sourceFilename.replace('.', '_'))
motionAdditiveRule = scene_api.motion_group.MotionAdditiveRule()
motionAdditiveRule.sampleFrame = 2
motionGroup.add_rule(motionAdditiveRule)
motionScaleRule = motionGroup.create_rule(scene_api.motion_group.MotionScaleRule())
motionScaleRule.scaleFactor = 1.1
motionGroup.add_rule(motionScaleRule)
# add motion group to scene manifest
sceneManifest.add_motion_group(motionGroup)
# Convert the manifest to a JSON string and return it
return sceneManifest.export()
sceneJobHandler = None
def on_update_manifest(args):
try:
scene = args[0]
return update_manifest(scene)
except RuntimeError as err:
print (f'ERROR - {err}')
scene_export_utils.log_exception_traceback()
except:
scene_export_utils.log_exception_traceback()
global sceneJobHandler
sceneJobHandler.disconnect()
sceneJobHandler = None
# try to create SceneAPI handler for processing
try:
import azlmbr.scene
sceneJobHandler = azlmbr.scene.ScriptBuildingNotificationBusHandler()
sceneJobHandler.connect()
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
except:
sceneJobHandler = None

@ -0,0 +1,8 @@
{
"values": [
{
"$type": "ScriptProcessorRule",
"scriptFilename": "Assets/TestAnim/scene_export_motion.py"
}
]
}

@ -0,0 +1,63 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import traceback, sys, uuid, os, json
#
# Utility methods for processing scenes
#
def log_exception_traceback():
exc_type, exc_value, exc_tb = sys.exc_info()
data = traceback.format_exception(exc_type, exc_value, exc_tb)
print(str(data))
def get_node_names(sceneGraph, nodeTypeName, testEndPoint = False, validList = None):
import azlmbr.scene.graph
import scene_api.scene_data
node = sceneGraph.get_root()
nodeList = []
children = []
paths = []
while node.IsValid():
# store children to process after siblings
if sceneGraph.has_node_child(node):
children.append(sceneGraph.get_node_child(node))
nodeName = scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node))
paths.append(nodeName.get_path())
include = True
if (validList is not None):
include = False # if a valid list filter provided, assume to not include node name
name_parts = nodeName.get_path().split('.')
for valid in validList:
if (valid in name_parts[-1]):
include = True
break
# store any node that has provides specifc data content
nodeContent = sceneGraph.get_node_content(node)
if include and nodeContent.CastWithTypeName(nodeTypeName):
if testEndPoint is not None:
include = sceneGraph.is_node_end_point(node) is testEndPoint
if include:
if (len(nodeName.get_path())):
nodeList.append(scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node)))
# advance to next node
if sceneGraph.has_node_sibling(node):
node = sceneGraph.get_node_sibling(node)
elif children:
node = children.pop()
else:
node = azlmbr.scene.graph.NodeIndex()
return nodeList, paths

@ -1,10 +1,10 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import traceback, logging, json
from typing import Tuple, List

@ -1,6 +1,7 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#

@ -1,7 +1,7 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#

@ -1,6 +1,7 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#

@ -1,16 +1,15 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import json
import uuid
import os, sys
sys.path.append(os.path.dirname(__file__))
import physics_data
import scene_api.physics_data
from scene_api.common_rules import RuleEncoder, BaseRule, SceneNodeSelectionList, CommentRule, CoordinateSystemRule
class ActorGroup():
"""
@ -18,10 +17,10 @@ class ActorGroup():
Attributes
----------
name:
name:
Name for the group. This name will also be used as the name for the generated file.
selectedRootBone:
selectedRootBone:
The root bone of the animation that will be exported.
rules: `list` of actor rules (derived from BaseRule)
@ -86,121 +85,15 @@ class ActorGroup():
out['rules'] = ruleList
return out
def to_json(self) -> any:
def to_json(self, i = 0) -> any:
jsonDOM = self.to_dict()
return json.dumps(jsonDOM, cls=RuleEncoder, indent=1)
class RuleEncoder(json.JSONEncoder):
"""
A helper class to encode the Python classes with to a Python dictionary
Methods
-------
encode(obj)
Converts contents to a Python dictionary for the JSONEncoder
"""
def encode(self, obj):
chunk = obj
if isinstance(obj, BaseRule):
chunk = obj.to_dict()
elif isinstance(obj, dict):
chunk = obj
elif hasattr(obj, 'to_dict'):
chunk = obj.to_dict()
else:
chunk = obj.__dict__
return super().encode(chunk)
class BaseRule():
"""
Base class of the actor rules to help encode the type name of abstract rules
Parameters
----------
typename : str
A typename the $type will be in the JSON chunk object
Attributes
----------
typename: str
The type name of the abstract classes to be serialized
id: UUID
a unique ID for the rule
return json.dumps(jsonDOM, cls=RuleEncoder, indent=i)
Methods
-------
to_dict()
Converts contents to a Python dictionary
Adds the '$type' member
Adds a random 'id' member
"""
def __init__(self, typename):
self.typename = typename
self.id = uuid.uuid4()
def __eq__(self, other):
return self.id.__eq__(other.id)
def __ne__(self, other):
return self.__eq__(other) is False
def __hash__(self):
return self.id.__hash__()
def to_dict(self):
data = self.__dict__
data['id'] = f"{{{str(self.id)}}}"
# rename 'typename' to '$type'
data['$type'] = self.typename
data.pop('typename')
return data
class SceneNodeSelectionList(BaseRule):
"""
Contains a list of node names to include (selectedNodes) and to exclude (unselectedNodes)
Attributes
----------
selectedNodes: `list` of str
The node names to include for this group rule
unselectedNodes: `list` of str
The node names to exclude for this group rule
Methods
-------
convert_selection(self, container, key):
this adds its contents to an existing dictionary container at a key position
select_targets(self, selectedList, allNodesList:list)
helper function to include a small list of node names from list of all the node names
to_dict()
Converts contents to a Python dictionary
"""
def __init__(self):
super().__init__('SceneNodeSelectionList')
self.selectedNodes = []
self.unselectedNodes = []
def convert_selection(self, container, key):
container[key] = self.to_dict()
def select_targets(self, selectedList, allNodesList:list):
self.selectedNodes = selectedList
self.unselectedNodes = allNodesList.copy()
for node in selectedList:
if node in self.unselectedNodes:
self.unselectedNodes.remove(node)
class LodNodeSelectionList(SceneNodeSelectionList):
"""
Level of Detail node selection list
The selected nodes should be joints with BoneData
derived from SceneNodeSelectionList
see also LodRule
@ -254,11 +147,11 @@ class PhysicsAnimationConfiguration():
Converts contents to a Python dictionary
"""
def __init__(self):
self.hitDetectionConfig = physics_data.CharacterColliderConfiguration()
self.ragdollConfig = physics_data.RagdollConfiguration()
self.clothConfig = physics_data.CharacterColliderConfiguration()
self.simulatedObjectColliderConfig = physics_data.CharacterColliderConfiguration()
self.hitDetectionConfig = scene_api.physics_data.CharacterColliderConfiguration()
self.ragdollConfig = scene_api.physics_data.RagdollConfiguration()
self.clothConfig = scene_api.physics_data.CharacterColliderConfiguration()
self.simulatedObjectColliderConfig = scene_api.physics_data.CharacterColliderConfiguration()
def to_dict(self):
data = {}
data["hitDetectionConfig"] = self.hitDetectionConfig.to_dict()
@ -288,8 +181,8 @@ class EMotionFXPhysicsSetup():
self.config = PhysicsAnimationConfiguration()
def to_dict(self):
return {
"config" : self.config.to_dict()
return {
"config" : self.config.to_dict()
}
class ActorPhysicsSetupRule(BaseRule):
@ -323,7 +216,7 @@ class ActorPhysicsSetupRule(BaseRule):
def __init__(self):
super().__init__('ActorPhysicsSetupRule')
self.data = EMotionFXPhysicsSetup()
def set_hit_detection_config(self, hitDetectionConfig) -> None:
self.data.config.hitDetectionConfig = hitDetectionConfig
@ -339,7 +232,7 @@ class ActorPhysicsSetupRule(BaseRule):
def to_dict(self):
data = super().to_dict()
data["data"] = self.data.to_dict()
return data
return data
class ActorScaleRule(BaseRule):
"""
@ -358,46 +251,7 @@ class ActorScaleRule(BaseRule):
"""
def __init__(self):
super().__init__('ActorScaleRule')
self.scaleFactor = 1.0
class CoordinateSystemRule(BaseRule):
"""
Modify the target coordinate system, applying a transformation to all data (transforms and vertex data if it exists).
Attributes
----------
targetCoordinateSystem: int
Change the direction the actor/motion will face by applying a post transformation to the data.
useAdvancedData: bool
If True, use advanced settings
originNodeName: str
Select a Node from the scene as the origin for this export.
rotation: [float, float, float, float]
Sets the orientation offset of the processed mesh in degrees. Rotates (yaw, pitch, roll) the group after translation.
translation: [float, float, float]
Moves the group along the given vector3.
scale: float
Sets the scale offset of the processed mesh.
Methods
-------
to_dict()
Converts contents to a Python dictionary
"""
def __init__(self):
super().__init__('CoordinateSystemRule')
self.targetCoordinateSystem = 0
self.useAdvancedData = False
self.originNodeName = ''
self.rotation = [0.0, 0.0, 0.0, 1.0]
self.translation = [0.0, 0.0, 0.0]
self.scale = 1.0
self.scaleFactor = 1.0
class SkeletonOptimizationRule(BaseRule):
"""
@ -436,8 +290,8 @@ class LodRule(BaseRule):
Set up the level of detail for the meshes in this group.
The engine supports 6 total lods.
1 for the base model then 5 more lods.
The rule only captures lods past level 0 so this is set to 5.
1 for the base model then 5 more lods.
The rule only captures lods past level 0 so this is set to 5.
Attributes
----------
@ -458,7 +312,7 @@ class LodRule(BaseRule):
def __init__(self):
super().__init__('{3CB103B3-CEAF-49D7-A9DC-5A31E2DF15E4} LodRule')
self.nodeSelectionList = [] # list of LodNodeSelectionList
def add_lod_level(self, lodLevel, selectedNodes=None, unselectedNodes=None) -> LodNodeSelectionList:
lodNodeSelection = LodNodeSelectionList()
lodNodeSelection.selectedNodes = selectedNodes
@ -466,13 +320,13 @@ class LodRule(BaseRule):
lodNodeSelection.lodLevel = lodLevel
self.nodeSelectionList.append(lodNodeSelection)
return lodNodeSelection
def to_dict(self):
data = super().to_dict()
selectionListList = data.pop('nodeSelectionList')
data['nodeSelectionList'] = []
for nodeList in selectionListList:
data['nodeSelectionList'].append(nodeList.to_dict())
data['nodeSelectionList'].append(nodeList.to_dict())
return data
class MorphTargetRule(BaseRule):
@ -499,21 +353,3 @@ class MorphTargetRule(BaseRule):
self.targets.convert_selection(data, 'targets')
return data
class CommentRule(BaseRule):
"""
Add an optional comment to the asset's properties.
Attributes
----------
text: str
Text for the comment.
Methods
-------
to_dict()
Converts contents to a Python dictionary
"""
def __init__(self):
super().__init__('CommentRule')
self.text = ''

@ -0,0 +1,221 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import traceback, sys, uuid, os, json, logging
def log_exception_traceback():
"""
Outputs an exception stacktrace.
"""
data = traceback.format_exc()
logger = logging.getLogger('python')
logger.error(data)
class BaseRule():
"""
Base class of the actor rules to help encode the type name of abstract rules
Parameters
----------
typename : str
A typename the $type will be in the JSON chunk object
Attributes
----------
typename: str
The type name of the abstract classes to be serialized
id: UUID
a unique ID for the rule
Methods
-------
to_dict()
Converts contents to a Python dictionary
Adds the '$type' member
Adds a random 'id' member
Note: Override this method if a derviced class needs to return a custom dictionary
"""
def __init__(self, typename):
self.typename = typename
self.id = uuid.uuid4()
def __eq__(self, other):
return self.id.__eq__(other.id)
def __ne__(self, other):
return self.__eq__(other) is False
def __hash__(self):
return self.id.__hash__()
def to_dict(self):
data = vars(self)
data['id'] = f"{{{str(self.id)}}}"
# rename 'typename' to '$type'
data['$type'] = self.typename
data.pop('typename')
return data
def convert_rule_to_json(rule:BaseRule, indentValue=0):
"""
Helper function to convert a BaseRule into a JSON string
Parameters
----------
obj : any
The object to convert to a JSON string as long as the obj class has an *to_dict* method
indentValue : int
The number of spaces to indent between each JSON block/value
"""
return json.dumps(rule.to_dict(), indent=indentValue, cls=RuleEncoder)
class CommentRule(BaseRule):
"""
Add an optional comment to the asset's properties.
Attributes
----------
text: str
Text for the comment.
Methods
-------
to_dict()
Converts contents to a Python dictionary
"""
def __init__(self):
super().__init__('CommentRule')
self.text = ''
class SceneNodeSelectionList(BaseRule):
"""
Contains a list of node names to include (selectedNodes) and to exclude (unselectedNodes)
Attributes
----------
selectedNodes: `list` of str
The node names to include for this group rule
unselectedNodes: `list` of str
The node names to exclude for this group rule
Methods
-------
convert_selection(self, container, key):
this adds its contents to an existing dictionary container at a key position
select_targets(self, selectedList, allNodesList:list)
helper function to include a small list of node names from list of all the node names
to_dict()
Converts contents to a Python dictionary
"""
def __init__(self):
super().__init__('SceneNodeSelectionList')
self.selectedNodes = []
self.unselectedNodes = []
def convert_selection(self, container, key):
container[key] = self.to_dict()
def select_targets(self, selectedList, allNodesList:list):
self.selectedNodes = selectedList
self.unselectedNodes = allNodesList.copy()
for node in selectedList:
if node in self.unselectedNodes:
self.unselectedNodes.remove(node)
class CoordinateSystemRule(BaseRule):
"""
Modify the target coordinate system, applying a transformation to all data (transforms and vertex data if it exists).
Attributes
----------
targetCoordinateSystem: int
Change the direction the actor/motion will face by applying a post transformation to the data.
useAdvancedData: bool
If True, use advanced settings
originNodeName: str
Select a Node from the scene as the origin for this export.
rotation: [float, float, float, float]
Sets the orientation offset of the processed mesh in degrees. Rotates (yaw, pitch, roll) the group after translation.
translation: [float, float, float]
Moves the group along the given vector3.
scale: float
Sets the scale offset of the processed mesh.
"""
def __init__(self):
super().__init__('CoordinateSystemRule')
self.targetCoordinateSystem = 0
self.useAdvancedData = False
self.originNodeName = ''
self.rotation = [0.0, 0.0, 0.0, 1.0]
self.translation = [0.0, 0.0, 0.0]
self.scale = 1.0
class TypeId():
"""
Wraps a UUID that represents a AZ::TypeId from O3DE
Attributes
----------
valud: uuid.Uuid
A unique ID that defaults to AZ::TypeId::CreateNull()
"""
def __init__(self):
self.value = uuid.UUID('{00000000-0000-0000-0000-000000000000}')
def __str__(self):
return f"{{{str(self.value)}}}"
class RuleEncoder(json.JSONEncoder):
"""
A helper class to encode the Python classes with to a Python dictionary
Methods
-------
default(obj)
Converts a single object to a JSON value that can be stored with a key
encode(obj)
Converts contents to a Python dictionary for the JSONEncoder
"""
def default(self, obj):
if (isinstance(obj,TypeId)):
return str(obj)
elif hasattr(obj, 'to_json_value'):
return obj.to_json_value()
return super().default(obj)
def encode(self, obj):
chunk = obj
if isinstance(obj, BaseRule):
chunk = obj.to_dict()
elif isinstance(obj, dict):
chunk = obj
elif hasattr(obj, 'to_dict'):
chunk = obj.to_dict()
else:
chunk = obj.__dict__
return super().encode(chunk)

@ -0,0 +1,214 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
from enum import Enum
import scene_api.common_rules
class MotionGroup(scene_api.common_rules.BaseRule):
"""
Configure animation data for exporting.
Attributes
----------
name: str
Name for the group.
This name will also be used as the name for the generated file.
selectedRootBone: str
The root bone of the animation that will be exported.
rules: list of BaseRule
Add or remove rules to fine-tune the export process.
List of rules for a motion group including:
MotionScaleRule
CoordinateSystemRule
MotionRangeRule
MotionAdditiveRule
MotionSamplingRule
"""
def __init__(self):
super().__init__('MotionGroup')
self.name = ''
self.selectedRootBone = ''
self.rules = set()
def add_rule(self, rule) -> bool:
if (rule not in self.rules):
self.rules.add(rule)
return True
return False
def create_rule(self, rule) -> any:
if (self.add_rule(rule)):
return rule
return None
def remove_rule(self, type) -> None:
self.rules.discard(rule)
def to_dict(self) -> dict:
out = super().to_dict()
out['name'] = self.name
out['selectedRootBone'] = self.selectedRootBone
# convert the rules
ruleList = []
for rule in self.rules:
ruleList.append(rule.to_dict())
out['rules'] = ruleList
return out
def to_json(self) -> str:
jsonDOM = self.to_dict()
return json.dumps(jsonDOM, cls=RuleEncoder)
class MotionCompressionSettingsRule(scene_api.common_rules.BaseRule):
"""
A BaseRule that ses the error tolerance settings while compressing the animation
Attributes
----------
maxTranslationError: float
Maximum error allowed in translation.
Min 0.0, Max 0.1
maxRotationError: float
Maximum error allowed in rotation.
Min 0.0, Max 0.1
maxScaleError: float
Maximum error allowed in scale.
Min 0.0, Max 0.01
"""
def __init__(self):
super().__init__('MotionCompressionSettingsRule')
self.maxTranslationError = 0.0001
self.maxRotationError = 0.0001
self.maxScaleError = 0.0001
class MotionScaleRule(scene_api.common_rules.BaseRule):
"""
A BaseRule that scales the spatial extent of motion
Attributes
----------
scaleFactor: float
Scale factor; min 0.0001, max 10000.0
"""
def __init__(self):
super().__init__('MotionScaleRule')
self.scaleFactor = 1.0
class MotionRangeRule(scene_api.common_rules.BaseRule):
"""
A BaseRule that defines the range of the motion that will be exported.
Attributes
----------
startFrame: float
The start frame of the animation that will be exported.
endFrame: float
The end frame of the animation that will be exported.
"""
def __init__(self):
super().__init__('MotionRangeRule')
self.startFrame = 0
self.endFrame = 0
class MotionAdditiveRule(scene_api.common_rules.BaseRule):
"""
A BaseRule that makes the motion an additive motion.
Attributes
----------
sampleFrame: int
The frame number that the motion will be made relative to.
"""
def __init__(self):
super().__init__('MotionAdditiveRule')
self.sampleFrame = 0
class SampleRateMethod(Enum):
"""
A collection of settings related to sampling of the motion
Attributes
----------
FromSourceScene: int, value = 0
Use the source scene's sample rate
Custom: int, value = 1
Use the use a custom sample rate
"""
FromSourceScene = 0
Custom = 1
def to_json_value(self):
if(self == SampleRateMethod.FromSourceScene):
return 0
return 1
class MotionSamplingRule(scene_api.common_rules.BaseRule):
"""
A collection of settings related to sampling of the motion
Attributes
----------
motionDataType: scene_api.common_rules.TypeId()
The motion data type to use. This defines how the motion data is stored.
This can have an effect on performance and memory usage.
sampleRateMethod: SampleRateMethod
Either use the sample rate from the source scene file or use a custom sample rate.
The sample rate is automatically limited to the rate from source scene file (e.g. FBX)
customSampleRate: float
Overwrite the sample rate of the motion, in frames per second.
Min: 1.0, Max 240.0
translationQualityPercentage: float
The percentage of quality for translation. Higher values preserve quality, but increase memory usage.
Min: 1.0, Max 100.0
rotationQualityPercentage: float
The percentage of quality for rotation. Higher values preserve quality, but increase memory usage.
Min: 1.0, Max 100.0
scaleQualityPercentage: float
The percentage of quality for scale. Higher values preserve quality, but increase memory usage.
Min: 1.0, Max 100.0
allowedSizePercentage: float
The percentage of extra memory usage allowed compared to the smallest size.
For example a value of 10 means we are allowed 10 percent more memory worst case, in trade for extra performance.
Allow 15 percent larger size, in trade for performance (in Automatic mode, so when m_motionDataType is a Null typeId).
Min: 0.0, Max 100.0
keepDuration: bool
When enabled this keep the duration the same as the Fbx motion duration, even if no joints are animated.
When this option is disabled and the motion doesn't animate any joints then the resulting motion will have a duration of zero seconds.
"""
def __init__(self):
super().__init__('MotionSamplingRule')
self.motionDataType = scene_api.common_rules.TypeId()
self.sampleRateMethod = SampleRateMethod.FromSourceScene
self.customSampleRate = 60.0
self.translationQualityPercentage = 75.0
self.rotationQualityPercentage = 75.0
self.scaleQualityPercentage = 75.0
self.allowedSizePercentage = 15.0
self.keepDuration = True

@ -1,9 +1,10 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Shape.h
@ -69,7 +70,7 @@ class CharacterColliderNodeConfiguration():
name : str
debug name of the node
shapes : `list` of `tuple` of (ColliderConfiguration, ShapeConfiguration)
shapes : `list` of `tuple` of (ColliderConfiguration, ShapeConfiguration)
a list of pairs of collider and shape configuration
Methods
@ -83,12 +84,12 @@ class CharacterColliderNodeConfiguration():
"""
def __init__(self):
self.name = ''
self.shapes = [] # List of Tuple of (ColliderConfiguration, ShapeConfiguration)
self.shapes = [] # List of Tuple of (ColliderConfiguration, ShapeConfiguration)
def add_collider_shape_pair(self, colliderConfiguration, shapeConfiguration) -> None:
pair = (colliderConfiguration, shapeConfiguration)
self.shapes.append(pair)
def to_dict(self):
data = {}
shapeList = []
@ -115,7 +116,7 @@ class CharacterColliderConfiguration():
Helper function to add a character collider node configuration into the nodes
add_character_collider_node_configuration_node(name, colliderConfiguration, shapeConfiguration)
Helper function to add a character collider node configuration into the nodes
Helper function to add a character collider node configuration into the nodes
**Returns**: CharacterColliderNodeConfiguration
@ -125,10 +126,10 @@ class CharacterColliderConfiguration():
"""
def __init__(self):
self.nodes = [] # list of CharacterColliderNodeConfiguration
def add_character_collider_node_configuration(self, characterColliderNodeConfiguration) -> None:
self.nodes.append(characterColliderNodeConfiguration)
def add_character_collider_node_configuration_node(self, name, colliderConfiguration, shapeConfiguration) -> CharacterColliderNodeConfiguration:
characterColliderNodeConfiguration = CharacterColliderNodeConfiguration()
self.add_character_collider_node_configuration(characterColliderNodeConfiguration)
@ -169,7 +170,7 @@ class ShapeConfiguration():
"$type": self._shapeType,
"Scale": self.scale
}
class SphereShapeConfiguration(ShapeConfiguration):
"""
The configuration for a Sphere collider
@ -191,7 +192,7 @@ class SphereShapeConfiguration(ShapeConfiguration):
def to_dict(self):
data = super().to_dict()
data['Radius'] = self.radius
return data
return data
class BoxShapeConfiguration(ShapeConfiguration):
"""
@ -215,7 +216,7 @@ class BoxShapeConfiguration(ShapeConfiguration):
data = super().to_dict()
data['Configuration'] = self.dimensions
return data
class CapsuleShapeConfiguration(ShapeConfiguration):
"""
The configuration for a Capsule collider
@ -237,13 +238,13 @@ class CapsuleShapeConfiguration(ShapeConfiguration):
super().__init__('CapsuleShapeConfiguration')
self.height = 1.00
self.radius = 0.25
def to_dict(self):
data = super().to_dict()
data['Height'] = self.height
data['Radius'] = self.radius
return data
return data
class PhysicsAssetShapeConfiguration(ShapeConfiguration):
"""
The configuration for a Asset collider using a mesh asset for collision
@ -276,7 +277,7 @@ class PhysicsAssetShapeConfiguration(ShapeConfiguration):
self.assetScale = [1.0, 1.0, 1.0]
self.useMaterialsFromAsset = True
self.subdivisionLevel = 4
def set_asset_reference(self, assetReference: str) -> None:
self.asset = { "assetHint": assetReference }
@ -286,7 +287,7 @@ class PhysicsAssetShapeConfiguration(ShapeConfiguration):
data['AssetScale'] = self.assetScale
data['UseMaterialsFromAsset'] = self.useMaterialsFromAsset
data['SubdivisionLevel'] = self.subdivisionLevel
return data
return data
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Configuration\JointConfiguration.h
@ -313,7 +314,7 @@ class JointConfiguration():
ChildLocalPosition: [float, float, float]
Joint position relative to child body.
StartSimulationEnabled: bool
StartSimulationEnabled: bool
When active, the joint will be enabled when the simulation begins.
Methods
@ -372,7 +373,7 @@ class SimulatedBodyConfiguration():
"orientation" : self.orientation,
"startSimulationEnabled" : self.startSimulationEnabled
}
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Configuration\RigidBodyConfiguration.h
@ -394,7 +395,7 @@ class RigidBodyConfiguration(SimulatedBodyConfiguration):
Local space offset for the center of mass (COM).
mass: float
The mass of the rigid body in kilograms.
The mass of the rigid body in kilograms.
A value of 0 is treated as infinite.
The trajectory of infinite mass bodies cannot be affected by any collisions or forces other than gravity.
@ -423,8 +424,8 @@ class RigidBodyConfiguration(SimulatedBodyConfiguration):
When active, the rigid body is not affected by gravity or other forces and is moved by script.
ccdEnabled: bool
When active, the rigid body has continuous collision detection (CCD).
Use this to ensure accurate collision detection, particularly for fast moving rigid bodies.
When active, the rigid body has continuous collision detection (CCD).
Use this to ensure accurate collision detection, particularly for fast moving rigid bodies.
CCD must be activated in the global PhysX preferences.
ccdMinAdvanceCoefficient: float
@ -511,7 +512,7 @@ class RigidBodyConfiguration(SimulatedBodyConfiguration):
data["Maximum Angular Velocity"] = self.maxAngularVelocity
data["Include All Shapes In Mass"] = self.includeAllShapesInMassCalculation
data["CCD Min Advance"] = self.ccdMinAdvanceCoefficient
data["CCD Friction"] = self.ccdFrictionEnabled
data["CCD Friction"] = self.ccdFrictionEnabled
return data
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Ragdoll.h
@ -566,7 +567,7 @@ class RagdollConfiguration():
def __init__(self):
self.nodes = [] # list of RagdollNodeConfiguration
self.colliders = CharacterColliderConfiguration()
def add_ragdoll_node_configuration(self, ragdollNodeConfiguration) -> None:
self.nodes.append(ragdollNodeConfiguration)

@ -1,15 +1,15 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import typing
import json
import azlmbr.scene as sceneApi
from enum import IntEnum
# Wraps the AZ.SceneAPI.Containers.SceneGraph.NodeIndex internal class
class SceneGraphNodeIndex:
def __init__(self, scene_graph_node_index) -> None:
@ -199,6 +199,16 @@ class SceneManifest:
self.manifest['values'].append(prefab_group)
return prefab_group
def add_actor_group(self, group) -> dict:
groupDict = group.to_dict()
self.manifest['values'].append(groupDict)
return groupDict
def add_motion_group(self, group) -> dict:
groupDict = group.to_dict()
self.manifest['values'].append(groupDict)
return groupDict
def mesh_group_select_node(self, mesh_group: dict, node_name: str) -> None:
"""Adds a node as a selected node.

Loading…
Cancel
Save