Rename to final folder name
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976194
|
||||
# Test Case Title : Verify that you can add PhysX Rigid Bodies Physics component to an Entity without any warning or Error.
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_cube = ("Entity Cube found", "Cube not found")
|
||||
linear_damp = ("Cube Linear Damping equal", "Cube Linear Damping not equal")
|
||||
angular_damp = ("Cube Angular Damping equal", "Cube Angular Damping not equal")
|
||||
mass = ("Cube Mass equal", "Cube Mass not equal")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_AddRigidBodyComponent():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Open a Project that already has a PhysxRigidBody component in it and verify PhysxRigidBody is working in Game Mode
|
||||
|
||||
Level Description:
|
||||
PhysxRigidBody (entity) - PhysxRigidBody entity is created in the level
|
||||
|
||||
Expected Behavior:
|
||||
The PhysxRigidBody entity should be working in the game mode
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entity
|
||||
4) Validate Linear damping, Angular Damping and Mass of PhysxRigidBody
|
||||
5) Set a new values for Linear damping, Angular Damping and Mass of PhysxRigidBody and validate it
|
||||
6) Exit game mode
|
||||
7) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os, sys
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
DEFAULT_LINEAR_DAMPING = 0.05
|
||||
DEFAULT_ANGULAR_DAMPING = 0.15
|
||||
DEFAULT_MASS = 1.0
|
||||
NEW_LINEAR_DAMPING = 1.05
|
||||
NEW_ANGULAR_DAMPING = 1.15
|
||||
NEW_MASS = 2.0
|
||||
CLOSE_ENOUGH = 0.001
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_AddRigidBodyComponent")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate Entity
|
||||
cube_id = general.find_game_entity("CubeRigidBody")
|
||||
Report.critical_result(Tests.find_cube, cube_id.IsValid())
|
||||
|
||||
# 4) Validate Linear damping, Angular Damping and Mass of PhysxRigidBody
|
||||
cube_linear_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearDamping", cube_id)
|
||||
Report.result(Tests.linear_damp, abs(cube_linear_damping - DEFAULT_LINEAR_DAMPING) < CLOSE_ENOUGH)
|
||||
cube_angular_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularDamping", cube_id)
|
||||
Report.result(Tests.angular_damp, abs(cube_angular_damping - DEFAULT_ANGULAR_DAMPING) < CLOSE_ENOUGH)
|
||||
cube_mass = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", cube_id)
|
||||
Report.result(Tests.mass, abs(cube_mass - DEFAULT_MASS) < CLOSE_ENOUGH)
|
||||
|
||||
# 5) Set a new values for Linear damping, Angular Damping and Mass of PhysxRigidBody and validate it
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearDamping", cube_id, NEW_LINEAR_DAMPING)
|
||||
cube_linear_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearDamping", cube_id)
|
||||
Report.result(Tests.linear_damp, abs(cube_linear_damping - NEW_LINEAR_DAMPING) < CLOSE_ENOUGH)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetAngularDamping", cube_id, NEW_ANGULAR_DAMPING)
|
||||
cube_angular_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularDamping", cube_id)
|
||||
Report.result(Tests.angular_damp, abs(cube_angular_damping - NEW_ANGULAR_DAMPING) < CLOSE_ENOUGH)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetMass", cube_id, NEW_MASS)
|
||||
cube_mass = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", cube_id)
|
||||
Report.result(Tests.mass, abs(cube_mass - NEW_MASS) < CLOSE_ENOUGH)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_AddRigidBodyComponent)
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
# Test case ID : C4976200
|
||||
# Test Case Title : Verify that with higher angular damping, the object in rotation comes to rest faster
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
low_find_bar = ("Find bar low", "Failed to find bar low")
|
||||
medium_find_bar = ("Find bar medium", "Failed to find bar medium")
|
||||
high_find_bar = ("Find bar high", "Failed to find bar high")
|
||||
low_no_translation_change = ("The low bar had no tanslation change", "The low bar did have a translation change")
|
||||
medium_no_translation_change = ("The medium bar had no tanslation change", "The medium bar did have a translation change")
|
||||
high_no_translation_change = ("The high bar had no tanslation change", "The high bar did have a translation change")
|
||||
low_trigger_a = ("LowBarTriggerA triggered by the low bar", "LowBarTriggerA not triggered by the low bar")
|
||||
low_trigger_b = ("LowBarTriggerB not triggered by the low bar", "LowBarTriggerB triggered by the low bar")
|
||||
medium_trigger_a = ("MediumTriggerA triggered by the medium bar", "MediumTriggerA not triggered by the medium bar")
|
||||
medium_trigger_b = ("MediumTriggerB not triggered by the medium bar", "MediumTriggerB triggered by the medium bar")
|
||||
high_trigger_a = ("HighTriggerA not triggered by the high bar", "HighTriggerA triggered by the high bar")
|
||||
high_trigger_b = ("HighTriggerB not triggered by the high bar", "HighTriggerB triggered by the high bar")
|
||||
low_rotation = ("The low bar rotated only on the x axis", "The low bar did not rotate only on the x axis")
|
||||
medium_rotation = ("The medium bar rotated only on the x axis", "The medium bar did not only rotate on the x axis")
|
||||
high_rotation = ("The high bar did not rotate", "The high bar did rotate")
|
||||
comparative_rotation = ("The rotation decreased with increased angular damping", "The rotation din not decrease with increased angular damping")
|
||||
timeout = ("All spheres rotation IsClose to 0 before timeout", "All spheres rotation are not IsClose to 0 before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_AngularDampingAffectsRotation():
|
||||
"""
|
||||
The level consists of a PhysX Terrain component that is not interacted with (per the test case)
|
||||
3 PhysX colliders with shape box, PhysX rigid bodies physics, and mesh with shape box.
|
||||
In the test we use the term bar - this means a box shape changed to be a rectangle, think thin plank.
|
||||
The bar shape is chosen for debugging, it is easy to see rotation.
|
||||
LowBar has 2 associated triggers LowBarTriggerA and LowBarTriggerB
|
||||
- LowBarTriggerA should be tripped to measure the rotation of the bar
|
||||
- LowBarTriggerB should not be tripped to measure that the rotation does not start increasing unexpectedly
|
||||
MediumBar has 2 associated triggers
|
||||
- MediumBarTriggerA should be tripped to measure the rotation of the bar
|
||||
- MediumBarTriggerB should not be tripped to measure that the rotation does not start increasing unexpectedly
|
||||
HighBar has 2 associated triggers
|
||||
- HighBarTriggerA is above the bar and should not be tripped
|
||||
- HighBarTriggerB is below the bar and should not be tripped
|
||||
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find bars, set bars attributes, and measure initial position
|
||||
4) Awaken bars
|
||||
5) Measure
|
||||
6) Report results
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
from editor_python_test_tools.utils import AngleHelper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
class Bar:
|
||||
def __init__(
|
||||
self, name, initial_angular_velocity, angular_damping, triggers, find_bar_test, no_translation_change_test
|
||||
):
|
||||
self.name = name
|
||||
self.initial_angular_velocity = initial_angular_velocity
|
||||
self.angular_damping = angular_damping
|
||||
self.entity = None
|
||||
self.initial_position = None
|
||||
self.final_position = None
|
||||
self.initial_rotation = None
|
||||
self.final_rotation = None
|
||||
self.timeout = False
|
||||
self.triggers = triggers
|
||||
self.find_bar_test = find_bar_test
|
||||
self.no_translation_change_test = no_translation_change_test
|
||||
|
||||
def find_trigger(self, name):
|
||||
return next(t for t in self.triggers if t.name == name)
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
name = {}
|
||||
angular_damping = {}
|
||||
timeout = {}
|
||||
""".format(
|
||||
self.name, self.angular_damping, self.timeout
|
||||
)
|
||||
|
||||
def report(self):
|
||||
Report.info(self)
|
||||
Report.info(
|
||||
"Initial angular velocity {} for {}".format(self.initial_angular_velocity.GetLength(), self.name)
|
||||
)
|
||||
Report.info_vector3(self.initial_position, "Initial position {}".format(self.name))
|
||||
if self.final_position:
|
||||
Report.info_vector3(self.final_position, "Final position {}".format(self.name))
|
||||
Report.info_vector3(self.initial_rotation, "Initial rotation {}".format(self.name))
|
||||
if self.final_rotation:
|
||||
Report.info_vector3(self.final_rotation, "Final rotation {}".format(self.name))
|
||||
for trigger in self.triggers:
|
||||
Report.info(trigger)
|
||||
|
||||
class Trigger:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.entity = None
|
||||
self.handler = None
|
||||
self.triggering_entity = None
|
||||
self.triggered = False
|
||||
|
||||
def on_trigger(self, args):
|
||||
self.triggered = True
|
||||
self.triggering_entity = args[0]
|
||||
Report.info("{} was triggered by {}".format(self.name, self.triggering_entity_name()))
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
name = {}
|
||||
triggering entity name = {}
|
||||
triggered = {}
|
||||
""".format(
|
||||
self.name, self.triggering_entity_name(), self.triggered
|
||||
)
|
||||
|
||||
def triggering_entity_name(self):
|
||||
if self.triggering_entity:
|
||||
return azlmbr.entity.GameEntityContextRequestBus(
|
||||
azlmbr.bus.Broadcast, "GetEntityName", self.triggering_entity
|
||||
)
|
||||
return None
|
||||
|
||||
INITIAL_ANGULAR_VELOCITY = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
ZERO_ANGULAR_VELOCITY = lymath.Vector3(0.0, 0.0, 0.0)
|
||||
TOLERANCE = 0.01
|
||||
TIMEOUT = 2.0
|
||||
LOW_TRIGGER_A = "LowBarTriggerA"
|
||||
LOW_TRIGGER_B = "LowBarTriggerB"
|
||||
MEDIUM_TRIGGER_A = "MediumBarTriggerA"
|
||||
MEDIUM_TRIGGER_B = "MediumBarTriggerB"
|
||||
HIGH_TRIGGER_A = "HighBarTriggerA"
|
||||
HIGH_TRIGGER_B = "HighBarTriggerB"
|
||||
|
||||
# bars
|
||||
# fmt: off
|
||||
low_damping = Bar(
|
||||
"LowBar",
|
||||
INITIAL_ANGULAR_VELOCITY,
|
||||
5.0,
|
||||
[Trigger(LOW_TRIGGER_A), Trigger(LOW_TRIGGER_B)],
|
||||
Tests.low_find_bar,
|
||||
Tests.low_no_translation_change,
|
||||
)
|
||||
medium_damping = Bar(
|
||||
"MediumBar",
|
||||
INITIAL_ANGULAR_VELOCITY,
|
||||
10.0,
|
||||
[Trigger(MEDIUM_TRIGGER_A), Trigger(MEDIUM_TRIGGER_B)],
|
||||
Tests.medium_find_bar,
|
||||
Tests.medium_no_translation_change,
|
||||
)
|
||||
high_damping = Bar(
|
||||
"HighBar",
|
||||
INITIAL_ANGULAR_VELOCITY,
|
||||
100.0,
|
||||
[Trigger(HIGH_TRIGGER_A), Trigger(HIGH_TRIGGER_B)],
|
||||
Tests.high_find_bar,
|
||||
Tests.high_no_translation_change,
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
bars = [low_damping, medium_damping, high_damping]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_AngularDampingAffectsRotation")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
# 3) Find bars, set bars attributes, and measure initial position
|
||||
for bar in bars:
|
||||
bar.entity = general.find_game_entity(bar.name)
|
||||
Report.critical_result(bar.find_bar_test, bar.entity.IsValid())
|
||||
azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "SetAngularVelocity", bar.entity, bar.initial_angular_velocity
|
||||
)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetAngularDamping", bar.entity, bar.angular_damping)
|
||||
general.idle_wait_frames(1) # wait one frame for changes to apply
|
||||
bar.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", bar.entity)
|
||||
bar.initial_rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", bar.entity)
|
||||
# add handler for each trigger
|
||||
for trigger in bar.triggers:
|
||||
trigger.entity = general.find_game_entity(trigger.name)
|
||||
trigger.handler = azlmbr.bus.NotificationHandler("TriggerNotificationBus")
|
||||
trigger.handler.connect(trigger.entity)
|
||||
trigger.handler.add_callback("OnTriggerEnter", trigger.on_trigger)
|
||||
|
||||
def all_bars_stopped_rotating():
|
||||
bar_results = [False for bar in bars]
|
||||
for i, bar in enumerate(bars):
|
||||
bar_angular_velocity = azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "GetAngularVelocity", bar.entity
|
||||
)
|
||||
if bar_angular_velocity.IsClose(ZERO_ANGULAR_VELOCITY, TOLERANCE):
|
||||
bar.final_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", bar.entity)
|
||||
bar.final_rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", bar.entity)
|
||||
bar_results[i] = True
|
||||
return all(bar_results)
|
||||
|
||||
# 4) Awaken bars
|
||||
for bar in bars:
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", bar.entity)
|
||||
|
||||
# 5) Measure
|
||||
if not helper.wait_for_condition(all_bars_stopped_rotating, TIMEOUT):
|
||||
bar.timeout = True
|
||||
|
||||
# 6) Report results
|
||||
no_timeout = True
|
||||
for bar in bars:
|
||||
if bar.timeout:
|
||||
Report.info(
|
||||
"Timeout occurred. bar with damping = {} and initial angular velocity = {} did not stop rotating in the timeout of {}".format(
|
||||
bar.angular_damping, bar.initial_angular_velocity, TIMEOUT
|
||||
)
|
||||
)
|
||||
no_timeout = False
|
||||
|
||||
# fast fail if timeout occurred, comparisons will be meaningless and all info is in log to determine which bar(s) timed out
|
||||
Report.critical_result(Tests.timeout, no_timeout)
|
||||
# no translation
|
||||
for bar in bars:
|
||||
Report.result(bar.no_translation_change_test, bar.initial_position.IsClose(bar.final_position, TOLERANCE))
|
||||
# rotation (comparisons are safe because our test setup and triggers ensure the bar rotates < 360 degrees)
|
||||
# if a bar rotates to exactly the expected position + n(360), the triggers will fail the test
|
||||
outcome = (
|
||||
low_damping.final_rotation.x > low_damping.initial_rotation.x
|
||||
and AngleHelper.is_angle_close_deg(low_damping.initial_rotation.y, low_damping.final_rotation.y, TOLERANCE)
|
||||
and AngleHelper.is_angle_close_deg(low_damping.initial_rotation.z, low_damping.final_rotation.z, TOLERANCE)
|
||||
)
|
||||
Report.result(Tests.low_rotation, outcome)
|
||||
outcome = (
|
||||
medium_damping.final_rotation.x > medium_damping.initial_rotation.x
|
||||
and AngleHelper.is_angle_close_deg(
|
||||
medium_damping.initial_rotation.y, medium_damping.final_rotation.y, TOLERANCE
|
||||
)
|
||||
and AngleHelper.is_angle_close_deg(
|
||||
medium_damping.initial_rotation.z, medium_damping.final_rotation.z, TOLERANCE
|
||||
)
|
||||
)
|
||||
Report.result(Tests.medium_rotation, outcome)
|
||||
# we do not need worry about is_angle_close here because we expect this to not change at all
|
||||
# if it rotates 360, the triggers will fail the test
|
||||
Report.result(Tests.high_rotation, high_damping.initial_rotation.IsClose(high_damping.final_rotation, TOLERANCE))
|
||||
# comparative rotation (comparisons are safe because our test setup and triggers ensure the bar rotates < 360 degrees)
|
||||
# if the bars rotate to exactly the expected position + n(360), the triggers will fail the test
|
||||
Report.result(
|
||||
Tests.comparative_rotation,
|
||||
high_damping.final_rotation.x < medium_damping.final_rotation.x < low_damping.final_rotation.x,
|
||||
)
|
||||
# triggers (quantitative measure of rotation)
|
||||
Report.result(Tests.low_trigger_a, low_damping.find_trigger(LOW_TRIGGER_A).triggered)
|
||||
Report.result(Tests.low_trigger_b, not low_damping.find_trigger(LOW_TRIGGER_B).triggered)
|
||||
Report.result(Tests.medium_trigger_a, medium_damping.find_trigger(MEDIUM_TRIGGER_A).triggered)
|
||||
Report.result(Tests.medium_trigger_b, not medium_damping.find_trigger(MEDIUM_TRIGGER_B).triggered)
|
||||
Report.result(Tests.high_trigger_a, not high_damping.find_trigger(HIGH_TRIGGER_A).triggered)
|
||||
Report.result(Tests.high_trigger_b, not high_damping.find_trigger(HIGH_TRIGGER_B).triggered)
|
||||
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_AngularDampingAffectsRotation)
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976209
|
||||
# Test Case Title : Verify that when Compute COM is enabled, the PhysX system computes the COM of the object on its own
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
|
||||
find_enabled_sphere = ("Enabled Sphere Found", "Enabled Sphere not found")
|
||||
find_enabled_capsule = ("Enabled Capsule Found", "Enabled Capsule not found")
|
||||
find_enabled_box = ("Enabled Box Found", "Enabled Box not found")
|
||||
find_enabled_physics_asset = ("Enabled Physics Asset Found", "Enabled Physics Asset not found")
|
||||
|
||||
find_disabled_sphere = ("Disabled Sphere Found", "Disabled Sphere not found")
|
||||
find_disabled_capsule = ("Disabled Capsule Found", "Disabled Capsule not found")
|
||||
find_disabled_box = ("Disabled Box Found", "Disabled Box not found")
|
||||
find_disabled_physics_asset = ("Disabled Physics Asset Found", "Disabled Physics Asset not found")
|
||||
|
||||
enabled_sphere_COM_expected = ("Enabled Sphere COM was close to expected value", "Enabled Sphere COM was not close to expected value")
|
||||
enabled_capsule_COM_expected = ("Enabled Capsule COM was close to expected value", "Enabled Capsule COM was not close to expected value")
|
||||
enabled_box_COM_expected = ("Enabled Box COM was close to expected value", "Enabled Box COM was not close to expected value")
|
||||
enabled_physics_asset_COM_expected = ("Enabled Physics Asset COM was close to expected value", "Enabled Physics Asset COM was not close to expected value")
|
||||
|
||||
disabled_sphere_COM_expected = ("Disabled Sphere COM was close to expected value", "Disabled Sphere COM was not close to expected value")
|
||||
disabled_capsule_COM_expected = ("Disabled Capsule COM was close to expected value", "Disabled Capsule COM was not close to expected value")
|
||||
disabled_box_COM_expected = ("Disabled Box COM was close to expected value", "Disabled Box not COM was not close to expected value")
|
||||
disabled_physics_asset_COM_expected = ("Disabled Physics Asset COM was close to expected value", "Disabled Physics Asset not COM was close to expected value")
|
||||
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_COM_ComputingWorks():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure when compute COM is enabled, that the COM is automatically calculated.
|
||||
|
||||
Level Description:
|
||||
There are 4 sets of entities:
|
||||
Spheres (entity: SphereEnabled), (entity: SphereDisabled)
|
||||
Boxes (entity: BoxEnabled), (entity: BoxDisabled)
|
||||
Capsules (entity: CapsuleEnabled), (entity: CapsuleDisabled)
|
||||
Physics Assets with sedan mesh (entity: PhysicsAssetEnabled), (entity: PhysicsAssetDisabled)
|
||||
|
||||
Each set has two entities, both of which are identical to one another - save for the Compute COM setting on their
|
||||
rigid body. One has it set to enabled, and the other has it set to disabled.
|
||||
|
||||
Each entity has a rigid body and two PhysX colliders. The first collider has no offset. The second collider is
|
||||
positioned +2 units in the X direction.
|
||||
|
||||
Expected Behavior:
|
||||
The entities with 'Compute COM' disabled will have local COM offsets at the objects origin ((0.0, 0.0, 0.0) Locally)
|
||||
The entities with 'Compute COM' enabled will have local COM offsets at the average position between the two
|
||||
colliders.
|
||||
For every entity that isn't a physics asset, that's +1 unit to the right (1/2 the offset value of the second
|
||||
collider). The physics asset has a mesh whose origin is not at the natural center of mass of the object,
|
||||
so we check its COM offset as a special case.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
4) Check center of mass for each entity
|
||||
5) Special Case: Check center of mass for each physics asset
|
||||
6) Exit game mode
|
||||
7) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
- Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: (None)
|
||||
"""
|
||||
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
CONTROL_COM = lymath.Vector3(0.0, 0.0, 0.0)
|
||||
EXPECTED_COM = lymath.Vector3(1.0, 0.0, 0.0)
|
||||
PHYSICS_ASSET_EXPECTED_COM = lymath.Vector3(1.0, -0.08, 0.43) # Derived from the sedan mesh specifically
|
||||
CLOSE_THRESHOLD = 0.01
|
||||
|
||||
class Shape:
|
||||
def __init__(self, name, valid_test, com_valid_test):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.center_of_mass = self.get_com()
|
||||
self.position = self.get_position()
|
||||
self.com_local = self.center_of_mass.Subtract(self.position)
|
||||
self.valid_test = valid_test
|
||||
self.com_valid_test = com_valid_test
|
||||
|
||||
def get_com(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetCenterOfMassWorld", self.id)
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_COM_ComputingWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate entities
|
||||
sphere_enabled = Shape("SphereEnabled", Tests.find_enabled_sphere, Tests.enabled_sphere_COM_expected)
|
||||
sphere_disabled = Shape("SphereDisabled", Tests.find_disabled_sphere, Tests.disabled_sphere_COM_expected)
|
||||
|
||||
capsule_enabled = Shape("CapsuleEnabled", Tests.find_enabled_capsule, Tests.enabled_capsule_COM_expected)
|
||||
capsule_disabled = Shape("CapsuleDisabled", Tests.find_disabled_capsule, Tests.disabled_capsule_COM_expected)
|
||||
|
||||
box_enabled = Shape("BoxEnabled", Tests.find_enabled_box, Tests.enabled_box_COM_expected)
|
||||
box_disabled = Shape("BoxDisabled", Tests.find_disabled_box, Tests.disabled_box_COM_expected)
|
||||
|
||||
physics_asset_enabled = Shape(
|
||||
"PhysicsAssetEnabled", Tests.find_enabled_physics_asset, Tests.enabled_physics_asset_COM_expected
|
||||
)
|
||||
physics_asset_disabled = Shape(
|
||||
"PhysicsAssetDisabled", Tests.find_disabled_physics_asset, Tests.disabled_physics_asset_COM_expected
|
||||
)
|
||||
|
||||
# physics asset is a special case, and thus not included
|
||||
enabled_shapes = [sphere_enabled, capsule_enabled, box_enabled]
|
||||
disabled_shapes = [sphere_disabled, capsule_disabled, box_disabled]
|
||||
|
||||
all_shapes = enabled_shapes + disabled_shapes + [physics_asset_enabled, physics_asset_disabled]
|
||||
|
||||
for shape in all_shapes:
|
||||
Report.critical_result(shape.valid_test, shape.id.IsValid())
|
||||
|
||||
# 4) Check center of mass for each entity
|
||||
for enabled_shape in enabled_shapes:
|
||||
enabled_is_close = enabled_shape.com_local.IsClose(EXPECTED_COM, CLOSE_THRESHOLD)
|
||||
Report.result(enabled_shape.com_valid_test, enabled_is_close)
|
||||
|
||||
for disabled_shape in disabled_shapes:
|
||||
disabled_is_close = disabled_shape.com_local.IsClose(CONTROL_COM, CLOSE_THRESHOLD)
|
||||
Report.result(disabled_shape.com_valid_test, disabled_is_close)
|
||||
|
||||
# 5) Check center of mass for each physics asset
|
||||
enabled_is_close = physics_asset_enabled.com_local.IsClose(PHYSICS_ASSET_EXPECTED_COM, CLOSE_THRESHOLD)
|
||||
print(f"{physics_asset_enabled.com_local.x}, {physics_asset_enabled.com_local.y}, {physics_asset_enabled.com_local.z}")
|
||||
Report.result(physics_asset_enabled.com_valid_test, enabled_is_close)
|
||||
|
||||
disabled_is_close = physics_asset_disabled.com_local.IsClose(CONTROL_COM, CLOSE_THRESHOLD)
|
||||
Report.result(physics_asset_disabled.com_valid_test, disabled_is_close)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_COM_ComputingWorks)
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976210
|
||||
# Test Case Title : Verify that when Compute COM is disabled, the user gets an option to add the co-ordinates of
|
||||
# the COM and the COM gets implemented at those co-ordinates.
|
||||
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
boxes_validated = ("All box entities were found and validated", "Couldn't find or validate at least one box")
|
||||
test_objects_validated = ("All test entities were found and validated", "Not all test entities could be found and validated")
|
||||
sphere_com_set = ("The Test Sphere's COM was successfully set", "The Test Sphere's COM WAS NOT successfully set")
|
||||
capsule_com_set = ("The Test Capsule's COM was successfully set", "The Test Capsule's COM WAS NOT successfully set")
|
||||
box_com_set = ("The Test Box's COM was successfully set", "The Test Box's COM WAS NOT successfully set")
|
||||
sphere_confirm = ("The Test Sphere rolled uphill due to COM offset", "The Test Sphere rolled downhill-- COM did not work as expected")
|
||||
capsule_confirm = ("The Test Capsule fell against gravity due to COM offset", "The Test Capsule fell with gravity-- COM did not work as expected")
|
||||
box_confirm = ("The Test Box fell against gravity due to COM offset", "The Test Box fell with gravity-- COM did not work as expected")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_COM_ManualSettingWorks():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Tests that when an entity has "Compute COM" disabled, a user can set a custom center of mass and that
|
||||
the set center of mass behaves as expected.
|
||||
|
||||
Level Description:
|
||||
Three test objects (Box, Capsule and Sphere) are positioned on a platform that is rotated at an acute angle. Two
|
||||
plates (Pass and Fail) are perpendicular to the platform so that the Fail Plate is underneath the test objects, and
|
||||
the Pass Plate is above. Gravity is enabled for all test objects. Stationary objects (plates, platform... etc)
|
||||
have gravity disabled and have masses set for "very large numbers" (10,000 kg) to resist the test objects'
|
||||
movements. Test objects have their "compute COM" property disabled, but in the editor have a computed COM of
|
||||
(0.0, 0.0, 0.0) and gravity enabled.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode starts, the script manually sets each test objects center of mass in a way where that entity
|
||||
will "fall against gravity". The box and capsule should tilt against gravity into the Pass Plate, and the sphere
|
||||
should "roll up hill" to the Pass Plate.
|
||||
|
||||
Test Steps:
|
||||
0) Define data and functions
|
||||
1) Open level, enter game mode
|
||||
2) Find and validate entities
|
||||
3) set and verify center of mass (COM)
|
||||
4) Wait for results or timeout
|
||||
5) Log results
|
||||
6) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# internal editor imports
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
import azlmbr.math as azmath
|
||||
|
||||
# 0) Set up data and functions used in the test
|
||||
|
||||
# EntityData class for storing and organizing useful test information/results
|
||||
class EntityData:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.init_pos = None
|
||||
self.current_pos = None
|
||||
self.init_rot = None
|
||||
self.current_rot = None
|
||||
self.result = None
|
||||
self.fail_info = None
|
||||
|
||||
# ***** Global variables ****
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 1.5
|
||||
CAPSULE_BOX_OFFSET = azmath.Vector3(3.0, 0.0, 0.0)
|
||||
SPHERE_OFFSET = azmath.Vector3(0.5, 0.0, 0.0)
|
||||
PASS = "pass"
|
||||
FAIL = "fail"
|
||||
|
||||
# Test entities
|
||||
test_box = EntityData("Test_Box")
|
||||
test_capsule = EntityData("Test_Capsule")
|
||||
test_sphere = EntityData("Test_Sphere")
|
||||
test_entities = [test_box, test_capsule, test_sphere]
|
||||
|
||||
# Plate entities
|
||||
pass_plate = EntityData("Pass_Plate")
|
||||
fail_plate = EntityData("Fail_Plate")
|
||||
|
||||
# All non-moving entities
|
||||
non_movers = [EntityData("Platform"), EntityData("Pass_Buffer"), EntityData("Fail_Buffer"), pass_plate, fail_plate]
|
||||
|
||||
# Full entity list
|
||||
all_entities = non_movers + test_entities
|
||||
|
||||
# ******** Helper Functions ********
|
||||
|
||||
# Attempts to set COM, and manually computes expected COM to verify it was applied correctly
|
||||
def set_and_validate_COM(entity, com_offset):
|
||||
# type: (EntityData, Vector3) -> None
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.01
|
||||
# Set COM offset
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetCenterOfMassOffset", entity.id, com_offset)
|
||||
entity_world_com_pos = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetCenterOfMassWorld", entity.id)
|
||||
# Get world rotation matrix
|
||||
tm_matrix = azmath.Matrix3x3_CreateFromTransform(
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", entity.id)
|
||||
)
|
||||
# Calculate expected world COM
|
||||
world_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity.id)
|
||||
calculated_com = tm_matrix.MultiplyVector3(com_offset).Add(world_pos)
|
||||
# If expected COM and actual COM differ, log data collected
|
||||
if not entity_world_com_pos.IsClose(calculated_com, CLOSE_ENOUGH_THRESHOLD):
|
||||
Report.info("COM not applied to entity: {}".format(entity.name))
|
||||
Report.info_vector3(world_pos, " Entity position:")
|
||||
Report.info_vector3(com_offset, " COM offset:")
|
||||
Report.info_vector3(entity_world_com_pos, " Entity world COM:")
|
||||
Report.info_vector3(calculated_com, " Expected COM")
|
||||
return False
|
||||
return True
|
||||
|
||||
# ** Entity batch operation helpers **
|
||||
|
||||
# Attempts to validate entities' IDs and initial positions and initial rotations.
|
||||
# Returns True if all entities were successfully validated.
|
||||
# Print to the log if there are any problems retrieving vital information
|
||||
def validate_entities(entity_list):
|
||||
# type: ([EntityData]) -> int
|
||||
count = 0
|
||||
for entity in entity_list:
|
||||
valid = True
|
||||
entity.id = general.find_game_entity(entity.name)
|
||||
if not entity.id.IsValid():
|
||||
valid = False
|
||||
Report.info("Entity: {} could not be validated".format(entity.name))
|
||||
entity.init_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity.id)
|
||||
entity.current_pos = entity.init_pos
|
||||
if entity.init_pos is None or entity.init_pos.IsZero():
|
||||
valid = False
|
||||
Report.info("Entity: {}'s initial position could not be found".format(entity.name))
|
||||
entity.init_rot = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", entity.id)
|
||||
entity.current_rot = entity.init_rot
|
||||
if entity.init_rot is None:
|
||||
valid = False
|
||||
Report.info("Entity: {}'s initial rotation could not be found".format(entity.name))
|
||||
if valid:
|
||||
count += 1
|
||||
return count == len(entity_list)
|
||||
|
||||
# Updates entities' current position
|
||||
def update_pos_and_rot(entity_list):
|
||||
# type: ([EntityData]) -> None
|
||||
for entity in entity_list:
|
||||
entity.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity.id)
|
||||
entity.current_rot = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", entity.id)
|
||||
|
||||
# Checks for unexpected movement in stationary objects
|
||||
# Prints to the log if there is a difference between initial position and current position
|
||||
def check_for_unexpected_movement(entity_list):
|
||||
# type: ([EntityData]) -> None
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.1
|
||||
for entity in entity_list:
|
||||
if not entity.init_pos.IsClose(entity.current_pos, CLOSE_ENOUGH_THRESHOLD):
|
||||
entity.result = FAIL
|
||||
entity.fail_info = "Unexpected movement detected"
|
||||
|
||||
# Checks if we have enough information to end the test
|
||||
def done_collecting_results(entity_list, num_results):
|
||||
# type: ([EntityData], int) -> bool
|
||||
result_count = 0
|
||||
for entity in entity_list:
|
||||
if entity.result is not None:
|
||||
result_count += 1
|
||||
|
||||
# when all spheres have a result we are done
|
||||
if result_count == num_results:
|
||||
return True
|
||||
return False
|
||||
|
||||
# ****** single update function to pass ******
|
||||
# to helper.wait_for_condition()
|
||||
|
||||
# Should be called every frame:
|
||||
# Updates all entities positions and rotations
|
||||
# Returns True if exit conditions for test are met
|
||||
# see @done_collecting_results(..) for exit conditions
|
||||
def is_done_updating():
|
||||
# type: () -> bool
|
||||
update_pos_and_rot(all_entities)
|
||||
check_for_unexpected_movement(non_movers)
|
||||
return done_collecting_results(test_entities, len(test_entities))
|
||||
|
||||
# ******** Event Handlers ********
|
||||
|
||||
# Fail A Box collision event handler
|
||||
def on_collision_begin_fail(args):
|
||||
# type: ([EntityId, ...]) -> None
|
||||
collider_id = args[0]
|
||||
for entity in test_entities:
|
||||
if entity.id.Equal(collider_id) and entity.result is None:
|
||||
entity.result = FAIL
|
||||
entity.fail_info = "Collide with fail plate: COM did not apply properly"
|
||||
return
|
||||
|
||||
# Pass Box Collision Event Handler
|
||||
def on_collision_begin_pass(args):
|
||||
# type: ([EntityId, ...]) -> None
|
||||
collider_id = args[0]
|
||||
for entity in test_entities:
|
||||
if entity.id.Equal(collider_id) and entity.result is None:
|
||||
Report.info("Entity: {} collided with the Pass Plate".format(entity.name))
|
||||
entity.result = PASS
|
||||
return
|
||||
# it wasn't a test entity that collided, something went wrong
|
||||
if collider_id.IsValid():
|
||||
entity_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", collider_id)
|
||||
Report.info("Pass Plate collided with unexpected entity: {}".format(entity_name))
|
||||
|
||||
# ******** Execution Code *********
|
||||
|
||||
# 1) Open level and start game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "RigidBody_COM_ManualSettingWorks")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Find and validate test entities
|
||||
Report.critical_result(Tests.test_objects_validated, validate_entities(test_entities))
|
||||
Report.critical_result(Tests.boxes_validated, validate_entities(non_movers))
|
||||
|
||||
# 3) Set and verify COM offsets
|
||||
Report.critical_result(Tests.box_com_set, set_and_validate_COM(test_box, CAPSULE_BOX_OFFSET))
|
||||
Report.critical_result(Tests.capsule_com_set, set_and_validate_COM(test_capsule, CAPSULE_BOX_OFFSET))
|
||||
Report.critical_result(Tests.sphere_com_set, set_and_validate_COM(test_sphere, SPHERE_OFFSET))
|
||||
|
||||
# Assign handlers
|
||||
pass_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
pass_handler.connect(pass_plate.id)
|
||||
pass_handler.add_callback("OnCollisionBegin", on_collision_begin_pass)
|
||||
|
||||
fail_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
fail_handler.connect(fail_plate.id)
|
||||
fail_handler.add_callback("OnCollisionBegin", on_collision_begin_fail)
|
||||
|
||||
# 4) wait for either time out or for the test to complete
|
||||
if not helper.wait_for_condition(is_done_updating, TIME_OUT):
|
||||
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
|
||||
|
||||
# 5) Log results
|
||||
|
||||
# verify entities results
|
||||
Report.result(Tests.box_confirm, test_box.result is not None and test_box.result == PASS)
|
||||
Report.result(Tests.capsule_confirm, test_capsule.result is not None and test_capsule.result == PASS)
|
||||
Report.result(Tests.sphere_confirm, test_sphere.result is not None and test_sphere.result == PASS)
|
||||
|
||||
# Data dump at bottom of log
|
||||
Report.info("******** Collected Data *********")
|
||||
for entity in all_entities:
|
||||
Report.info("Entity: {}".format(entity.name))
|
||||
Report.info_vector3(entity.init_pos, " Initial position:")
|
||||
Report.info_vector3(entity.current_pos, " Final position:")
|
||||
Report.info_vector3(entity.init_rot, " Initial rotation:")
|
||||
Report.info_vector3(entity.current_rot, " Final rotation:")
|
||||
Report.info(" Result: {}".format(entity.result))
|
||||
if entity.fail_info is not None:
|
||||
Report.info(" Fail info: {}".format(entity.fail_info))
|
||||
Report.info("********************************")
|
||||
|
||||
# 6) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
Report.info("*** FINISHED TEST ***")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_COM_ManualSettingWorks)
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C13351703
|
||||
# Test Case Title : Check that Center of Mass calculations should not include trigger shapes
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_entities = ("Entities are found", "Entities are not found")
|
||||
com_expected = ("COM value is equal to expected value", "COM value is not equal to expected value")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_COM_NotIncludesTriggerShapes():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that Center of Mass calculations should not include trigger shapes.
|
||||
|
||||
Level Description:
|
||||
RigidBody (entity) - Entity with 1 Rigid Body component and 2 PhysX Collider components
|
||||
Rigid Body Component - Debug Draw Collider, Compute COM are enabled, Gravity is disabled
|
||||
1st Collider - Offset(-1.0, 0.0, 0.0) - Trigger enabled
|
||||
2nd Collider - Offset(1.0, 0.0, 0.0) - Trigger disabled
|
||||
|
||||
Expected Behavior:
|
||||
We are checking if the entity is valid.
|
||||
We are verifying if the center of mass is close to the collider whose trigger has been disabled.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Validate if COM is same as expected value
|
||||
5) Exit game mode
|
||||
6) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test critical_results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
# Constants
|
||||
OFFSET = lymath.Vector3(1.0, 0.0, 0.0) # Offset of the trigger disabled sphere
|
||||
CLOSE_THRESHOLD = sys.float_info.epsilon
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_COM_NotIncludesTriggerShapes")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
rigid_body_id = general.find_game_entity("RigidBody")
|
||||
Report.critical_result(Tests.find_entities, rigid_body_id.IsValid())
|
||||
|
||||
# 4) Validate if COM is same as expected value
|
||||
entity_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", rigid_body_id)
|
||||
com = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetCenterOfMassWorld", rigid_body_id)
|
||||
Report.result(Tests.com_expected, entity_position.Add(OFFSET).IsClose(com, CLOSE_THRESHOLD))
|
||||
|
||||
# 5) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_COM_NotIncludesTriggerShapes)
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
# Test case ID : C4976218
|
||||
# Test Case Title: Verify that when compute inertia is checked, the physX engine does compute the inertia of the objects
|
||||
|
||||
# fmt: off
|
||||
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_upper_boxes = ("Upper Boxes found", "Upper Boxes not found")
|
||||
find_lower_boxes = ("Lower Boxes found", "Lower Boxes not found")
|
||||
boxes_collided = ("All Boxes Collided", "Not all Boxes Collided")
|
||||
upper_box_x_did_topple = ("Upper Box X did topple", "Upper Box X did not toppled")
|
||||
upper_box_y_did_topple = ("Upper Box Y did topple", "Upper Box Y did not toppled")
|
||||
upper_box_z_did_topple = ("Upper Box Z did topple", "Upper Box Z did not toppled")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
def RigidBody_ComputeInertiaWorks():
|
||||
"""
|
||||
Summary:
|
||||
Level Description:
|
||||
A box (entity: Upper Box) set above and askew to another box (entity: Lower Box)
|
||||
Lower box and Upper box has computed inertia.
|
||||
Test Steps:
|
||||
--> Open level
|
||||
--> Enter game mode
|
||||
--> Retrieve entities
|
||||
--> Check for collision between the top box and lower box for the 3 pairs (6 boxes total)
|
||||
--> Check that the upper box's x, y, or z angular velocity decreases past -1.0
|
||||
--> Exit game mode
|
||||
--> Close editor
|
||||
:return: None
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
class UpperBox:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.collided_with_lower_box = False
|
||||
self.handler = None
|
||||
self.lower_box_list = None
|
||||
|
||||
def on_boxes_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
for boxes in self.lower_box_list:
|
||||
if other_id.Equal(boxes.id):
|
||||
Report.info("{} hit lower box".format(self.name))
|
||||
self.collided_with_lower_box = True
|
||||
|
||||
def create_handler(self):
|
||||
self.handler = bus.NotificationHandler("CollisionNotificationBus")
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_boxes_collision_begin)
|
||||
|
||||
@property
|
||||
def angular_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetAngularVelocity", self.id)
|
||||
|
||||
class LowerBox:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
|
||||
# Amount of time before test times out
|
||||
TIME_OUT = 3.0
|
||||
MINIMUM_ANGUlAR_VELOCITY = -1.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_ComputeInertiaWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
upper_box_x = UpperBox("Upper Box X")
|
||||
upper_box_y = UpperBox("Upper Box Y")
|
||||
upper_box_z = UpperBox("Upper Box Z")
|
||||
|
||||
lower_box_x = LowerBox("Lower Box X")
|
||||
lower_box_y = LowerBox("Lower Box Y")
|
||||
lower_box_z = LowerBox("Lower Box Z")
|
||||
|
||||
# Store boxes in two lists
|
||||
upper_box_list = [upper_box_x, upper_box_y, upper_box_z]
|
||||
lower_box_list = [lower_box_x, lower_box_y, lower_box_z]
|
||||
|
||||
|
||||
upper_boxes_found = True
|
||||
lower_boxes_found = True
|
||||
|
||||
for upper_boxes in upper_box_list:
|
||||
upper_boxes.lower_box_list = lower_box_list
|
||||
if upper_boxes.id is None:
|
||||
upper_boxes_found = False
|
||||
|
||||
for lower_boxes in lower_box_list:
|
||||
if lower_boxes.id is None:
|
||||
lower_boxes_found = False
|
||||
|
||||
Report.critical_result(Tests.find_upper_boxes, upper_boxes_found)
|
||||
Report.critical_result(Tests.find_lower_boxes, lower_boxes_found)
|
||||
|
||||
for box in upper_box_list:
|
||||
box.create_handler()
|
||||
|
||||
def all_boxes_collided():
|
||||
for boxes in upper_box_list:
|
||||
if not boxes.collided_with_lower_box:
|
||||
return False
|
||||
return True
|
||||
|
||||
# 4) Wait for the boxes to collide
|
||||
boxes_collided = helper.wait_for_condition(all_boxes_collided, TIME_OUT)
|
||||
Report.result(Tests.boxes_collided, boxes_collided)
|
||||
|
||||
# 5) Check that the upper box's corresponding angular velocity is lower than minimum (higher negative number)
|
||||
def validate_angular_velocities():
|
||||
velocities_set = True
|
||||
if upper_box_x.angular_velocity.x > MINIMUM_ANGUlAR_VELOCITY:
|
||||
velocities_set = False
|
||||
if upper_box_y.angular_velocity.y > MINIMUM_ANGUlAR_VELOCITY:
|
||||
velocities_set = False
|
||||
if upper_box_z.angular_velocity.z > MINIMUM_ANGUlAR_VELOCITY:
|
||||
velocities_set = False
|
||||
return velocities_set
|
||||
|
||||
helper.wait_for_condition(validate_angular_velocities, TIME_OUT)
|
||||
|
||||
# Checking to see if the X angular has a greater negative number (higher negative value) than MINIMUM
|
||||
Report.info("Angular Velocity for X Box = {}".format(upper_box_x.angular_velocity.x))
|
||||
Report.info("Angular Velocity for Y Box = {}".format(upper_box_y.angular_velocity.y))
|
||||
Report.info("Angular Velocity for Z Box = {}".format(upper_box_z.angular_velocity.z))
|
||||
|
||||
Report.result(Tests.upper_box_x_did_topple, upper_box_x.angular_velocity.x < MINIMUM_ANGUlAR_VELOCITY)
|
||||
Report.result(Tests.upper_box_y_did_topple, upper_box_y.angular_velocity.y < MINIMUM_ANGUlAR_VELOCITY)
|
||||
Report.result(Tests.upper_box_z_did_topple, upper_box_z.angular_velocity.z < MINIMUM_ANGUlAR_VELOCITY)
|
||||
|
||||
# 7) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_ComputeInertiaWorks)
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C100000
|
||||
# Test Case Title : Check that Gravity works
|
||||
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Entity Ball found", "Ball not found")
|
||||
gravity_started_disabled = ("Gravity started disabled", "Gravity didn't start disabled")
|
||||
gravity_set_enabled = ("Gravity has been enabled", "Gravity wasn't enabled")
|
||||
ball_fell = ("Ball fell", "Ball didn't fall")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt:on
|
||||
|
||||
|
||||
def RigidBody_EnablingGravityWorksPoC():
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "EnablingGravityWorks")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
general.idle_wait_frames(1)
|
||||
ball_id = general.find_game_entity("Ball")
|
||||
Report.critical_result(Tests.find_ball, ball_id.IsValid(), "Entity must be found")
|
||||
|
||||
# 4) Make sure gravity is off from the start
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
Report.critical_result(Tests.gravity_started_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Get the Z position before enabling the physics
|
||||
class Ball:
|
||||
z_start = None
|
||||
|
||||
Ball.z_start = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
|
||||
# 6) Activate gravity
|
||||
Report.info("Enabling Gravity")
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", ball_id)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", ball_id, True)
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
|
||||
def ball_fell():
|
||||
"""
|
||||
This is an example function to use with TestHelper.wait_for_condition
|
||||
It may take no parameters and it contains no wait_idle_* because that is
|
||||
already handled in TestHelper.wait_for_condition
|
||||
"""
|
||||
z_end = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
return z_end < 35
|
||||
|
||||
# 7) Validate ball fell by ensuring z is decreasing
|
||||
fell_down = helper.wait_for_condition(ball_fell, 10.0)
|
||||
Report.result(Tests.ball_fell, fell_down)
|
||||
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_EnablingGravityWorksPoC)
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
# Test case ID : C111111
|
||||
# Test Case Title : Check that Gravity works
|
||||
|
||||
# fmt:off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Entity Ball found", "Ball not found")
|
||||
find_terrain = ("Entity Terrain found", "Terrain not found")
|
||||
gravity_started_disabled = ("Gravity started disabled", "Gravity didn't start disabled")
|
||||
gravity_set_enabled = ("Gravity has been enabled", "Gravity wasn't enabled")
|
||||
ball_fell = ("Ball fell", "Ball didn't fall")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt:on
|
||||
|
||||
|
||||
def RigidBody_EnablingGravityWorksUsingNotificationsPoC():
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "EnablingGravityWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
ball_id = general.find_game_entity("Ball")
|
||||
Report.result(Tests.find_ball, ball_id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# 4) Make sure gravity is off from the start
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
Report.result(Tests.gravity_started_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Activate gravity
|
||||
Report.info("Enabling Gravity")
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", ball_id)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", ball_id, True)
|
||||
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
Report.result(Tests.gravity_set_enabled, gravity_enabled)
|
||||
|
||||
# 6) Listen to collision events seconds so it falls down
|
||||
|
||||
class TouchGround:
|
||||
value = False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(terrain_id):
|
||||
Report.info("Touched ground")
|
||||
TouchGround.value = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(ball_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
helper.wait_for_condition(lambda: TouchGround.value, 3.0)
|
||||
Report.result(Tests.ball_fell, TouchGround.value)
|
||||
|
||||
# 7) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_EnablingGravityWorksUsingNotificationsPoC)
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976197
|
||||
# Test Case Title : Verify that when you assign an Initial Angular Velocity to an object,
|
||||
# it moves with that Angular velocity when we switch to game mode
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
gravity_disabled = ("Gravity is disabled", "Gravity is not disabled")
|
||||
cube_found = ("Cube was found", "Cube was not found")
|
||||
trigger_found = ("Trigger was found", "Trigger was not found")
|
||||
cube_pos = ("Cube position is valid", "Cube position is not valid")
|
||||
trigger_pos = ("Trigger position is valid", "Trigger position is not valid")
|
||||
cube_init_rotation = ("Cube rotation is valid", "Cube rotation is not valid")
|
||||
cube_touched_trigger = ("Cube touched Trigger", "Cube did not touch Trigger")
|
||||
cube_rotated_on_x = ("Cube rotated on X axis", "Cube did not rotate on X axis")
|
||||
cube_not_rotated_on_y = ("Cube did not rotate on Y axis", "Cube rotated on Y axis")
|
||||
cube_not_rotated_on_z = ("Cube did not rotate on Z axis", "Cube rotated on Z axis")
|
||||
cube_velocity = ("The velocity is close to expected", "The velocity is not close to expected")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_InitialAngularVelocity():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure initial angular velocity of 5.0 rad/sec on X axis is exerted on a rigid body object
|
||||
|
||||
Level Description:
|
||||
A cube positioned above terrain, with PhysX Shape Collider component with Box shape (dimensions: x=1, y=1, z=3),
|
||||
and with PhysX Rigid Body component, gravity disabled, initial angular velocity: (x=5, y=0, z=0) rad/s and
|
||||
angular damping set to 0.0
|
||||
A trigger with PhysX Shape Collider component with Box shape (dimensions: x=1, y=1, z=1), trigger enabled,
|
||||
positioned above terrain close to the Cube but not touching.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the cube should rotate on x axis with 5 radian per second speed.
|
||||
The cube is supposed to touch the trigger as it rotates.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Retrieves test entities (Cube and Trigger)
|
||||
4) Checks if gravity is disabled
|
||||
5) Checks the Cube and Trigger locations
|
||||
6) Checks the Cube initial rotation
|
||||
7) Captures the angular velocity of cube
|
||||
8) Sets up Trigger
|
||||
9) Waits for the Cube to rotate and touch the Trigger
|
||||
10) Captures the Cube new rotation and reports results
|
||||
11) Exits game mode
|
||||
12) Closes the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: (None)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Cube:
|
||||
id = None
|
||||
position = None
|
||||
init_rotation = None
|
||||
rotation = None
|
||||
angular_velocity = None
|
||||
touched_trigger = False
|
||||
# We found no bus to retrieve physics rigid body initial angular velocity
|
||||
# So we are hardcoding 5 as its initial angular velocity
|
||||
INITIAL_ANGULAR_VELOCITY = 5 # radian per second on X axis
|
||||
|
||||
class Trigger:
|
||||
id = None
|
||||
position = None
|
||||
|
||||
# Constants
|
||||
INITIAL_ANGULAR_VELOCITY_TOLERANCE = 0.05
|
||||
TIME_OUT = 2.0
|
||||
ROTATION_TOLERANCE = 0.001
|
||||
|
||||
def is_angle_close(x, y):
|
||||
r = (math.sin(x) - math.sin(y)) * (math.sin(x) - math.sin(y)) + (math.cos(x) - math.cos(y)) * (
|
||||
math.cos(x) - math.cos(y)
|
||||
)
|
||||
diff = math.acos((2.0 - r) / 2.0)
|
||||
return abs(diff) <= ROTATION_TOLERANCE
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "RigidBody_InitialAngularVelocity")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
Cube.id = general.find_game_entity("Cube")
|
||||
Trigger.id = general.find_game_entity("Trigger")
|
||||
Report.critical_result(Tests.cube_found, Cube.id.IsValid())
|
||||
Report.critical_result(Tests.trigger_found, Trigger.id.IsValid())
|
||||
|
||||
# 4) Check gravity is disabled
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Cube.id)
|
||||
Report.result(Tests.gravity_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Log Cube and Trigger positions
|
||||
Cube.position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Cube.id)
|
||||
valid_cube_pos = (Cube.position is not None) and (not Cube.position.IsZero())
|
||||
Report.info_vector3(Cube.position, "Cube initial position:")
|
||||
Report.critical_result(Tests.cube_pos, valid_cube_pos)
|
||||
|
||||
Trigger.position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Trigger.id)
|
||||
valid_trigger_pos = (Trigger.position is not None) and (not Trigger.position.IsZero())
|
||||
Report.info_vector3(Trigger.position, "Trigger initial position:")
|
||||
Report.critical_result(Tests.trigger_pos, valid_trigger_pos)
|
||||
|
||||
# 6) Log Cube initial rotation
|
||||
Cube.init_rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", Cube.id)
|
||||
Report.info_vector3(Cube.init_rotation, "Cube initial rotation:")
|
||||
Report.critical_result(Tests.cube_init_rotation, (Cube.init_rotation is not None))
|
||||
|
||||
# 7) Log Cube angular velocity
|
||||
Cube.angular_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularVelocity", Cube.id)
|
||||
Report.info_vector3(Cube.angular_velocity, "Cube angular velocity:")
|
||||
valid_velocity = abs(Cube.angular_velocity.x - Cube.INITIAL_ANGULAR_VELOCITY) < INITIAL_ANGULAR_VELOCITY_TOLERANCE
|
||||
Report.result(Tests.cube_velocity, valid_velocity)
|
||||
|
||||
# 8) Set up Trigger
|
||||
def set_touched_value(args):
|
||||
entering_entity_id = args[0]
|
||||
if entering_entity_id.Equal(Cube.id):
|
||||
Report.info("Cube touched the Trigger")
|
||||
Cube.touched_trigger = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(Trigger.id)
|
||||
handler.add_callback("OnTriggerEnter", set_touched_value)
|
||||
|
||||
# 9) Wait a maximum of 2 seconds for the sphere to touch the trigger
|
||||
helper.wait_for_condition(lambda: Cube.touched_trigger, TIME_OUT)
|
||||
Report.result(Tests.cube_touched_trigger, Cube.touched_trigger)
|
||||
|
||||
# 10) Log Cube's new rotation and validates it
|
||||
Cube.rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", Cube.id)
|
||||
Report.info_vector3(Cube.rotation, "Cube new rotation:")
|
||||
Report.result(Tests.cube_rotated_on_x, not is_angle_close(Cube.rotation.x, Cube.init_rotation.x))
|
||||
Report.result(Tests.cube_not_rotated_on_y, is_angle_close(Cube.rotation.y, Cube.init_rotation.y))
|
||||
Report.result(Tests.cube_not_rotated_on_z, is_angle_close(Cube.rotation.z, Cube.init_rotation.z))
|
||||
|
||||
# 11) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_InitialAngularVelocity)
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976195
|
||||
# Test Case Title : Verify that when you assign an Initial Linear Velocity to an object,
|
||||
# ... it moves with that linear velocity when we switch to game mode.
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
entities_found = ("Entities found", "Entities not found")
|
||||
entities_pos = ("Entities positions are valid", "Entities positions are not valid")
|
||||
sphere_moved = ("Sphere moved correctly", "Sphere did not move correctly")
|
||||
sphere_velocity = ("The magnitude of velocity is close to 5", "The magnitude of velocity is not close to 5")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
def RigidBody_InitialLinearVelocity():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure intial linear velocity of 5.0 on X axis is exerted on a rigid body object.
|
||||
|
||||
Level Description:
|
||||
A sphere (entity: Sphere) positioned above terrain which is assigned with
|
||||
an initial linear velocity of 5.0 in x direction. The Sphere has gravity disabled.
|
||||
A trigger cube positioned on the same Y and Z but different X of the sphere.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the sphere should move on x direction with 5 m/s speed.
|
||||
The sphere is supposed to touch the trigger cube as it moves.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Retrieves test entities (Sphere and Trigger Cube)
|
||||
4) Ensures that the sphere and the cube are located
|
||||
5) Captures the velocity of sphere
|
||||
5.1) Sets up trigger cube
|
||||
6) Waits for the shpere to move and touch the trigger cube
|
||||
7) Captures the sphere new location
|
||||
8) Exits game mode
|
||||
9) Closes the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: (None)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "RigidBody_InitialLinearVelocity")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
trigger_cube_id = general.find_game_entity("TriggerCube")
|
||||
valid_entity_ids = (sphere_id.IsValid() and trigger_cube_id.IsValid())
|
||||
Report.critical_result(Tests.entities_found, valid_entity_ids)
|
||||
|
||||
# 4) Log sphere and cube initial position
|
||||
init_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
trigger_cube_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", trigger_cube_id)
|
||||
sphere_pos_found = ((init_sphere_pos is not None) and (not init_sphere_pos.IsZero()))
|
||||
trigger_cube_pos_found = ((trigger_cube_pos is not None) and (not trigger_cube_pos.IsZero()))
|
||||
pos_found = sphere_pos_found and trigger_cube_pos_found
|
||||
Report.critical_result(Tests.entities_pos, pos_found)
|
||||
Report.info_vector3(init_sphere_pos, "Sphere initial position:")
|
||||
|
||||
# 5) Log Sphere's velocity
|
||||
sphere_linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere_id)
|
||||
Report.info("Sphere linear velocity {}".format(sphere_linear_velocity.ToString()))
|
||||
sphere_linear_velocity_magnitude = sphere_linear_velocity.GetLength()
|
||||
Report.info("Magnitude = {}".format(sphere_linear_velocity_magnitude))
|
||||
# We found no bus to retrieve physics rigid body initial linear velocity
|
||||
# So we are hardcoding 5 as its initial linear velocity
|
||||
SPHERE_INITIAL_VELOCITY = 5
|
||||
outcome = abs(sphere_linear_velocity_magnitude - SPHERE_INITIAL_VELOCITY) < 0.05
|
||||
Report.result(Tests.sphere_velocity, outcome)
|
||||
|
||||
# 5.1) Set up trigger cube
|
||||
class SphereTouchedTrigger:
|
||||
value = False
|
||||
|
||||
def set_touched_value(args):
|
||||
# Called when the sphere touches the trigger cube
|
||||
SphereTouchedTrigger.value = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(trigger_cube_id)
|
||||
handler.add_callback("OnTriggerEnter", set_touched_value)
|
||||
|
||||
# 6) Wait a maximum of 3 seconds for the sphere to touch the trigger
|
||||
helper.wait_for_condition(lambda: SphereTouchedTrigger.value, 3.0)
|
||||
|
||||
# 7) Log Sphere's new location and validates sphere's movement
|
||||
new_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
# The sphere is supposed to move on X axis, but not on Y nor Z
|
||||
valid_movement = (new_sphere_pos.x > init_sphere_pos.x and
|
||||
abs(new_sphere_pos.y - init_sphere_pos.y) < 0.001 and
|
||||
abs(new_sphere_pos.z - init_sphere_pos.z) < 0.001)
|
||||
Report.result(Tests.sphere_moved, valid_movement)
|
||||
Report.info_vector3(new_sphere_pos, "Sphere new position:")
|
||||
|
||||
# 8) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_InitialLinearVelocity)
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976207
|
||||
# Test Case Title : Verify that when Kinematic is checked, the object behaves as a Kinematic object
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_box = ("Box found", "Box not found")
|
||||
find_ramp = ("Ramp found", "Ramp not found")
|
||||
box_is_not_kinematic = ("Box is not kinematic", "Box is kinematic")
|
||||
ramp_is_kinematic = ("Ramp is kinematic", "Ramp is not kinematic")
|
||||
box_gravity_enabled = ("Gravity enabled on the box", "Gravity not enabled on the box")
|
||||
ramp_gravity_enabled = ("Gravity enabled on the ramp", "Gravity not enabled on the ramp")
|
||||
box_touched_ramp = ("Box touched ramp", "Box did not touch ramp")
|
||||
ramp_did_not_move = ("Ramp did not move", "Ramp moved")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_KinematicModeWorks():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure kinematic property causes entity to remain in place when gravity acts upon it
|
||||
and when another entity collides with it.
|
||||
|
||||
Level Description:
|
||||
A box (entity: Box) set above a kinematic ramp (entity: Ramp). Gravity is enabled for the the box and the ramp.
|
||||
|
||||
Expected behavior:
|
||||
The box collides with the ramp, and the ramp does not fall.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Retrieve entities
|
||||
3) Check for kinematic ramp and not kinematic box
|
||||
4) Check that gravity is enabled on the entities
|
||||
5) Get the initial position of the ramp
|
||||
6) Check to see that the box hits the ramp
|
||||
6.5) Wait for the box to touch the ramp or timeout
|
||||
7) Check to see that the ramp stayed at the same position
|
||||
8) Exit game mode and close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
# Specific wait times in seconds
|
||||
TIME_OUT = 3.0
|
||||
REACTION = 0.1
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "RigidBody_KinematicModeWorks")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
box_id = general.find_game_entity("Box")
|
||||
Report.result(Tests.find_box, box_id.IsValid())
|
||||
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
# 3) Check for kinematic ramp and not kinematic box
|
||||
box_kinematic = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", box_id)
|
||||
Report.result(Tests.box_is_not_kinematic, not box_kinematic)
|
||||
|
||||
ramp_kinematic = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", ramp_id)
|
||||
Report.result(Tests.ramp_is_kinematic, ramp_kinematic)
|
||||
|
||||
# 4) Check that gravity is enabled on the entities
|
||||
box_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", box_id)
|
||||
Report.result(Tests.box_gravity_enabled, box_gravity_enabled)
|
||||
|
||||
ramp_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ramp_id)
|
||||
Report.result(Tests.ramp_gravity_enabled, ramp_gravity_enabled)
|
||||
|
||||
# 5) Get the initial position of the ramp
|
||||
ramp_pos_start = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", ramp_id)
|
||||
Report.info("Ramp's initial position: {}".format(ramp_pos_start))
|
||||
|
||||
# 6) Check to see that the box hits the ramp
|
||||
class RampTouched:
|
||||
value = False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ramp_id):
|
||||
Report.info("Box touched ramp")
|
||||
RampTouched.value = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(box_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 6.5) Wait for the box to touch the ramp or timeout
|
||||
helper.wait_for_condition(lambda: RampTouched.value, TIME_OUT)
|
||||
Report.result(Tests.box_touched_ramp, RampTouched.value)
|
||||
|
||||
# 7) Check to see that the ramp stayed at the same position
|
||||
general.idle_wait(REACTION) # wait for collision reaction
|
||||
ramp_pos_end = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", ramp_id)
|
||||
Report.info("Ramp's final position: {}".format(ramp_pos_end))
|
||||
Report.result(Tests.ramp_did_not_move, ramp_pos_start.Equal(ramp_pos_end))
|
||||
|
||||
# 8) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_KinematicModeWorks)
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
# Test case ID : C4976199
|
||||
# Test Case Title : Verify that with higher linear damping, the object in motion comes to rest faster
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_low = ("Entered game mode low", "Failed to enter game mode low")
|
||||
enter_game_mode_medium = ("Entered game mode medium", "Failed to enter game medium")
|
||||
enter_game_mode_high = ("Entered game mode high", "Failed to enter game mode high")
|
||||
find_sphere_low = ("Find sphere low", "Failed to find sphere low")
|
||||
find_sphere_medium = ("Find sphere medium", "Failed to find sphere medium")
|
||||
find_sphere_high = ("Find sphere high", "Failed to find sphere high")
|
||||
find_triggers_low = ("Find triggers low", "Failed to find triggers low")
|
||||
find_triggers_medium = ("Find triggers medium", "Failed to find triggers medium")
|
||||
find_triggers_high = ("Find triggers high", "Failed to find triggers high")
|
||||
x_movement = ("x direction movement decreases with increased damping", "x direction movement does not decrease with increased damping")
|
||||
high_damping_no_movement = ("High damping IsClose to 0 movement", "High damping is not IsClose to 0 movement")
|
||||
triggers_tripped_low = ("Low damping trips all 3 triggers", "Low damping did not trip all 3 triggers")
|
||||
triggers_tripped_medium = ("Medium damping trips 2/3 triggers", "Medium damping does not trip 2/3 triggers")
|
||||
triggers_tripped_high = ("High damping trips no triggers", "High damping trips triggers and should not")
|
||||
timeout = ("All spheres velocity IsClose to 0 before timeout", "All spheres velocity are not IsClose to 0 before timeout")
|
||||
exit_game_mode_low = ("Exited game mode low", "Couldn't exit game mode low")
|
||||
exit_game_mode_medium = ("Exited game mode medium", "Couldn't exit game mode medium")
|
||||
exit_game_mode_high = ("Exited game mode high", "Couldn't exit game mode high")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_LinearDampingAffectsMotion():
|
||||
"""
|
||||
The level consists of a PhysX Terrain component that is not interacted with (per the test case)
|
||||
and a PhysX collider with shape sphere, PhysX rigid bodies physics, and mesh with shape sphere.
|
||||
3 colliders with trigger are added to ensure x movement length is decreased quantitatively not
|
||||
just comparatively.
|
||||
The sphere starts asleep.
|
||||
We will enter game mode for each state defined
|
||||
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Set sphere attributes, measure initial position, and then ForceAwake
|
||||
5) Measure
|
||||
6) Exit game mode
|
||||
7) Run and repeat steps 2-6
|
||||
8) Report results
|
||||
|
||||
Notes:
|
||||
Initially we calculated time to stop by calling time.time() both when awakening the sphere and when velocity equaled zero.
|
||||
Comparing the time to stop accross sphere settings proved flaky.
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
class SphereInfo:
|
||||
def __init__(
|
||||
self,
|
||||
initial_linear_velocity,
|
||||
linear_damping,
|
||||
enter_game_mode_test,
|
||||
find_sphere_test,
|
||||
exit_game_mode_test,
|
||||
triggers_test,
|
||||
):
|
||||
self.initial_linear_velocity = initial_linear_velocity
|
||||
self.linear_damping = linear_damping
|
||||
self.entity = None
|
||||
self.initial_position = None
|
||||
self.final_position = None
|
||||
self.timeout = False
|
||||
self.triggers = []
|
||||
self.enter_game_mode_test = enter_game_mode_test
|
||||
self.find_sphere_test = find_sphere_test
|
||||
self.exit_game_mode_test = exit_game_mode_test
|
||||
self.triggers_test = triggers_test
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
initial_linear_velocity = {}
|
||||
linear_damping = {}
|
||||
timeout = {}
|
||||
""".format(
|
||||
self.initial_linear_velocity.GetLength(), self.linear_damping, self.timeout
|
||||
)
|
||||
|
||||
def report(self):
|
||||
Report.info(self.__str__())
|
||||
Report.info_vector3(self.initial_position, "Initial position")
|
||||
Report.info_vector3(self.final_position, "Final position")
|
||||
for trigger in self.triggers:
|
||||
Report.info(trigger.__str__())
|
||||
|
||||
class Trigger:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.entity = None
|
||||
self.handler = None
|
||||
self.triggering_entity = None
|
||||
self.triggered = False
|
||||
|
||||
def on_trigger(self, args):
|
||||
self.triggered = True
|
||||
self.triggering_entity = args[0]
|
||||
Report.info("{} was triggered by {}".format(self.name, self.triggering_entity_name()))
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
name = {}
|
||||
triggering entity name = {}
|
||||
triggered = {}
|
||||
""".format(
|
||||
self.name, self.triggering_entity_name(), self.triggered
|
||||
)
|
||||
|
||||
def triggering_entity_name(self):
|
||||
if self.triggering_entity:
|
||||
return azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", self.triggering_entity)
|
||||
return None
|
||||
|
||||
INITIAL_LINEAR_VELOCITY = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
ZERO_VELOCITY = lymath.Vector3(0.0, 0.0, 0.0)
|
||||
TOLERANCE = 0.001
|
||||
TIMEOUT = 3.0
|
||||
TRIGGER1 = "Trigger1"
|
||||
TRIGGER2 = "Trigger2"
|
||||
TRIGGER3 = "Trigger3"
|
||||
|
||||
# sphere state under test
|
||||
# fmt: off
|
||||
low_damping = SphereInfo(
|
||||
INITIAL_LINEAR_VELOCITY,
|
||||
5.0,
|
||||
Tests.enter_game_mode_low,
|
||||
Tests.find_sphere_low,
|
||||
Tests.exit_game_mode_low,
|
||||
Tests.find_triggers_low,
|
||||
)
|
||||
medium_damping = SphereInfo(
|
||||
INITIAL_LINEAR_VELOCITY,
|
||||
10.0,
|
||||
Tests.enter_game_mode_medium,
|
||||
Tests.find_sphere_medium,
|
||||
Tests.exit_game_mode_medium,
|
||||
Tests.find_triggers_medium,
|
||||
)
|
||||
high_damping = SphereInfo(
|
||||
INITIAL_LINEAR_VELOCITY,
|
||||
10000.0,
|
||||
Tests.enter_game_mode_high,
|
||||
Tests.find_sphere_high,
|
||||
Tests.exit_game_mode_high,
|
||||
Tests.find_triggers_high,
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
spheres = [low_damping, medium_damping, high_damping]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_LinearDampingAffectsMotion")
|
||||
|
||||
def run_test_steps(sphere):
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(sphere.enter_game_mode_test)
|
||||
|
||||
# 3) Retrieve entities
|
||||
sphere.entity = general.find_game_entity("Sphere")
|
||||
Report.critical_result(sphere.find_sphere_test, sphere.entity.IsValid())
|
||||
|
||||
triggers = [Trigger(TRIGGER1), Trigger(TRIGGER2), Trigger(TRIGGER3)]
|
||||
for trigger in triggers:
|
||||
trigger.entity = general.find_game_entity(trigger.name)
|
||||
|
||||
invalid_triggers = [t for t in triggers if not t.entity.IsValid()]
|
||||
for trigger in invalid_triggers:
|
||||
Report.info("Trigger {} is invalid".format(trigger.name))
|
||||
Report.critical_result(sphere.triggers_test, len(invalid_triggers) == 0)
|
||||
|
||||
# 4) Set sphere attributes, measure initial position, and then ForceAwake
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", sphere.entity, sphere.initial_linear_velocity)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearDamping", sphere.entity, sphere.linear_damping)
|
||||
sphere.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", sphere.entity).GetPosition()
|
||||
|
||||
# add handler for each trigger
|
||||
for trigger in triggers:
|
||||
trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
trigger.handler.connect(trigger.entity)
|
||||
trigger.handler.add_callback("OnTriggerEnter", trigger.on_trigger)
|
||||
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", sphere.entity)
|
||||
general.idle_wait_frames(1) # wait one frame for changes to apply
|
||||
|
||||
# 5) Measure
|
||||
|
||||
def sphere_stopped():
|
||||
sphere_linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere.entity)
|
||||
if sphere_linear_velocity.IsClose(ZERO_VELOCITY, TOLERANCE):
|
||||
sphere.final_position = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTM", sphere.entity
|
||||
).GetPosition()
|
||||
for trigger in [t for t in triggers if t.triggered]:
|
||||
if trigger.triggering_entity.Equal(sphere.entity):
|
||||
sphere.triggers.append(trigger)
|
||||
return True
|
||||
return False
|
||||
|
||||
if not helper.wait_for_condition(sphere_stopped, TIMEOUT):
|
||||
sphere.timeout = True
|
||||
sphere.report()
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(sphere.exit_game_mode_test)
|
||||
|
||||
# 7) Run and repeat steps 2-6
|
||||
for sphere in spheres:
|
||||
run_test_steps(sphere)
|
||||
|
||||
# 8) Report results
|
||||
no_timeout = True
|
||||
for sphere in spheres:
|
||||
if sphere.timeout:
|
||||
Report.info(
|
||||
"Timeout occurred. Sphere with damping = {} and Initial velocity = {} did not come to rest in the timeout of {}".format(
|
||||
sphere.linear_damping, sphere.initial_linear_velocity, TIMEOUT
|
||||
)
|
||||
)
|
||||
no_timeout = False
|
||||
|
||||
# fast fail if timeout occurred, comparisons will be meaningless and all info is in log to determine which sphere(s) timed out
|
||||
Report.critical_result(Tests.timeout, no_timeout)
|
||||
Report.result(
|
||||
Tests.high_damping_no_movement, high_damping.final_position.IsClose(high_damping.initial_position, TOLERANCE)
|
||||
)
|
||||
# comparative movement length
|
||||
Report.result(
|
||||
Tests.x_movement, high_damping.final_position.x < medium_damping.final_position.x < low_damping.final_position.x
|
||||
)
|
||||
# quantitative movement length
|
||||
all_three_tripped = (
|
||||
len(low_damping.triggers) == 3
|
||||
and len([t for t in low_damping.triggers if t.name == TRIGGER1]) == 1
|
||||
and len([t for t in low_damping.triggers if t.name == TRIGGER2]) == 1
|
||||
and len([t for t in low_damping.triggers if t.name == TRIGGER3]) == 1
|
||||
)
|
||||
Report.result(Tests.triggers_tripped_low, all_three_tripped)
|
||||
first_two_triggers_tripped = (
|
||||
len(medium_damping.triggers) == 2
|
||||
and len([t for t in medium_damping.triggers if t.name == TRIGGER1]) == 1
|
||||
and len([t for t in medium_damping.triggers if t.name == TRIGGER2]) == 1
|
||||
)
|
||||
Report.result(Tests.triggers_tripped_medium, first_two_triggers_tripped)
|
||||
Report.result(Tests.triggers_tripped_high, len(high_damping.triggers) == 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_LinearDampingAffectsMotion)
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976201
|
||||
# Test Case Title : Verify that the value assigned to the Mass of the object, gets actually assigned to the object
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# test iteration 1
|
||||
enter_game_mode_1 = ("Entered game mode first time", "Failed to enter game mode first time")
|
||||
ProjectileSphere_exists_1 = ("ProjectileSphere entity found first time", "ProjectileSphere entity not found first time")
|
||||
TargetSphere_exists_1 = ("TargetSphere entity found first time", "TargetSphere entity not found first time")
|
||||
Trigger1_exists_1 = ("Trigger1 entity found first time", "Trigger1 entity not found first time")
|
||||
Trigger2_exists_1 = ("Trigger2 entity found first time", "Trigger2 entity not found first time")
|
||||
Trigger3_exists_1 = ("Trigger3 entity found first time", "Trigger3 entity not found first time")
|
||||
TargetSphere_mass_1 = ("Mass of TargetSphere was set to 1.0", "Mass of TargetSphere was not set to 1.0")
|
||||
spheres_collided_1 = ("ProjectileSphere and TargetSphere collided first time", "Timed out before ProjectileSphere & 2 collided first time")
|
||||
stopped_correctly_1 = ("TargetSphere hit Trigger1 & Trigger2 but not Trigger_3", "TargetSphere did not stop correctly")
|
||||
check_y_1 = ("sphere did not move far from expected in Y direction _1", "TargetSphere moved an unexpected distance in Y direction _1")
|
||||
check_z_1 = ("sphere did not move far from expected in Z direction _1", "TargetSphere moved an unexpected distance in Z direction _1")
|
||||
exit_game_mode_1 = ("Exited game mode first time", "Couldn't exit game mode first time")
|
||||
|
||||
# test iteration 2
|
||||
enter_game_mode_2 = ("Entered game mode second time", "Failed to enter game mode second time")
|
||||
ProjectileSphere_exists_2 = ("ProjectileSphere entity found second time", "ProjectileSphere entity not found second time")
|
||||
TargetSphere_exists_2 = ("TargetSphere entity found second time", "TargetSphere entity not found second time")
|
||||
Trigger1_exists_2 = ("Trigger1 entity found second time", "Trigger1 entity not found second time")
|
||||
Trigger2_exists_2 = ("Trigger2 entity found second time", "Trigger2 entity not found second time")
|
||||
Trigger3_exists_2 = ("Trigger3 entity found second time", "Trigger3 entity not found second time")
|
||||
TargetSphere_mass_2 = ("Mass of TargetSphere was set to 10.0", "Mass of TargetSphere was not set to 10.0")
|
||||
spheres_collided_2 = ("ProjectileSphere and TargetSphere collided second time", "Timed out before ProjectileSphere & 2 collided second time")
|
||||
stopped_correctly_2 = ("TargetSphere hit Trigger1 but not Trigger2 or Trigger3", "TargetSphere did not stop correctly")
|
||||
check_y_2 = ("sphere did not move far from expected in Y direction _2", "TargetSphere moved an unexpected distance in Y direction _2")
|
||||
check_z_2 = ("sphere did not move far from expected in Z direction _2", "TargetSphere moved an unexpected distance in Z direction _2")
|
||||
exit_game_mode_2 = ("Exited game mode second time", "Couldn't exit game mode second time")
|
||||
|
||||
# test iteration 3
|
||||
enter_game_mode_3 = ("Entered game mode third time", "Failed to enter game mode third time")
|
||||
ProjectileSphere_exists_3 = ("ProjectileSphere entity found third time", "ProjectileSphere entity not found third time")
|
||||
TargetSphere_exists_3 = ("TargetSphere entity found third time", "TargetSphere entity not found third time")
|
||||
Trigger1_exists_3 = ("Trigger1 entity found third time", "Trigger1 entity not found third time")
|
||||
Trigger2_exists_3 = ("Trigger2 entity found third time", "Trigger2 entity not found third time")
|
||||
Trigger3_exists_3 = ("Trigger3 entity found third time", "Trigger3 entity not found third time")
|
||||
TargetSphere_mass_3 = ("Mass of TargetSphere was set to 100.0", "Mass of TargetSphere was not set to 100.0")
|
||||
spheres_collided_3 = ("ProjectileSphere and TargetSphere collided third time", "Timed out before ProjectileSphere & 2 collided third time")
|
||||
stopped_correctly_3 = ("TargetSphere did not hit Trigger1, Trigger2, or Trigger3", "TargetSphere hit one or more triggers before stopping")
|
||||
check_y_3 = ("sphere did not move far from expected in Y direction _3", "TargetSphere moved an unexpected distance in Y direction _3")
|
||||
check_z_3 = ("sphere did not move far from expected in Z direction _3", "TargetSphere moved an unexpected distance in Z direction _3")
|
||||
exit_game_mode_3 = ("Exited game mode third time", "Couldn't exit game mode third time")
|
||||
|
||||
# general
|
||||
velocity_sizing = ("The velocities are in the correct order of magnitude", "The velocities are not correctly ordered in magnitude")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_MassDifferentValuesWorks():
|
||||
"""
|
||||
Summary:
|
||||
Checking that the mass set to the object is actually applied via colliding entities
|
||||
|
||||
Level Description:
|
||||
ProjectileSphere (entity) - Sphere shaped Mesh; Sphere shaped PhysX Collider;
|
||||
PhysX Rigid Body: initial linear velocity in X direction is 5m/s, initial mass 1kg,
|
||||
gravity disabled, linear damping default (0.05)
|
||||
TargetSphere (entity) - Sphere shaped Mesh; Sphere shaped PhysX Collider;
|
||||
PhysX Rigid Body: no initial velocity, initial mass 1kg, gravity disabled, linear damping 1.0
|
||||
|
||||
Expected Behavior:
|
||||
The ProjectileSphere entity will float towards TargetSphere entity and then collide with it.
|
||||
Because they are the same mass initially, the second sphere will move after collision.
|
||||
TargetSphere's mass will be increased and scenario will run again,
|
||||
but TargetSphere will have a smaller velocity after collision.
|
||||
TargetSphere will then increase mass again and should barely move after the final collision.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Repeat steps 3-9
|
||||
3) Enter game mode
|
||||
4) Find and setup entities
|
||||
5) Set mass of the TargetSphere
|
||||
6) Check for collision
|
||||
7) Wait for TargetSphere x velocity = 0
|
||||
8) Check the triggers
|
||||
9) Exit game mode
|
||||
10) Verify the velocity of TargetSphere decreased after collision as mass increased
|
||||
11) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
MOVEMENT_TIMEOUT = 7.0
|
||||
COLLISION_TIMEOUT = 2.0
|
||||
VELOCITY_ZERO = 0.01
|
||||
Y_Z_BUFFER = 0.01
|
||||
TARGET_SPHERE_NAME = "TargetSphere"
|
||||
PROJECTILE_SPHERE_NAME = "ProjectileSphere"
|
||||
TRIGGER_1_NAME = "Trigger1"
|
||||
TRIGGER_2_NAME = "Trigger2"
|
||||
TRIGGER_3_NAME = "Trigger3"
|
||||
|
||||
class ProjectileSphere:
|
||||
def __init__(self, test_iteration):
|
||||
self.name = PROJECTILE_SPHERE_NAME
|
||||
self.test_iteration = test_iteration
|
||||
self.timeout_reached = True
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(
|
||||
Tests.__dict__["ProjectileSphere_exists_" + str(self.test_iteration)], self.id.IsValid()
|
||||
)
|
||||
|
||||
def destroy_me(self):
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DestroyGameEntity", self.id)
|
||||
|
||||
class TargetSphere:
|
||||
def __init__(self, mass_to_assign, stop_before_trigger_name, expected_trigger_pattern, test_iteration):
|
||||
self.id = None
|
||||
self.name = TARGET_SPHERE_NAME
|
||||
self.start_mass = None
|
||||
self.mass_to_assign = mass_to_assign
|
||||
self.collision_begin = False
|
||||
self.after_collision_velocity = None
|
||||
self.x_movement_timeout = True
|
||||
self.stop_before_trigger_name = stop_before_trigger_name
|
||||
self.expected_trigger_pattern = expected_trigger_pattern
|
||||
self.collision_ended = False
|
||||
self.test_iteration = test_iteration
|
||||
self.test_set_mass = self.get_test("TargetSphere_mass_")
|
||||
self.test_enter_game_mode = self.get_test("enter_game_mode_")
|
||||
self.test_ProjectileSphere_exist = self.get_test("ProjectileSphere_exists_")
|
||||
self.test_TargetSphere_exist = self.get_test("TargetSphere_exists_")
|
||||
self.test_spheres_collided = self.get_test("spheres_collided_")
|
||||
self.test_stop_properly = self.get_test("stopped_correctly_")
|
||||
self.test_check_y = self.get_test("check_y_")
|
||||
self.test_check_z = self.get_test("check_z_")
|
||||
self.test_exit_game_mode = self.get_test("exit_game_mode_")
|
||||
|
||||
def get_test(self, test_prefix):
|
||||
return Tests.__dict__[test_prefix + str(self.test_iteration)]
|
||||
|
||||
def find(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__["TargetSphere_exists_" + str(self.test_iteration)], self.id.IsValid())
|
||||
|
||||
def setup_mass(self):
|
||||
self.start_mass = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", self.id)
|
||||
Report.info("{} starting mass: {}".format(self.name, self.start_mass))
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetMass", self.id, self.mass_to_assign)
|
||||
general.idle_wait_frames(1) # wait for mass to apply
|
||||
mass_after_set = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", self.id)
|
||||
Report.info("{} mass after setting: {}".format(self.name, mass_after_set))
|
||||
Report.result(self.test_set_mass, self.mass_to_assign == mass_after_set)
|
||||
|
||||
def current_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.id):
|
||||
Report.info("spheres collision begin")
|
||||
self.collision_begin = True
|
||||
|
||||
def on_collision_end(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.id):
|
||||
Report.info("spheres collision end")
|
||||
self.after_collision_velocity = self.current_velocity()
|
||||
self.collision_ended = True
|
||||
|
||||
def add_collision_handlers(self, projectile_sphere_id):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(projectile_sphere_id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
self.handler.add_callback("OnCollisionEnd", self.on_collision_end)
|
||||
|
||||
def x_velocity_zero(self):
|
||||
if abs(self.current_velocity().x) < VELOCITY_ZERO:
|
||||
Report.info("TargetSphere has stopped moving.")
|
||||
self.x_movement_timeout = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def collision_complete(self):
|
||||
return self.collision_begin and self.collision_ended
|
||||
|
||||
def check_y_z_movement_from_collision(self):
|
||||
"""
|
||||
Used to check that the entity has not moved too far in either the Y or Z direction
|
||||
"""
|
||||
|
||||
def is_within_tolerance(velocity_one_direction):
|
||||
return abs(velocity_one_direction) < Y_Z_BUFFER
|
||||
|
||||
Report.info_vector3(self.after_collision_velocity, "Initial Velocity: ")
|
||||
Report.result(self.test_check_y, is_within_tolerance(self.after_collision_velocity.y))
|
||||
Report.result(self.test_check_z, is_within_tolerance(self.after_collision_velocity.z))
|
||||
|
||||
class Trigger:
|
||||
"""
|
||||
Used in the level to tell if the TargetSphere entity has moved a certain distance.
|
||||
There are three triggers set up in the level.
|
||||
"""
|
||||
|
||||
def __init__(self, name, test_iteration):
|
||||
self.name = name
|
||||
self.handler = None
|
||||
self.triggered = False
|
||||
self.test_iteration = test_iteration
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__[self.name + "_exists_" + str(self.test_iteration)], self.id.IsValid())
|
||||
self.setup_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
"""
|
||||
This is passed into this object's handler.add_callback().
|
||||
"""
|
||||
other_id = args[0]
|
||||
self.triggered = True
|
||||
triggered_by_name = azlmbr.entity.GameEntityContextRequestBus(
|
||||
azlmbr.bus.Broadcast, "GetEntityName", other_id
|
||||
)
|
||||
Report.info("{} was triggered by {}.".format(self.name, triggered_by_name))
|
||||
|
||||
def setup_handler(self):
|
||||
"""
|
||||
This is called to setup the handler for this trigger object
|
||||
"""
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
class TriggerResultPattern:
|
||||
"""
|
||||
Used to store and determine which triggers were activated and compare to expected
|
||||
"""
|
||||
|
||||
def __init__(self, trigger1_activated, trigger2_activated, trigger3_activated):
|
||||
self.trigger1_activated = trigger1_activated
|
||||
self.trigger2_activated = trigger2_activated
|
||||
self.trigger3_activated = trigger3_activated
|
||||
|
||||
def __eq__(self, other_pattern):
|
||||
"""
|
||||
Used to determine if two patterns equal/match each other (i.e. Expected VS Actual)
|
||||
"""
|
||||
if isinstance(other_pattern, self.__class__):
|
||||
return (
|
||||
self.trigger1_activated == other_pattern.trigger1_activated
|
||||
and self.trigger2_activated == other_pattern.trigger2_activated
|
||||
and self.trigger3_activated == other_pattern.trigger3_activated
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
def report(self, expect_actual):
|
||||
Report.info(
|
||||
"""TargetSphere {} Triggers:
|
||||
Trigger_1: {}
|
||||
Trigger_2: {}
|
||||
Trigger_3: {}
|
||||
""".format(
|
||||
expect_actual, self.trigger1_activated, self.trigger2_activated, self.trigger3_activated
|
||||
)
|
||||
)
|
||||
|
||||
target_sphere_1kg = TargetSphere(
|
||||
mass_to_assign=1.0,
|
||||
stop_before_trigger_name=TRIGGER_3_NAME,
|
||||
expected_trigger_pattern=TriggerResultPattern(True, True, False),
|
||||
test_iteration=1,
|
||||
)
|
||||
|
||||
target_sphere_10kg = TargetSphere(
|
||||
mass_to_assign=10.0,
|
||||
stop_before_trigger_name=TRIGGER_2_NAME,
|
||||
expected_trigger_pattern=TriggerResultPattern(True, False, False),
|
||||
test_iteration=2,
|
||||
)
|
||||
|
||||
target_sphere_100kg = TargetSphere(
|
||||
mass_to_assign=100.0,
|
||||
stop_before_trigger_name=TRIGGER_1_NAME,
|
||||
expected_trigger_pattern=TriggerResultPattern(False, False, False),
|
||||
test_iteration=3,
|
||||
)
|
||||
|
||||
target_spheres = [target_sphere_1kg, target_sphere_10kg, target_sphere_100kg]
|
||||
|
||||
target_sphere_velocities = {}
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_MassDifferentValuesWorks")
|
||||
|
||||
# 2) Repeat steps 3-9
|
||||
for target_sphere in target_spheres:
|
||||
Report.info("***************** Begin Test Iteration {} ******************".format(target_sphere.test_iteration))
|
||||
|
||||
# 3) Enter game mode
|
||||
helper.enter_game_mode(target_sphere.test_enter_game_mode)
|
||||
|
||||
# 4) Find and setup entities
|
||||
projectile_sphere = ProjectileSphere(target_sphere.test_iteration)
|
||||
target_sphere.find()
|
||||
target_sphere.add_collision_handlers(projectile_sphere.id)
|
||||
|
||||
trigger_1 = Trigger(TRIGGER_1_NAME, target_sphere.test_iteration)
|
||||
trigger_2 = Trigger(TRIGGER_2_NAME, target_sphere.test_iteration)
|
||||
trigger_3 = Trigger(TRIGGER_3_NAME, target_sphere.test_iteration)
|
||||
|
||||
# 5) Set mass of the TargetSphere
|
||||
target_sphere.setup_mass()
|
||||
|
||||
# 6) Check for collision
|
||||
|
||||
helper.wait_for_condition(target_sphere.collision_complete, COLLISION_TIMEOUT)
|
||||
Report.critical_result(target_sphere.test_spheres_collided, target_sphere.collision_complete())
|
||||
projectile_sphere.destroy_me()
|
||||
Report.info_vector3(
|
||||
target_sphere.after_collision_velocity, "Velocity of {} after the collision: ".format(target_sphere.name)
|
||||
)
|
||||
|
||||
Report.info("The sphere should stop before touching {}".format(target_sphere.stop_before_trigger_name))
|
||||
|
||||
# 7) Wait for TargetSphere x velocity = 0
|
||||
|
||||
helper.wait_for_condition(target_sphere.x_velocity_zero, MOVEMENT_TIMEOUT)
|
||||
if target_sphere.x_movement_timeout is True:
|
||||
Report.info("TargetSphere failed to stop moving in the x direction before timeout was reached.")
|
||||
|
||||
# 8) Check the triggers
|
||||
actual_trigger_pattern = TriggerResultPattern(trigger_1.triggered, trigger_2.triggered, trigger_3.triggered)
|
||||
|
||||
patterns_match = actual_trigger_pattern == target_sphere.expected_trigger_pattern
|
||||
target_sphere.expected_trigger_pattern.report("Expected")
|
||||
actual_trigger_pattern.report("Actual")
|
||||
Report.result(target_sphere.test_stop_properly, patterns_match)
|
||||
|
||||
target_sphere.check_y_z_movement_from_collision()
|
||||
target_sphere_velocities.update({target_sphere.test_iteration: target_sphere.after_collision_velocity.x})
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(target_sphere.test_exit_game_mode)
|
||||
Report.info("~~~~~~~~~~~~~~ Test Iteration {} End ~~~~~~~~~~~~~~~~~~".format(target_sphere.test_iteration))
|
||||
|
||||
# 10) Verify the velocity of TargetSphere decreased after collision as mass increased
|
||||
outcome = target_sphere_velocities[1] > target_sphere_velocities[2] > target_sphere_velocities[3]
|
||||
Report.result(Tests.velocity_sizing, outcome)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_MassDifferentValuesWorks)
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C13352089
|
||||
# Test Case Title : Verify that maximum angular velocity interacts correctly with initial angular velocity
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
|
||||
# level
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
trigger_touch_times = ("Bar5 rotated more than bar6", "Bar5 did not stop more slowly than bar6")
|
||||
rotation_duration = ("Bar5 stopped faster than bar6", "Bar5 did not stop faster than bar6")
|
||||
|
||||
# bar 1
|
||||
bar1_gravity_disabled = ("Bar1 : Gravity is disabled", "Bar1 : Gravity is not disabled")
|
||||
bar1_found = ("Bar1 : Found entity", "Bar1 : Entity not found")
|
||||
bar1_rotation = ("Bar1 : Rotated on X axis", "Bar1 : Unexpected rotation")
|
||||
bar1_angular_velocity = ("Bar1 : Expected angular velocity", "Bar1 : Unexpected angular velocity")
|
||||
|
||||
# bar 2
|
||||
bar2_gravity_disabled = ("Bar2 : Gravity is disabled", "Bar2 : Gravity is not disabled")
|
||||
bar2_found = ("Bar2 : Found entity", "Bar2 : Entity not found")
|
||||
bar2_rotation = ("Bar2 : Rotated on X axis", "Bar2 : Unexpected rotation")
|
||||
bar2_angular_velocity = ("Bar2 : Expected angular velocity", "Bar2 : Unexpected angular velocity")
|
||||
|
||||
# bar 3
|
||||
bar3_gravity_disabled = ("Bar3 : Gravity is disabled", "Bar3 : Gravity is not disabled")
|
||||
bar3_found = ("Bar3 : Found entity", "Bar3 : Entity not found")
|
||||
bar3_rotation = ("Bar3 : Rotated on X axis", "Bar3 : Unexpected rotation")
|
||||
bar3_angular_velocity = ("Bar3 : Expected angular velocity", "Bar3 : Unexpected angular velocity")
|
||||
|
||||
# bar 4
|
||||
bar4_gravity_disabled = ("Bar4 : Gravity is disabled", "Bar4 : Gravity is not disabled")
|
||||
bar4_found = ("Bar4 : Found entity", "Bar4 : Entity not found")
|
||||
bar4_rotation = ("Bar4 : Did not rotate", "Bar4 : Unexpected rotation")
|
||||
bar4_angular_velocity = ("Bar4 : Expected angular velocity", "Bar4 : Unexpected angular velocity")
|
||||
|
||||
# bar 5
|
||||
bar5_gravity_disabled = ("Bar5 : Gravity is disabled", "Bar5 : Gravity is not disabled")
|
||||
bar5_found = ("Bar5 : Found entity", "Bar5 : Entity not found")
|
||||
bar5_rotation = ("Bar5 : Rotated on X axis", "Bar5 : Unexpected rotation")
|
||||
bar5_angular_velocity = ("Bar5 : Expected angular velocity", "Bar5 : Unexpected angular velocity")
|
||||
|
||||
# bar 6
|
||||
bar6_gravity_disabled = ("Bar6 : Gravity is disabled", "Bar6 : Gravity is not disabled")
|
||||
bar6_found = ("Bar6 : Found entity", "Bar6 : Entity not found")
|
||||
bar6_rotation = ("Bar6 : Rotated on X axis", "Bar6 : Unexpected rotation")
|
||||
bar6_angular_velocity = ("Bar6 : Expected angular velocity", "Bar6 : Unexpected angular velocity")
|
||||
|
||||
# trigger for bar 5
|
||||
bar5_trigger_found = ("Trigger for bar5 : Found entity", "Trigger for bar5 : Entity not found")
|
||||
|
||||
# trigger for bar 6
|
||||
bar6_trigger_found = ("Trigger for bar6 : Found entity", "Trigger for bar6 : Entity not found")
|
||||
|
||||
did_not_timeout = ("Should_wait did not time out", "Should wait timed out")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_MaxAngularVelocityWorks():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure maximum angular velocity and angular damping interact correctly with
|
||||
initial angular velocity
|
||||
|
||||
Level Description:
|
||||
bars:
|
||||
6 PhysX Rigid Bodies with PhysX Shape Collider (Box Shape, Dimensions: X=1, Y=1, Z=5), gravity disabled,
|
||||
start asleep, positioned above terrain
|
||||
bar 1: Initial Angular Velocity = 10 rad/s, Max Angular Velocity = 5 rad/s, Angular Damping = 0.0
|
||||
bar 2: Initial Angular Velocity = 10 rad/s, Max Angular Velocity = 10 rad/s, Angular Damping = 0.0
|
||||
bar 3: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 20 rad/s, Angular Damping = 0.0
|
||||
bar 4: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 0 rad/s, Angular Damping = 0.0
|
||||
bar 5: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 20 rad/s, Angular Damping = 2.0
|
||||
bar 6: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 20 rad/s, Angular Damping = 5.0
|
||||
triggers:
|
||||
2 PhysX Shape Collider (Box Shape), gravity disabled, trigger enabled, start asleep
|
||||
trigger for bar 5: positioned above terrain, in front of bar 5
|
||||
trigger for bar 6: positioned above terrain, in front of bar 6
|
||||
|
||||
Expected Behavior:
|
||||
bar 1: Should rotate at 5 rad/s on X axis
|
||||
bar 2: Should rotate at 10 rad/s on X axis
|
||||
bar 3: Should rotate at 20 rad/s on X axis
|
||||
bar 4: Should not rotate at all
|
||||
bar 5: Should start rotating at 20 rad/s on X axis and stop slowly (touching its trigger several times)
|
||||
bar 6: Should start rotating at 20 rad/s on X axis but stop quickly (touching its trigger 0 or very few times)
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Set up bars values
|
||||
4) Loop for each bar
|
||||
4.1) Find the bar
|
||||
4.2) Validate ID
|
||||
4.3) Activate the bar
|
||||
4.4) Validate and log its position
|
||||
4.5) Confirm gravity is disabled for the bar
|
||||
4.6) Log bar's initial rotation
|
||||
4.7) Log bar's angular damping
|
||||
4.8) Log bar's angular velocity
|
||||
4.9) Set up trigger if the bar has one
|
||||
4.10) Wait for each bar to rotate or stop rotating
|
||||
4.11) Log current location
|
||||
4.12) Validate rotation based on expected behavior
|
||||
4.13) Destroy bar (and its trigger)
|
||||
5) Compare trigger touch times for bar5 and bar6
|
||||
6) Exit game mode
|
||||
7) Close editor
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
import time
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import azlmbr.math as lymath
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
ANGULAR_VELOCITY_TOLERANCE = 0.05
|
||||
TIME_OUT = 15.0
|
||||
ROTATION_TOLERANCE = 0.001
|
||||
|
||||
def is_close(x, y, tolerance=ANGULAR_VELOCITY_TOLERANCE):
|
||||
return abs(x - y) < tolerance
|
||||
|
||||
def is_angle_close(x, y):
|
||||
r = (math.sin(x) - math.sin(y)) * (math.sin(x) - math.sin(y)) + (math.cos(x) - math.cos(y)) * (
|
||||
math.cos(x) - math.cos(y)
|
||||
)
|
||||
diff = math.acos((2.0 - r) / 2.0)
|
||||
return abs(diff) <= ROTATION_TOLERANCE
|
||||
|
||||
class Entity: # Parent class for bars and triggers
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.validate_ID()
|
||||
self.activate_entity()
|
||||
|
||||
def validate_ID(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
found_tuple = Tests.__dict__[self.name + "_found"]
|
||||
Report.critical_result(found_tuple, self.id.IsValid())
|
||||
|
||||
def activate_entity(self):
|
||||
Report.info("Activating Entity : " + self.name)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
general.idle_wait_frames(1)
|
||||
self.rotation_start_time = time.time()
|
||||
|
||||
class Bar(Entity):
|
||||
def __init__(self, name, init_ang_velocity_on_X, max_ang_velocity, has_trigger=False):
|
||||
# 1) Validate ID, then activate Bar
|
||||
Entity.__init__(self, name)
|
||||
self.rotation_duration = 999999.9
|
||||
self.init_angular_velocity = lymath.Vector3(init_ang_velocity_on_X, 0.0, 0.0)
|
||||
self.max_angular_velocity = max_ang_velocity
|
||||
self.max_valid_velocity = min(self.max_angular_velocity, self.init_angular_velocity.GetLength())
|
||||
self.angular_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularDamping", self.id)
|
||||
# ) Log initial rotation
|
||||
self.init_rotation = self.get_rotation()
|
||||
# ) Verify gravity is disabled for bar
|
||||
self.validate_gravity_is_disabled()
|
||||
# ) Validate angular velocity according to max angular velocity
|
||||
self.validate_angular_velocity()
|
||||
# ) Setup trigger
|
||||
if has_trigger:
|
||||
self.touched_trigger = 0
|
||||
self.setup_trigger()
|
||||
|
||||
def validate_gravity_is_disabled(self):
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
gravity_tuple = Tests.__dict__[self.name + "_gravity_disabled"]
|
||||
Report.result(gravity_tuple, not gravity_enabled)
|
||||
|
||||
def setup_trigger(self):
|
||||
self.trigger = Entity(self.name + "_trigger")
|
||||
self.trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.trigger.handler.connect(self.trigger.id)
|
||||
self.trigger.handler.add_callback("OnTriggerEnter", self.count_touch_times)
|
||||
|
||||
def get_angular_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularVelocity", self.id)
|
||||
|
||||
def get_rotation(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", self.id)
|
||||
|
||||
def validate_angular_velocity(self):
|
||||
general.idle_wait_frames(10)
|
||||
self.angular_velocity = self.get_angular_velocity()
|
||||
angular_velocity_tuple = Tests.__dict__[self.name + "_angular_velocity"]
|
||||
if self.angular_damping == 0:
|
||||
velocity_is_valid = is_close(self.angular_velocity.GetLength(), self.max_valid_velocity)
|
||||
else:
|
||||
velocity_is_valid = self.max_valid_velocity >= self.angular_velocity.GetLength() >= 0.0
|
||||
|
||||
Report.result(angular_velocity_tuple, velocity_is_valid)
|
||||
|
||||
def validate_rotation(self):
|
||||
self.rotation = self.get_rotation()
|
||||
rotated_on_x = not is_angle_close(self.init_rotation.x, self.rotation.x)
|
||||
rotated_on_y = not is_angle_close(self.init_rotation.y, self.rotation.y)
|
||||
rotated_on_z = not is_angle_close(self.init_rotation.z, self.rotation.z)
|
||||
if self.max_valid_velocity != 0:
|
||||
return rotated_on_x and not rotated_on_y and not rotated_on_z
|
||||
else:
|
||||
return not rotated_on_x and not rotated_on_y and not rotated_on_z
|
||||
|
||||
def has_waited_enough(self):
|
||||
if self.angular_damping > 0:
|
||||
# Bar5 or bar6 should stop rotating to consider their movement as done
|
||||
if self.get_angular_velocity().IsZero():
|
||||
self.rotation_duration = time.time() - self.rotation_start_time
|
||||
return True
|
||||
elif self.angular_damping == 0:
|
||||
# Bar1, bar2, bar3 or bar4
|
||||
return True
|
||||
return False
|
||||
|
||||
def count_touch_times(self, args):
|
||||
entering_entity_id = args[0]
|
||||
if entering_entity_id.Equal(self.id):
|
||||
Report.info(self.name + " touched " + self.trigger.name)
|
||||
self.touched_trigger += 1
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "RigidBody_MaxAngularVelocityWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Init bars values
|
||||
bars = [
|
||||
Bar(name="bar1", init_ang_velocity_on_X=10.0, max_ang_velocity=5),
|
||||
Bar(name="bar2", init_ang_velocity_on_X=10.0, max_ang_velocity=20),
|
||||
Bar(name="bar3", init_ang_velocity_on_X=20.0, max_ang_velocity=20),
|
||||
Bar(name="bar4", init_ang_velocity_on_X=20.0, max_ang_velocity=0,),
|
||||
Bar(name="bar5", init_ang_velocity_on_X=20.0, max_ang_velocity=20, has_trigger=True,),
|
||||
Bar(name="bar6", init_ang_velocity_on_X=20.0, max_ang_velocity=20, has_trigger=True,),
|
||||
]
|
||||
|
||||
# 4.10) Wait
|
||||
Report.critical_result(
|
||||
Tests.did_not_timeout,
|
||||
helper.wait_for_condition(lambda: all([bar.has_waited_enough() for bar in bars]), TIME_OUT),
|
||||
)
|
||||
|
||||
# 4.12) Validate rotation
|
||||
for bar in bars:
|
||||
Report.result(Tests.__dict__["{}_rotation".format(bar.name)], bar.validate_rotation())
|
||||
|
||||
# 5) Compare number of times bar5 and bar6 touched their trigger
|
||||
Report.result(Tests.trigger_touch_times, bars[4].touched_trigger > bars[5].touched_trigger)
|
||||
|
||||
Report.info(bars[4].rotation_duration)
|
||||
Report.info(bars[5].rotation_duration)
|
||||
|
||||
Report.result(Tests.rotation_duration, bars[4].rotation_duration > bars[5].rotation_duration)
|
||||
|
||||
# 6) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_MaxAngularVelocityWorks)
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5340400
|
||||
# Test Case Title : Verify that when Compute inertia is disabled, the user gets to set the moment of inertia
|
||||
# and physX engine work accordingly
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_upper_box = ("Upper Box found", "Upper Box not found")
|
||||
find_lower_box = ("Lower Box found", "Lower Box not found")
|
||||
find_physx_terrain = ("PhysX Terrain found", "PhysX Terrain not found")
|
||||
boxes_collided = ("Boxes collided", "Boxes did not collide")
|
||||
upper_box_did_not_topple_t1 = ("Upper Box did not topple at time t1", "Upper Box toppled at time t1")
|
||||
upper_box_did_not_touch_ground = ("Upper Box did not touch the terrain", "Upper Box touched the terrain")
|
||||
upper_box_did_not_topple_t2 = ("Upper Box did not topple at time t2", "Upper Box toppled at time t2")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_MomentOfInertiaManualSetting():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that assigning a high moment of inertia component causes the entity to exhibit
|
||||
high intertia along the component axis and therefore resist change in motion along that axis.
|
||||
|
||||
Level Description:
|
||||
A box (entity: Upper Box) set above and askew to another box (entity: Lower Box) which is set on a PhysX terrain
|
||||
(entity: PhysX Terrain).
|
||||
Gravity is enabled.
|
||||
Lower box has computed inertia.
|
||||
Upper box has Inertia Diagonal x=10000, y=1, z=1.
|
||||
|
||||
Expected behavior:
|
||||
The upper box falls onto the lower box and tilts extremely slowly toward the terrain. The torque exerted on the
|
||||
upper box by the opposing influences of momentum, normal force from the lower box, and/or ongoing acceleration due
|
||||
to gravity will be greatly resisted by the Inertia Diagonal x=10000 resulting in a tiny change in angular velocity.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve entities
|
||||
4) Check for collision between the two boxes
|
||||
4.5) Wait for the boxes to collide
|
||||
5) Check that the upper box's x-angular velocity is insignificant at time t1 upon collision
|
||||
6) Check that the upper box does not collide with the terrain in a given time
|
||||
6.5) Wait for the given time
|
||||
7) Check that the upper box's x-angular velocity remains insignificant at time t2 after collision
|
||||
8) Exit game mode
|
||||
9) Close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
# Specific wait time in seconds
|
||||
TIME_OUT = 3.0
|
||||
|
||||
# X-angular velocity should be positive and tiny
|
||||
def XAngularVelocityIsValid(x_ang_vel):
|
||||
TOLERANCE = 0.01
|
||||
return 0 < x_ang_vel < TOLERANCE
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_MomentOfInertiaManualSetting")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
upper_box_id = general.find_game_entity("Upper Box")
|
||||
Report.result(Tests.find_upper_box, upper_box_id.IsValid())
|
||||
|
||||
lower_box_id = general.find_game_entity("Lower Box")
|
||||
Report.result(Tests.find_lower_box, lower_box_id.IsValid())
|
||||
|
||||
physx_terrain_id = general.find_game_entity("PhysX Terrain")
|
||||
Report.result(Tests.find_physx_terrain, physx_terrain_id.IsValid())
|
||||
|
||||
# 4) Check for collision between the two boxes
|
||||
class BoxesCollided:
|
||||
value = False
|
||||
|
||||
def on_boxes_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(lower_box_id):
|
||||
Report.info("Boxes collided")
|
||||
BoxesCollided.value = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(upper_box_id)
|
||||
handler.add_callback("OnCollisionBegin", on_boxes_collision_begin)
|
||||
|
||||
# 4.5) Wait for the boxes to collide
|
||||
helper.wait_for_condition(lambda: BoxesCollided.value, TIME_OUT)
|
||||
Report.result(Tests.boxes_collided, BoxesCollided.value)
|
||||
|
||||
# 5) Check that the upper box's x-angular velocity is insignificant at time t1 upon collision
|
||||
# The torque on the upper box caused by its momentum and the normal force from the lower box is resisted at time t1
|
||||
upper_box_angular_velocity = azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "GetAngularVelocity", upper_box_id
|
||||
)
|
||||
Report.info("Upper Box's x-angular velocity at time t1 upon collision: {}".format(upper_box_angular_velocity.x))
|
||||
Report.result(Tests.upper_box_did_not_topple_t1, XAngularVelocityIsValid(upper_box_angular_velocity.x))
|
||||
|
||||
# 6) Check that the upper box does not collide with the terrain in a given time
|
||||
# This will also validate that the angular velocity does not reach a local maximum
|
||||
class UpperBoxCollidedWithTerrain:
|
||||
value = False
|
||||
|
||||
def on_ground_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(physx_terrain_id):
|
||||
Report.info("Upper Box collided with terrain")
|
||||
UpperBoxCollidedWithTerrain.value = True
|
||||
|
||||
handler.add_callback("OnCollisionBegin", on_ground_collision_begin)
|
||||
|
||||
# 6.5) Wait for the given time
|
||||
helper.wait_for_condition(lambda: UpperBoxCollidedWithTerrain.value, TIME_OUT)
|
||||
Report.result(Tests.upper_box_did_not_touch_ground, not UpperBoxCollidedWithTerrain.value)
|
||||
|
||||
# 7) Check that the upper box's x-angular velocity remains insignificant at time t2 after collision
|
||||
# The torque on the upper box caused by gravity and the normal force from the lower box is resisted through time t2
|
||||
upper_box_angular_velocity = azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "GetAngularVelocity", upper_box_id
|
||||
)
|
||||
Report.info("Upper Box's x-angular velocity at time t2 after collision: {}".format(upper_box_angular_velocity.x))
|
||||
Report.result(Tests.upper_box_did_not_topple_t2, XAngularVelocityIsValid(upper_box_angular_velocity.x))
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_MomentOfInertiaManualSetting)
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C14976307
|
||||
# Test Case Title : Check that Set Gravity Enabled works on an entity with gravity that starts as disabled
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_entities = ("Entities are found", "Entities are not found")
|
||||
gravity_initially_disabled = ("Gravity was initially disabled", "Gravity was initially enabled")
|
||||
gravity_enabled = ("Enabled gravity successfully", "Failed to enable gravity")
|
||||
collision_occured = ("Sphere collided with terrain", "Sphere did not collide with terrain")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_SetGravityWorks():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that Set Gravity Enabled works on an entity with gravity that starts as disabled
|
||||
|
||||
Level Description:
|
||||
Terrain (entity) - Terrain entity is created in the level
|
||||
Sphere (entity) - Entity with PhysX rigid body, mesh and collider with gravity disabled placed above
|
||||
the terrain
|
||||
|
||||
Expected Behavior:
|
||||
After 5 seconds, when SetGravity is called, the entity falls to the ground
|
||||
We are checking if entities are valid and enabling the gravity after 5 seconds in game mode to check if ball
|
||||
falls on the terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Gravity check for entity
|
||||
5) Enabling gravity after 5 seconds
|
||||
6) Adding collision handlers for terrain
|
||||
7) Checking if the object collides with terrain
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 3.0
|
||||
WAIT_TIME = 5.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_SetGravityWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_entities, terrain_id.IsValid() and sphere_id.IsValid())
|
||||
|
||||
sphere_gravity_enabled = False
|
||||
class Sphere:
|
||||
sphere_collision_occured = False
|
||||
|
||||
# 4) Gravity check for entities
|
||||
sphere_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.result(Tests.gravity_initially_disabled, not sphere_gravity_enabled)
|
||||
|
||||
# 5) Adding collision handlers for terrain
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere_id):
|
||||
Report.info("Sphere collided with the terrain")
|
||||
Sphere.sphere_collision_occured = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(terrain_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 6) Enabling gravity after 5 seconds
|
||||
general.idle_wait(WAIT_TIME)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", sphere_id, True)
|
||||
sphere_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.result(Tests.gravity_enabled, sphere_gravity_enabled)
|
||||
|
||||
# 7) Checking if the object collides with terrain
|
||||
helper.wait_for_condition(lambda: Sphere.sphere_collision_occured, TIME_OUT)
|
||||
Report.result(Tests.collision_occured, Sphere.sphere_collision_occured)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_SetGravityWorks)
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C4976202
|
||||
Test Case Title : Verify that if the object is moving with Kinetic energy less than
|
||||
the sleep threshold value, then physX will put it to stop after 0.4 secs (once the
|
||||
wake counter goes to zero) if the KE is still below the threshold
|
||||
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_1 = ("Entered game mode_1", "Failed to enter game mode_1" )
|
||||
Trigger1_exists_1 = ("Trigger1 entity was found_1", "Trigger1 entity was not found_1" )
|
||||
Trigger2_exists_1 = ("Trigger2 entity was found_1", "Trigger2 entity was not found_1" )
|
||||
Trigger3_exists_1 = ("Trigger3 entity was found_1", "Trigger3 entity was not found_1" )
|
||||
Pushing_Cube_exists_1 = ("Pushing_Cube entity was found_1", "Pushing_Cube entity was not found_1" )
|
||||
Target_Ball_exists_1 = ("Target_Ball entity was found_1", "Target_Ball entity was not found_1" )
|
||||
threshold_setup_1 = ("The sleep threshold was setup properly_1", "The sleep threshold was not setup properly_1" )
|
||||
cube_collided_with_ball_1 = ("The cube hit the ball the_1", "The cube did not hit the ball the_1" )
|
||||
ball_stopped_moving_1 = ("The ball has stopped moving before timeout_1", "The ball did not stop moving before timeout was reached_1" )
|
||||
trigger_patterns_match_1 = ("The actual trigger pattern matches expected_1", "The actual trigger pattern did not match expected_1" )
|
||||
check_y_z_movement_1 = ("The ball did not move too far in Y or Z_1", "The ball moved farther than expected in Y or Z after collision_1")
|
||||
exit_game_mode_1 = ("Exited game mode_1", "Couldn't exit game mode_1" )
|
||||
|
||||
enter_game_mode_2 = ("Entered game mode_2", "Failed to enter game mode_2" )
|
||||
Trigger1_exists_2 = ("Trigger1 entity was found_2", "Trigger1 entity was not found_2" )
|
||||
Trigger2_exists_2 = ("Trigger2 entity was found_2", "Trigger2 entity was not found_2" )
|
||||
Trigger3_exists_2 = ("Trigger3 entity was found_2", "Trigger3 entity was not found_2" )
|
||||
Pushing_Cube_exists_2 = ("Pushing_Cube entity was found_2", "Pushing_Cube entity was not found_2" )
|
||||
Target_Ball_exists_2 = ("Target_Ball entity was found_2", "Target_Ball entity was not found_2" )
|
||||
threshold_setup_2 = ("The sleep threshold was setup properly_2", "The sleep threshold was not setup properly_2" )
|
||||
cube_collided_with_ball_2 = ("The cube hit the ball the_2", "The cube did not hit the ball the_2" )
|
||||
ball_stopped_moving_2 = ("The ball has stopped moving before timeout_2", "The ball did not stop moving before timeout was reached_2" )
|
||||
trigger_patterns_match_2 = ("The actual trigger pattern matches expected_2", "The actual trigger pattern did not match expected_2" )
|
||||
check_y_z_movement_2 = ("The ball did not move too far in Y or Z_2", "The ball moved farther than expected in Y or Z after collision_2")
|
||||
exit_game_mode_2 = ("Exited game mode_2", "Couldn't exit game mode_2" )
|
||||
|
||||
enter_game_mode_3 = ("Entered game mode_3", "Failed to enter game mode_3" )
|
||||
Trigger1_exists_3 = ("Trigger1 entity was found_3", "Trigger1 entity was not found_3" )
|
||||
Trigger2_exists_3 = ("Trigger2 entity was found_3", "Trigger2 entity was not found_3" )
|
||||
Trigger3_exists_3 = ("Trigger3 entity was found_3", "Trigger3 entity was not found_3" )
|
||||
Pushing_Cube_exists_3 = ("Pushing_Cube entity was found_3", "Pushing_Cube entity was not found_3" )
|
||||
Target_Ball_exists_3 = ("Target_Ball entity was found_3", "Target_Ball entity was not found_3" )
|
||||
threshold_setup_3 = ("The sleep threshold was setup properly_3", "The sleep threshold was not setup properly_3" )
|
||||
cube_collided_with_ball_3 = ("The cube hit the ball the_3", "The cube did not hit the ball the_3" )
|
||||
ball_stopped_moving_3 = ("The ball has stopped moving before timeout_3", "The ball did not stop moving before timeout was reached_3" )
|
||||
trigger_patterns_match_3 = ("The actual trigger pattern matches expected_3", "The actual trigger pattern did not match expected_3" )
|
||||
check_y_z_movement_3 = ("The ball did not move too far in Y or Z_3", "The ball moved farther than expected in Y or Z after collision_3")
|
||||
exit_game_mode_3 = ("Exited game mode_3", "Couldn't exit game mode_3" )
|
||||
|
||||
stop_locations_comparison = ("The stop locations are correctly ordered", "The stop locations are not ordered correctly" )
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_SleepWhenBelowKineticThreshold():
|
||||
"""
|
||||
Summary:
|
||||
A Pushing Cube and a Sphere are suspended over the PhysX Terrain.
|
||||
The cube entity has an initial velocity and on start will move toward the ball
|
||||
and collide with it. When the kinetic energy of the ball is below a certain threshold,
|
||||
it should stop moving.
|
||||
|
||||
Level Description:
|
||||
Terrain (entity):
|
||||
PhysX Terrain component: default settings
|
||||
|
||||
PushingCube (entity): Start Inactive
|
||||
Mesh component: Box shape
|
||||
PhysX Collider component: Box shape; default settings
|
||||
PhysX Rigid Body component: Initial linear velocity: 5m/s in positive X direction;
|
||||
gravity disabled; mass 1.0kg;
|
||||
|
||||
TargetBall (entity):
|
||||
Mesh component: Sphere shape
|
||||
PhysX Collider component: Sphere shape; default settings
|
||||
PhysX Rigid Body component: Linear damping: 0.5; mass 10kg; gravity disabled; sleep threshold: 1.0;
|
||||
|
||||
Trigger1 (entity):
|
||||
Box Shape component: Dimensions (0.1, 1.0, 1.0); Visible: Checked; Game View: Checked
|
||||
PhysX Collider component: Box shape; Dimensions (0.1, 1.0, 1.0);
|
||||
|
||||
Trigger2 (entity):
|
||||
Box Shape component: Dimensions (0.1, 1.0, 1.0); Visible: Checked; Game View: Checked
|
||||
PhysX Collider component: Box shape; Dimensions (0.1, 1.0, 1.0);
|
||||
|
||||
Trigger3 (entity):
|
||||
Box Shape component: Dimensions (0.1, 1.0, 1.0); Visible: Checked; Game View: Checked
|
||||
PhysX Collider component: Box shape; Dimensions (0.1, 1.0, 1.0);
|
||||
|
||||
Expected Behavior:
|
||||
The cube entity should push the ball entity into motion.
|
||||
When the Kinetic Energy of the ball drops below the sleep threshold, it should stop moving.
|
||||
The test steps will run for each sleep threshold value (1.0, 5.0, and 10.0). The ball should stop
|
||||
before touching a specific Trigger (1.0 & Trigger3, 5.0 & Trigger 2, 10.0 & Trigger 1)
|
||||
|
||||
Test Steps:
|
||||
1) Open the level
|
||||
# Steps 2-9 are repeated for each threshold value
|
||||
2) Enter game mode
|
||||
3) Find entities and setup handlers or values
|
||||
4) Activate the PushingCube entity to start movement
|
||||
5) Wait for the ball to be hit by the pushing block
|
||||
6) Wait for the ball to come to a stop in X direction
|
||||
7) Check that the triggers match the expected trigger results
|
||||
8) Check that the ball didn't move too far in Y or Z directions
|
||||
9) Exit game mode
|
||||
10) Run test_steps_per_threshold for remaining threshold values
|
||||
11) Compare the stop locations to each other
|
||||
12) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
TRIGGER_TIMEOUT = 5.0
|
||||
TIMEOUT_SECONDS = 2.0
|
||||
VELOCITY_TOLERANCE = 0.05
|
||||
Y_Z_BUFFER = 0.01
|
||||
|
||||
class TestData:
|
||||
"""
|
||||
This is a placeholder to store the values persisting the duration of the test
|
||||
"""
|
||||
|
||||
threshold_count = 0
|
||||
stop_locations = []
|
||||
|
||||
class Entity(object):
|
||||
"""
|
||||
Base class for Entities to add basic common functionality such as fetch_id, validate_existence
|
||||
"""
|
||||
|
||||
def __init__(self, name, test_steps_run):
|
||||
self.name = name
|
||||
self.test_steps_run = test_steps_run
|
||||
self.id = None
|
||||
self.fetch_id()
|
||||
self.validate_exist()
|
||||
|
||||
def fetch_id(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
|
||||
def validate_exist(self):
|
||||
Report.critical_result(Tests.__dict__[self.name + "_exists_" + str(self.test_steps_run)], self.id.IsValid())
|
||||
|
||||
def get_location(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Trigger(Entity):
|
||||
"""
|
||||
Trigger entities used to tell if the Target_Ball has moved far enough/too far compared to expected distances
|
||||
"""
|
||||
|
||||
def __init__(self, test_steps_run, name):
|
||||
super(Trigger, self).__init__(name, test_steps_run)
|
||||
self.handler = None
|
||||
self.triggered = False
|
||||
self.triggered_by = None
|
||||
self.attach_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
other_id = args[0]
|
||||
self.triggered = True
|
||||
self.triggered_by = azlmbr.entity.GameEntityContextRequestBus(
|
||||
azlmbr.bus.Broadcast, "GetEntityName", other_id
|
||||
)
|
||||
Report.info("{} was triggered by {}.".format(self.name, self.triggered_by))
|
||||
|
||||
def attach_handler(self):
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
class PushingCube(Entity):
|
||||
def __init__(self, test_steps_run, name="Pushing_Cube"):
|
||||
super(PushingCube, self).__init__(name, test_steps_run)
|
||||
|
||||
def activate(self):
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
|
||||
class TargetBall(Entity):
|
||||
def __init__(self, test_steps_run, sleep_threshold, name="Target_Ball"):
|
||||
super(TargetBall, self).__init__(name, test_steps_run)
|
||||
self.expected_sleep_threshold = sleep_threshold
|
||||
self.handler = None
|
||||
self.init_velocity = None
|
||||
self.grabbed_velocity = None
|
||||
self.collided_with_box = False
|
||||
self.stopped_moving_x = False
|
||||
self.trigger_timed_out = True
|
||||
self.pushing_cube_id = None
|
||||
self.setup_sleep_threshold()
|
||||
self.attach_collision_handler()
|
||||
|
||||
def setup_sleep_threshold(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "SetSleepThreshold", self.id, self.expected_sleep_threshold
|
||||
)
|
||||
grabbed_value = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetSleepThreshold", self.id)
|
||||
Report.info("Expected sleep threshold value: {}".format(self.expected_sleep_threshold))
|
||||
Report.info("Actual value: {}".format(grabbed_value))
|
||||
Report.critical_result(
|
||||
Tests.__dict__["threshold_setup_" + str(TestData.threshold_count)],
|
||||
grabbed_value == self.expected_sleep_threshold,
|
||||
)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.pushing_cube_id):
|
||||
Report.info("Push_Box collided with ball.")
|
||||
self.init_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
Report.info_vector3(self.init_velocity, "Initial Velocity of {}".format(self.name))
|
||||
self.collided_with_box = True
|
||||
|
||||
def attach_collision_handler(self):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def has_stopped_moving_in_x(self):
|
||||
"""
|
||||
This method is used as a condition for helper.wait_for_condition()
|
||||
"""
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
if abs(velocity.x) < VELOCITY_TOLERANCE:
|
||||
Report.info("{} has stopped moving in the X direction.".format(self.name))
|
||||
self.stopped_moving_x = True
|
||||
self.trigger_timed_out = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_y_z_delta(self):
|
||||
"""
|
||||
Used to check that the entity has not moved too far in either the Y or Z direction
|
||||
"""
|
||||
|
||||
def is_within_tolerance(velocity_one_direction):
|
||||
return abs(velocity_one_direction) < Y_Z_BUFFER
|
||||
|
||||
Report.info_vector3(self.init_velocity, "Initial Velocity: ")
|
||||
Report.result(
|
||||
Tests.__dict__["check_y_z_movement_" + str(TestData.threshold_count)],
|
||||
is_within_tolerance(self.init_velocity.y) and is_within_tolerance(self.init_velocity.z),
|
||||
)
|
||||
|
||||
# 1) Open the level
|
||||
helper.open_level("Physics", "RigidBody_SleepWhenBelowKineticThreshold")
|
||||
|
||||
def test_steps(sleep_threshold_value, trigger_pattern):
|
||||
TestData.threshold_count += 1
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.__dict__["enter_game_mode_" + str(TestData.threshold_count)])
|
||||
|
||||
# 3) Find entities and setup handlers or values
|
||||
target_ball = TargetBall(TestData.threshold_count, sleep_threshold_value)
|
||||
pushing_cube = PushingCube(TestData.threshold_count)
|
||||
target_ball.pushing_cube_id = pushing_cube.id
|
||||
|
||||
triggers = []
|
||||
for i in range(1, 4):
|
||||
triggers.append(Trigger(TestData.threshold_count, "Trigger" + str(i)))
|
||||
|
||||
# 4) Activate the Pushing_Box entity to start movement
|
||||
pushing_cube.activate()
|
||||
|
||||
# 5) Wait for the ball to be hit by the pushing block
|
||||
helper.wait_for_condition(lambda: target_ball.collided_with_box, TIMEOUT_SECONDS)
|
||||
Report.result(
|
||||
Tests.__dict__["cube_collided_with_ball_" + str(TestData.threshold_count)], target_ball.collided_with_box
|
||||
)
|
||||
|
||||
# 6) Wait for the ball to come to a stop in X direction
|
||||
helper.wait_for_condition(target_ball.has_stopped_moving_in_x, TRIGGER_TIMEOUT)
|
||||
Report.result(
|
||||
Tests.__dict__["ball_stopped_moving_" + str(TestData.threshold_count)], target_ball.stopped_moving_x
|
||||
)
|
||||
|
||||
# record the location of the stopping point
|
||||
TestData.stop_locations.append(target_ball.get_location())
|
||||
|
||||
# 7) Check that the triggers match the expected trigger results
|
||||
trigger_result = (triggers[0].triggered, triggers[1].triggered, triggers[2].triggered)
|
||||
triggers_matched = trigger_result == trigger_pattern
|
||||
Report.result(Tests.__dict__["trigger_patterns_match_" + str(TestData.threshold_count)], triggers_matched)
|
||||
|
||||
# 8) Check that the ball didn't move too far in Y or Z directions
|
||||
target_ball.check_y_z_delta()
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.__dict__["exit_game_mode_" + str(TestData.threshold_count)])
|
||||
|
||||
# 10) Run test_steps_per_threshold for each threshold value
|
||||
test_steps(1.0, (True, True, False))
|
||||
test_steps(5.0, (True, False, False))
|
||||
test_steps(10.0, (False, False, False))
|
||||
|
||||
# 11) Compare the stop locations to each other
|
||||
order = TestData.stop_locations[0].x > TestData.stop_locations[1].x > TestData.stop_locations[2].x
|
||||
Report.result(Tests.stop_locations_comparison, order)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_SleepWhenBelowKineticThreshold)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976204
|
||||
# Test Case Title : Verify that when Start Asleep is checked, the object in air does not fall down due to
|
||||
# gravity or does not start moving with initial linear velocity assigned to it when switched to game mode
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
find_sphere = ("Sphere found", "Sphere not found")
|
||||
gravity_enabled = ("Gravity is enabled", "Gravity is disabled")
|
||||
start_asleep_enabled = ("Start asleep is enabled", "Start asleep is disabled")
|
||||
sphere_position_fixed = ("Sphere position did not change", "Sphere position changed")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def RigidBody_StartAsleepWorks():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Verify that when Start Asleep is checked, the object in air does not fall down due to gravity or
|
||||
does not start moving with initial linear velocity assigned to it when switched to game mode
|
||||
|
||||
Level Description:
|
||||
Terrain (entity) - Entity with PhysX Terrain component
|
||||
Sphere (entity) - Entity with components PhysX Rigid Body, PhysX Collider and Rendering Mesh (sphere shape)
|
||||
initial velocity in x direction - 5 m/s
|
||||
gravity enabled, Start asleep enabled.
|
||||
|
||||
Expected Behavior:
|
||||
Nothing happens. The sphere is in its position in the air.
|
||||
We are checking if the sphere is in the same position as it was earlier.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Get the initial x,y,z positions of the sphere
|
||||
5) Check gravity, start asleep values for the entity
|
||||
6) Wait until timeout to check if sphere position changed
|
||||
7) Check the final x, y, z positions of the sphere
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 3.0 # waits for 3 seconds to verify if the sphere moved
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "RigidBody_StartAsleepWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
Report.critical_result(Tests.find_sphere, sphere_id.IsValid())
|
||||
|
||||
# 4) Get the initial x,y,z positions of the sphere
|
||||
init_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
|
||||
# 5) Check gravity, start asleep values for the entity
|
||||
is_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.critical_result(Tests.gravity_enabled, is_gravity_enabled)
|
||||
is_awake = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsAwake", sphere_id)
|
||||
Report.critical_result(Tests.start_asleep_enabled, not is_awake)
|
||||
|
||||
# 6) Wait until timeout to check if sphere position changed
|
||||
general.idle_wait(TIME_OUT)
|
||||
|
||||
# 7) Check the final x, y, z positions of the sphere
|
||||
final_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
Report.critical_result(Tests.sphere_position_fixed, init_sphere_pos.Equal(final_sphere_pos))
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_StartAsleepWorks)
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976206
|
||||
# Test Case Title : Verify that when Gravity enables is checked, the object falls down due to gravity [sic]
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Ball entity found", "Ball entity not found")
|
||||
find_terrain = ("Terrain entity found", "Terrain entity not found")
|
||||
gravity_started_disabled = ("Gravity started disabled", "Gravity didn't start disabled")
|
||||
gravity_set_enabled = ("Gravity has been enabled", "Gravity wasn't enabled")
|
||||
ball_fell = ("Ball fell", "Ball didn't fall")
|
||||
touched_ground = ("Ball touched the ground", "Ball did not touch the ground")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
# Wait times defined in frames for waiting while in game mode
|
||||
class Frames:
|
||||
entity_load = 2
|
||||
delta_frames = 1
|
||||
|
||||
|
||||
def RigidBody_StartGravityEnabledWorks():
|
||||
"""
|
||||
Summary:
|
||||
Runs automated test to verify that when the gravity enabled checkbox is ticked,
|
||||
the object will fall down due to gravity.
|
||||
|
||||
Level Description:
|
||||
A ball entity is suspended in the air with gravity disabled by default
|
||||
Below the ball is a terrain entity with a PhysX Terrain component
|
||||
|
||||
Expected Behavior:
|
||||
The level opens and by default gravity is not enabled on the ball.
|
||||
When game mode is entered, the ball should not fall until gravity is enabled,
|
||||
then the ball should fall and collide with the terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Find the entities
|
||||
3) Make sure that gravity is turned off by default
|
||||
4) Get the Z position before enabling gravity
|
||||
5) Activate gravity
|
||||
6) Check that the ball is falling towards the terrain
|
||||
7) Check that there is a collision with the PhysX Terrain
|
||||
8) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
timeout_seconds = 3.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.open_level("Physics", "RigidBody_StartGravityEnabledWorks")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
class Ball:
|
||||
id = None
|
||||
gravity_enabled = None
|
||||
starting_height = None
|
||||
current_height = None
|
||||
fell = False
|
||||
touched_ground = False
|
||||
|
||||
general.idle_wait_frames(Frames.entity_load)
|
||||
|
||||
ball_id = general.find_game_entity("RigidBody")
|
||||
Ball.id = ball_id
|
||||
Report.critical_result(Tests.find_ball, Ball.id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# 3) Make sure gravity is off from the start
|
||||
Ball.gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Ball.id)
|
||||
Report.result(Tests.gravity_started_disabled, not Ball.gravity_enabled)
|
||||
|
||||
# 4) Get the Z position before enabling the physics
|
||||
Ball.starting_height = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
|
||||
|
||||
# 5) Activate gravity
|
||||
general.idle_wait_frames(Frames.delta_frames)
|
||||
Report.info("Enabling Gravity")
|
||||
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", Ball.id, True)
|
||||
|
||||
Ball.gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Ball.id)
|
||||
Report.critical_result(Tests.gravity_set_enabled, Ball.gravity_enabled)
|
||||
|
||||
# 6) Compare the Z position after enabling the gravity and check that there is a collision with the PhysX Terrain
|
||||
def ball_moved_down():
|
||||
Ball.current_height = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
|
||||
if Ball.current_height < (Ball.starting_height - 1):
|
||||
Ball.fell = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(terrain_id):
|
||||
Report.info("Touched ground")
|
||||
Ball.touched_ground = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(Ball.id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
helper.wait_for_condition(lambda : (ball_moved_down() and Ball.touched_ground), timeout_seconds)
|
||||
Report.info("Ball.start_height: {} Ball.current_height: {}".format(Ball.starting_height, Ball.current_height))
|
||||
Report.result(Tests.ball_fell, Ball.fell)
|
||||
Report.result(Tests.touched_ground, Ball.touched_ground)
|
||||
|
||||
# 8) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(RigidBody_StartGravityEnabledWorks)
|
||||
Reference in New Issue
Block a user