{lyn2264} Adding Python API for Actor Group (#6064)
* {lyn2264} Adding Python API for Actor Group
* updating the IBoneData with behavior attributes
* adding actor_group.py for making actor group JSON data
* adding physics_data.py for making physics JSON data
Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
* fixing dictionary issues
Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
* updated comment style to numpydoc
Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
* updated to numpydoc style
Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
* example of actor group rules
Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
* Updated based on feedback
Signed-off-by: Allen Jackson <23512001+jackalbe@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,580 @@
|
||||
"""
|
||||
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
|
||||
|
||||
class ColliderConfiguration():
|
||||
"""
|
||||
Configuration for a collider
|
||||
|
||||
Attributes
|
||||
----------
|
||||
Trigger: bool
|
||||
Should this shape act as a trigger shape.
|
||||
|
||||
Simulated: bool
|
||||
Should this shape partake in collision in the physical simulation.
|
||||
|
||||
InSceneQueries: bool
|
||||
Should this shape partake in scene queries (ray casts, overlap tests, sweeps).
|
||||
|
||||
Exclusive: bool
|
||||
Can this collider be shared between multiple bodies?
|
||||
|
||||
Position: [float, float, float] Vector3
|
||||
Shape offset relative to the connected rigid body.
|
||||
|
||||
Rotation: [float, float, float, float] Quaternion
|
||||
Shape rotation relative to the connected rigid body.
|
||||
|
||||
ColliderTag: str
|
||||
Identification tag for the collider.
|
||||
|
||||
RestOffset: float
|
||||
Bodies will come to rest separated by the sum of their rest offsets.
|
||||
|
||||
ContactOffset: float
|
||||
Bodies will start to generate contacts when closer than the sum of their contact offsets.
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
self.Trigger = True
|
||||
self.Simulated = True
|
||||
self.InSceneQueries = True
|
||||
self.Exclusive = True
|
||||
self.Position = [0.0, 0.0, 0.0]
|
||||
self.Rotation = [0.0, 0.0, 0.0, 1.0]
|
||||
self.ColliderTag = ''
|
||||
self.RestOffset = 0.0
|
||||
self.ContactOffset = 0.02
|
||||
|
||||
def to_dict(self):
|
||||
return self.__dict__
|
||||
|
||||
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Character.h
|
||||
|
||||
class CharacterColliderNodeConfiguration():
|
||||
"""
|
||||
Shapes to define the animation's to model of physics
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
debug name of the node
|
||||
|
||||
shapes : `list` of `tuple` of (ColliderConfiguration, ShapeConfiguration)
|
||||
a list of pairs of collider and shape configuration
|
||||
|
||||
Methods
|
||||
-------
|
||||
add_collider_shape_pair(colliderConfiguration, shapeConfiguration)
|
||||
Helper function to add a collider and shape configuration at the same time
|
||||
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.name = ''
|
||||
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 = []
|
||||
for index, shape in enumerate(self.shapes):
|
||||
tupleValue = (shape[0].to_dict(), # ColliderConfiguration
|
||||
shape[1].to_dict()) # ShapeConfiguration
|
||||
shapeList.append(tupleValue)
|
||||
data['name'] = self.name
|
||||
data['shapes'] = shapeList
|
||||
return data
|
||||
|
||||
class CharacterColliderConfiguration():
|
||||
"""
|
||||
Information required to create the basic physics representation of a character.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
nodes : `list` of CharacterColliderNodeConfiguration
|
||||
a list of CharacterColliderNodeConfiguration nodes
|
||||
|
||||
Methods
|
||||
-------
|
||||
add_character_collider_node_configuration(colliderConfiguration, shapeConfiguration)
|
||||
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
|
||||
|
||||
**Returns**: CharacterColliderNodeConfiguration
|
||||
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
|
||||
"""
|
||||
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)
|
||||
characterColliderNodeConfiguration.name = name
|
||||
characterColliderNodeConfiguration.add_collider_shape_pair(colliderConfiguration, shapeConfiguration)
|
||||
return characterColliderNodeConfiguration
|
||||
|
||||
def to_dict(self):
|
||||
data = {}
|
||||
nodeList = []
|
||||
for node in self.nodes:
|
||||
nodeList.append(node.to_dict())
|
||||
data['nodes'] = nodeList
|
||||
return data
|
||||
|
||||
# see Code\Framework\AzFramework\AzFramework\Physics\ShapeConfiguration.h for underlying data structures
|
||||
|
||||
class ShapeConfiguration():
|
||||
"""
|
||||
Base class for all the shape collider configurations
|
||||
|
||||
Attributes
|
||||
----------
|
||||
scale : [float, float, float]
|
||||
a 3-element list to describe the scale along the X, Y, and Z axises such as [1.0, 1.0, 1.0]
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self, shapeType):
|
||||
self._shapeType = shapeType
|
||||
self.scale = [1.0, 1.0, 1.0]
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"$type": self._shapeType,
|
||||
"Scale": self.scale
|
||||
}
|
||||
|
||||
class SphereShapeConfiguration(ShapeConfiguration):
|
||||
"""
|
||||
The configuration for a Sphere collider
|
||||
|
||||
Attributes
|
||||
----------
|
||||
radius: float
|
||||
a scalar value to define the radius of the sphere
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__('SphereShapeConfiguration')
|
||||
self.radius = 0.5
|
||||
|
||||
def to_dict(self):
|
||||
data = super().to_dict()
|
||||
data['Radius'] = self.radius
|
||||
return data
|
||||
|
||||
class BoxShapeConfiguration(ShapeConfiguration):
|
||||
"""
|
||||
The configuration for a Box collider
|
||||
|
||||
Attributes
|
||||
----------
|
||||
dimensions: [float, float, float]
|
||||
The width, height, and depth dimensions of the Box collider
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__('BoxShapeConfiguration')
|
||||
self.dimensions = [1.0, 1.0, 1.0]
|
||||
|
||||
def to_dict(self):
|
||||
data = super().to_dict()
|
||||
data['Configuration'] = self.dimensions
|
||||
return data
|
||||
|
||||
class CapsuleShapeConfiguration(ShapeConfiguration):
|
||||
"""
|
||||
The configuration for a Capsule collider
|
||||
|
||||
Attributes
|
||||
----------
|
||||
height: float
|
||||
The height of the Capsule
|
||||
|
||||
radius: float
|
||||
The radius of the Capsule
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
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
|
||||
|
||||
class PhysicsAssetShapeConfiguration(ShapeConfiguration):
|
||||
"""
|
||||
The configuration for a Asset collider using a mesh asset for collision
|
||||
|
||||
Attributes
|
||||
----------
|
||||
asset: { "assetHint": assetReference }
|
||||
the name of the asset to load for collision information
|
||||
|
||||
assetScale: [float, float, float]
|
||||
The scale of the asset shape such as [1.0, 1.0, 1.0]
|
||||
|
||||
useMaterialsFromAsset: bool
|
||||
Auto-set physics materials using asset's physics material names
|
||||
|
||||
subdivisionLevel: int
|
||||
The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.
|
||||
|
||||
Methods
|
||||
-------
|
||||
set_asset_reference(self, assetReference: str)
|
||||
Helper function to set the asset reference to the collision mesh such as 'my/folder/my_mesh.azmodel'
|
||||
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__('PhysicsAssetShapeConfiguration')
|
||||
self.asset = {}
|
||||
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 }
|
||||
|
||||
def to_dict(self):
|
||||
data = super().to_dict()
|
||||
data['PhysicsAsset'] = self.asset
|
||||
data['AssetScale'] = self.assetScale
|
||||
data['UseMaterialsFromAsset'] = self.useMaterialsFromAsset
|
||||
data['SubdivisionLevel'] = self.subdivisionLevel
|
||||
return data
|
||||
|
||||
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Configuration\JointConfiguration.h
|
||||
|
||||
class JointConfiguration():
|
||||
"""
|
||||
The joint configuration
|
||||
|
||||
see also: class AzPhysics::JointConfiguration
|
||||
|
||||
Attributes
|
||||
----------
|
||||
Name: str
|
||||
For debugging/tracking purposes only.
|
||||
|
||||
ParentLocalRotation: [float, float, float, float]
|
||||
Parent joint frame relative to parent body.
|
||||
|
||||
ParentLocalPosition: [float, float, float]
|
||||
Joint position relative to parent body.
|
||||
|
||||
ChildLocalRotation: [float, float, float, float]
|
||||
Child joint frame relative to child body.
|
||||
|
||||
ChildLocalPosition: [float, float, float]
|
||||
Joint position relative to child body.
|
||||
|
||||
StartSimulationEnabled: bool
|
||||
When active, the joint will be enabled when the simulation begins.
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
self.Name = ''
|
||||
self.ParentLocalRotation = [0.0, 0.0, 0.0, 1.0]
|
||||
self.ParentLocalPosition = [0.0, 0.0, 0.0]
|
||||
self.ChildLocalRotation = [0.0, 0.0, 0.0, 1.0]
|
||||
self.ChildLocalPosition = [0.0, 0.0, 0.0]
|
||||
self.StartSimulationEnabled = True
|
||||
|
||||
def to_dict(self):
|
||||
return self.__dict__
|
||||
|
||||
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Configuration\SimulatedBodyConfiguration.h
|
||||
|
||||
class SimulatedBodyConfiguration():
|
||||
"""
|
||||
Base Class of all Physics Bodies that will be simulated.
|
||||
|
||||
see also: class AzPhysics::SimulatedBodyConfiguration
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name: str
|
||||
For debugging/tracking purposes only.
|
||||
|
||||
position: [float, float, float]
|
||||
starting position offset
|
||||
|
||||
orientation: [float, float, float, float]
|
||||
starting rotation (Quaternion)
|
||||
|
||||
startSimulationEnabled: bool
|
||||
to start when simulation engine starts
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
self.name = ''
|
||||
self.position = [0.0, 0.0, 0.0]
|
||||
self.orientation = [0.0, 0.0, 0.0, 1.0]
|
||||
self.startSimulationEnabled = True
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"name" : self.name,
|
||||
"position" : self.position,
|
||||
"orientation" : self.orientation,
|
||||
"startSimulationEnabled" : self.startSimulationEnabled
|
||||
}
|
||||
|
||||
|
||||
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Configuration\RigidBodyConfiguration.h
|
||||
|
||||
class RigidBodyConfiguration(SimulatedBodyConfiguration):
|
||||
"""
|
||||
PhysX Rigid Body Configuration
|
||||
|
||||
see also: class AzPhysics::RigidBodyConfiguration
|
||||
|
||||
Attributes
|
||||
----------
|
||||
initialLinearVelocity: [float, float, float]
|
||||
Linear velocity applied when the rigid body is activated.
|
||||
|
||||
initialAngularVelocity: [float, float, float]
|
||||
Angular velocity applied when the rigid body is activated (limited by maximum angular velocity)
|
||||
|
||||
centerOfMassOffset: [float, float, float]
|
||||
Local space offset for the center of mass (COM).
|
||||
|
||||
mass: float
|
||||
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.
|
||||
|
||||
linearDamping: float
|
||||
The rate of decay over time for linear velocity even if no forces are acting on the rigid body.
|
||||
|
||||
angularDamping: float
|
||||
The rate of decay over time for angular velocity even if no forces are acting on the rigid body.
|
||||
|
||||
sleepMinEnergy: float
|
||||
The rigid body can go to sleep (settle) when kinetic energy per unit mass is persistently below this value.
|
||||
|
||||
maxAngularVelocity: float
|
||||
Clamp angular velocities to this maximum value.
|
||||
|
||||
startAsleep: bool
|
||||
When active, the rigid body will be asleep when spawned, and wake when the body is disturbed.
|
||||
|
||||
interpolateMotion: bool
|
||||
When active, simulation results are interpolated resulting in smoother motion.
|
||||
|
||||
gravityEnabled: bool
|
||||
When active, global gravity affects this rigid body.
|
||||
|
||||
kinematic: bool
|
||||
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.
|
||||
CCD must be activated in the global PhysX preferences.
|
||||
|
||||
ccdMinAdvanceCoefficient: float
|
||||
Coefficient affecting how granularly time is subdivided in CCD.
|
||||
|
||||
ccdFrictionEnabled: bool
|
||||
Whether friction is applied when resolving CCD collisions.
|
||||
|
||||
computeCenterOfMass: bool
|
||||
Compute the center of mass (COM) for this rigid body.
|
||||
|
||||
computeInertiaTensor: bool
|
||||
When active, inertia is computed based on the mass and shape of the rigid body.
|
||||
|
||||
computeMass: bool
|
||||
When active, the mass of the rigid body is computed based on the volume and density values of its colliders.
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Basic initial settings.
|
||||
self.initialLinearVelocity = [0.0, 0.0, 0.0]
|
||||
self.initialAngularVelocity = [0.0, 0.0, 0.0]
|
||||
self.centerOfMassOffset = [0.0, 0.0, 0.0]
|
||||
|
||||
# Simulation parameters.
|
||||
self.mass = 1.0
|
||||
self.linearDamping = 0.05
|
||||
self.angularDamping = 0.15
|
||||
self.sleepMinEnergy = 0.005
|
||||
self.maxAngularVelocity = 100.0
|
||||
self.startAsleep = False
|
||||
self.interpolateMotion = False
|
||||
self.gravityEnabled = True
|
||||
self.kinematic = False
|
||||
self.ccdEnabled = False
|
||||
self.ccdMinAdvanceCoefficient = 0.15
|
||||
self.ccdFrictionEnabled = False
|
||||
self.computeCenterOfMass = True
|
||||
self.computeInertiaTensor = True
|
||||
self.computeMass = True
|
||||
|
||||
# Flags to restrict motion along specific world-space axes.
|
||||
self.lockLinearX = False
|
||||
self.lockLinearY = False
|
||||
self.lockLinearZ = False
|
||||
|
||||
# Flags to restrict rotation around specific world-space axes.
|
||||
self.lockAngularX = False
|
||||
self.lockAngularY = False
|
||||
self.lockAngularZ = False
|
||||
|
||||
# If set, non-simulated shapes will also be included in the mass properties calculation.
|
||||
self.includeAllShapesInMassCalculation = False
|
||||
|
||||
def to_dict(self):
|
||||
data = super().to_dict()
|
||||
data["Initial linear velocity"] = self.initialLinearVelocity
|
||||
data["Initial angular velocity"] = self.initialAngularVelocity
|
||||
data["Linear damping"] = self.linearDamping
|
||||
data["Angular damping"] = self.angularDamping
|
||||
data["Sleep threshold"] = self.sleepMinEnergy
|
||||
data["Start Asleep"] = self.startAsleep
|
||||
data["Interpolate Motion"] = self.interpolateMotion
|
||||
data["Gravity Enabled"] = self.gravityEnabled
|
||||
data["Kinematic"] = self.kinematic
|
||||
data["CCD Enabled"] = self.ccdEnabled
|
||||
data["Compute Mass"] = self.computeMass
|
||||
data["Lock Linear X"] = self.lockLinearX
|
||||
data["Lock Linear Y"] = self.lockLinearY
|
||||
data["Lock Linear Z"] = self.lockLinearZ
|
||||
data["Lock Angular X"] = self.lockAngularX
|
||||
data["Lock Angular Y"] = self.lockAngularY
|
||||
data["Lock Angular Z"] = self.lockAngularZ
|
||||
data["Mass"] = self.mass
|
||||
data["Compute COM"] = self.computeCenterOfMass
|
||||
data["Centre of mass offset"] = self.centerOfMassOffset
|
||||
data["Compute inertia"] = self.computeInertiaTensor
|
||||
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
|
||||
return data
|
||||
|
||||
# for underlying data structures, see Code\Framework\AzFramework\AzFramework\Physics\Ragdoll.h
|
||||
|
||||
class RagdollNodeConfiguration(RigidBodyConfiguration):
|
||||
"""
|
||||
Ragdoll node Configuration
|
||||
|
||||
see also: class Physics::RagdollConfiguration
|
||||
|
||||
Attributes
|
||||
----------
|
||||
JointConfig: JointConfiguration
|
||||
Ragdoll joint node configuration
|
||||
|
||||
Methods
|
||||
-------
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.JointConfig = JointConfiguration()
|
||||
|
||||
def to_dict(self):
|
||||
data = super().to_dict()
|
||||
data['JointConfig'] = self.JointConfig.to_dict()
|
||||
return data
|
||||
|
||||
class RagdollConfiguration():
|
||||
"""
|
||||
A configuration of join nodes and a character collider configuration for a ragdoll
|
||||
|
||||
see also: class Physics::RagdollConfiguration
|
||||
|
||||
Attributes
|
||||
----------
|
||||
nodes: `list` of RagdollNodeConfiguration
|
||||
A list of RagdollNodeConfiguration entries
|
||||
|
||||
colliders: CharacterColliderConfiguration
|
||||
A CharacterColliderConfiguration
|
||||
|
||||
Methods
|
||||
-------
|
||||
add_ragdoll_node_configuration(ragdollNodeConfiguration)
|
||||
Helper function to add a single ragdoll node configuration (normally for each joint/bone node)
|
||||
|
||||
to_dict()
|
||||
Converts contents to a Python dictionary
|
||||
"""
|
||||
def __init__(self):
|
||||
self.nodes = [] # list of RagdollNodeConfiguration
|
||||
self.colliders = CharacterColliderConfiguration()
|
||||
|
||||
def add_ragdoll_node_configuration(self, ragdollNodeConfiguration) -> None:
|
||||
self.nodes.append(ragdollNodeConfiguration)
|
||||
|
||||
def to_dict(self):
|
||||
data = {}
|
||||
nodeList = []
|
||||
for index, node in enumerate(self.nodes):
|
||||
nodeList.append(node.to_dict())
|
||||
data['nodes'] = nodeList
|
||||
data['colliders'] = self.colliders.to_dict()
|
||||
return data
|
||||
Reference in New Issue
Block a user