merging latest dev
Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"ContainerEntity": {
|
||||
"Id": "ContainerEntity",
|
||||
"Name": "PinkFlower",
|
||||
"Components": {
|
||||
"Component_[10444337162843472597]": {
|
||||
"$type": "EditorLockComponent",
|
||||
"Id": 10444337162843472597
|
||||
},
|
||||
"Component_[14431042590323756177]": {
|
||||
"$type": "EditorEntitySortComponent",
|
||||
"Id": 14431042590323756177,
|
||||
"ChildEntityOrderEntryArray": [
|
||||
{
|
||||
"EntityId": "Entity_[491002114939]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Component_[14577735453176806353]": {
|
||||
"$type": "EditorDisabledCompositionComponent",
|
||||
"Id": 14577735453176806353
|
||||
},
|
||||
"Component_[15674021346798629563]": {
|
||||
"$type": "EditorInspectorComponent",
|
||||
"Id": 15674021346798629563
|
||||
},
|
||||
"Component_[16784074985702513600]": {
|
||||
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
|
||||
"Id": 16784074985702513600,
|
||||
"Parent Entity": ""
|
||||
},
|
||||
"Component_[3351614541100572773]": {
|
||||
"$type": "EditorOnlyEntityComponent",
|
||||
"Id": 3351614541100572773
|
||||
},
|
||||
"Component_[3600658560328167663]": {
|
||||
"$type": "EditorPrefabComponent",
|
||||
"Id": 3600658560328167663
|
||||
},
|
||||
"Component_[6155673728651558934]": {
|
||||
"$type": "EditorEntityIconComponent",
|
||||
"Id": 6155673728651558934
|
||||
},
|
||||
"Component_[8458684662170321289]": {
|
||||
"$type": "EditorVisibilityComponent",
|
||||
"Id": 8458684662170321289
|
||||
},
|
||||
"Component_[8705192117416252351]": {
|
||||
"$type": "SelectionComponent",
|
||||
"Id": 8705192117416252351
|
||||
},
|
||||
"Component_[8801280051488695852]": {
|
||||
"$type": "EditorPendingCompositionComponent",
|
||||
"Id": 8801280051488695852
|
||||
}
|
||||
}
|
||||
},
|
||||
"Entities": {
|
||||
"Entity_[491002114939]": {
|
||||
"Id": "Entity_[491002114939]",
|
||||
"Name": "PinkFlower",
|
||||
"Components": {
|
||||
"Component_[11083205340162142682]": {
|
||||
"$type": "EditorLockComponent",
|
||||
"Id": 11083205340162142682
|
||||
},
|
||||
"Component_[11327363779873517]": {
|
||||
"$type": "EditorOnlyEntityComponent",
|
||||
"Id": 11327363779873517
|
||||
},
|
||||
"Component_[12717389211269537921]": {
|
||||
"$type": "EditorEntitySortComponent",
|
||||
"Id": 12717389211269537921
|
||||
},
|
||||
"Component_[12826285685970138542]": {
|
||||
"$type": "AZ::Render::EditorMeshComponent",
|
||||
"Id": 12826285685970138542,
|
||||
"Controller": {
|
||||
"Configuration": {
|
||||
"ModelAsset": {
|
||||
"assetId": {
|
||||
"guid": "{549F4C4D-A7D9-5F2A-A6BD-F24C8BC43BBF}",
|
||||
"subId": 280086017
|
||||
},
|
||||
"assetHint": "assets/objects/foliage/grass_flower_pink.azmodel"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Component_[14101419970865342875]": {
|
||||
"$type": "EditorPendingCompositionComponent",
|
||||
"Id": 14101419970865342875
|
||||
},
|
||||
"Component_[14770464075286403207]": {
|
||||
"$type": "EditorVisibilityComponent",
|
||||
"Id": 14770464075286403207
|
||||
},
|
||||
"Component_[15205142653091190082]": {
|
||||
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
|
||||
"Id": 15205142653091190082,
|
||||
"Parent Entity": "ContainerEntity"
|
||||
},
|
||||
"Component_[15510898811147803772]": {
|
||||
"$type": "EditorInspectorComponent",
|
||||
"Id": 15510898811147803772,
|
||||
"ComponentOrderEntryArray": [
|
||||
{
|
||||
"ComponentId": 15205142653091190082
|
||||
},
|
||||
{
|
||||
"ComponentId": 12826285685970138542,
|
||||
"SortIndex": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"Component_[15988401742428977134]": {
|
||||
"$type": "EditorDisabledCompositionComponent",
|
||||
"Id": 15988401742428977134
|
||||
},
|
||||
"Component_[4300550837037679336]": {
|
||||
"$type": "EditorEntityIconComponent",
|
||||
"Id": 4300550837037679336
|
||||
},
|
||||
"Component_[996914988793716659]": {
|
||||
"$type": "SelectionComponent",
|
||||
"Id": 996914988793716659
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4c1d04d687bdce69965965c290c907b3f9a730a7ea73874b734e1c7ff41426d9
|
||||
size 3832768
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:27f87510ae07771dbad3e430e31502b1d1d13dee868f143b7f1de2a7febc8eb9
|
||||
size 7046400
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"values": [
|
||||
{
|
||||
"$type": "ScriptProcessorRule",
|
||||
"scriptFilename": "Assets/TestAnim/scene_export_actor.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
import traceback, sys, uuid, os, json
|
||||
|
||||
#
|
||||
# Example for exporting ActorGroup scene rules
|
||||
#
|
||||
|
||||
def log_exception_traceback():
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
data = traceback.format_exception(exc_type, exc_value, exc_tb)
|
||||
print(str(data))
|
||||
|
||||
def get_node_names(sceneGraph, nodeTypeName, testEndPoint = False, validList = None):
|
||||
import azlmbr.scene.graph
|
||||
import scene_api.scene_data
|
||||
|
||||
node = sceneGraph.get_root()
|
||||
nodeList = []
|
||||
children = []
|
||||
paths = []
|
||||
|
||||
while node.IsValid():
|
||||
# store children to process after siblings
|
||||
if sceneGraph.has_node_child(node):
|
||||
children.append(sceneGraph.get_node_child(node))
|
||||
|
||||
nodeName = scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node))
|
||||
paths.append(nodeName.get_path())
|
||||
|
||||
include = True
|
||||
|
||||
if (validList is not None):
|
||||
include = False # if a valid list filter provided, assume to not include node name
|
||||
name_parts = nodeName.get_path().split('.')
|
||||
for valid in validList:
|
||||
if (valid in name_parts[-1]):
|
||||
include = True
|
||||
break
|
||||
|
||||
# store any node that has provides specifc data content
|
||||
nodeContent = sceneGraph.get_node_content(node)
|
||||
if include and nodeContent.CastWithTypeName(nodeTypeName):
|
||||
if testEndPoint is not None:
|
||||
include = sceneGraph.is_node_end_point(node) is testEndPoint
|
||||
if include:
|
||||
if (len(nodeName.get_path())):
|
||||
nodeList.append(scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node)))
|
||||
|
||||
# advance to next node
|
||||
if sceneGraph.has_node_sibling(node):
|
||||
node = sceneGraph.get_node_sibling(node)
|
||||
elif children:
|
||||
node = children.pop()
|
||||
else:
|
||||
node = azlmbr.scene.graph.NodeIndex()
|
||||
|
||||
return nodeList, paths
|
||||
|
||||
def generate_mesh_group(scene, sceneManifest, meshDataList, paths):
|
||||
# Compute the name of the scene file
|
||||
clean_filename = scene.sourceFilename.replace('.', '_')
|
||||
mesh_group_name = os.path.basename(clean_filename)
|
||||
|
||||
# make the mesh group
|
||||
mesh_group = sceneManifest.add_mesh_group(mesh_group_name)
|
||||
mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, clean_filename)) + '}'
|
||||
|
||||
# add all nodes to this mesh group
|
||||
for activeMeshIndex in range(len(meshDataList)):
|
||||
mesh_name = meshDataList[activeMeshIndex]
|
||||
mesh_path = mesh_name.get_path()
|
||||
sceneManifest.mesh_group_select_node(mesh_group, mesh_path)
|
||||
|
||||
def create_shape_configuration(nodeName):
|
||||
import scene_api.physics_data
|
||||
|
||||
if(nodeName in ['_foot_','_wrist_']):
|
||||
shapeConfiguration = scene_api.physics_data.BoxShapeConfiguration()
|
||||
shapeConfiguration.scale = [1.1, 1.1, 1.1]
|
||||
shapeConfiguration.dimensions = [2.1, 3.1, 4.1]
|
||||
return shapeConfiguration
|
||||
else:
|
||||
shapeConfiguration = scene_api.physics_data.CapsuleShapeConfiguration()
|
||||
shapeConfiguration.scale = [1.0, 1.0, 1.0]
|
||||
shapeConfiguration.height = 1.0
|
||||
shapeConfiguration.radius = 1.0
|
||||
return shapeConfiguration
|
||||
|
||||
def create_collider_configuration(nodeName):
|
||||
import scene_api.physics_data
|
||||
|
||||
colliderConfiguration = scene_api.physics_data.ColliderConfiguration()
|
||||
colliderConfiguration.Position = [0.1, 0.1, 0.2]
|
||||
colliderConfiguration.Rotation = [45.0, 35.0, 25.0]
|
||||
return colliderConfiguration
|
||||
|
||||
def generate_physics_nodes(actorPhysicsSetupRule, nodeNameList):
|
||||
import scene_api.physics_data
|
||||
|
||||
hitDetectionConfig = scene_api.physics_data.CharacterColliderConfiguration()
|
||||
simulatedObjectColliderConfig = scene_api.physics_data.CharacterColliderConfiguration()
|
||||
clothConfig = scene_api.physics_data.CharacterColliderConfiguration()
|
||||
ragdollConfig = scene_api.physics_data.RagdollConfiguration()
|
||||
|
||||
for nodeName in nodeNameList:
|
||||
shapeConfiguration = create_shape_configuration(nodeName)
|
||||
colliderConfiguration = create_collider_configuration(nodeName)
|
||||
hitDetectionConfig.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
|
||||
simulatedObjectColliderConfig.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
|
||||
clothConfig.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
|
||||
#
|
||||
ragdollNode = scene_api.physics_data.RagdollNodeConfiguration()
|
||||
ragdollNode.JointConfig.Name = nodeName
|
||||
ragdollConfig.add_ragdoll_node_configuration(ragdollNode)
|
||||
ragdollConfig.colliders.add_character_collider_node_configuration_node(nodeName, colliderConfiguration, shapeConfiguration)
|
||||
|
||||
actorPhysicsSetupRule.set_simulated_object_collider_config(simulatedObjectColliderConfig)
|
||||
actorPhysicsSetupRule.set_hit_detection_config(hitDetectionConfig)
|
||||
actorPhysicsSetupRule.set_cloth_config(clothConfig)
|
||||
actorPhysicsSetupRule.set_ragdoll_config(ragdollConfig)
|
||||
|
||||
def generate_actor_group(scene, sceneManifest, meshDataList, paths):
|
||||
import scene_api.scene_data
|
||||
import scene_api.physics_data
|
||||
import scene_api.actor_group
|
||||
|
||||
# fetch bone data
|
||||
validNames = ['_neck_','_pelvis_','_leg_','_knee_','_spine_','_arm_','_clavicle_','_head_','_elbow_','_wrist_']
|
||||
graph = scene_api.scene_data.SceneGraph(scene.graph)
|
||||
nodeList, allNodePaths = get_node_names(graph, 'BoneData', validList = validNames)
|
||||
|
||||
nodeNameList = []
|
||||
for activeMeshIndex, nodeName in enumerate(nodeList):
|
||||
nodeNameList.append(nodeName.get_name())
|
||||
|
||||
# add comment
|
||||
commentRule = scene_api.actor_group.CommentRule()
|
||||
commentRule.text = str(nodeNameList)
|
||||
|
||||
# ActorPhysicsSetupRule
|
||||
actorPhysicsSetupRule = scene_api.actor_group.ActorPhysicsSetupRule()
|
||||
generate_physics_nodes(actorPhysicsSetupRule, nodeNameList)
|
||||
|
||||
# add scale of the Actor rule
|
||||
actorScaleRule = scene_api.actor_group.ActorScaleRule()
|
||||
actorScaleRule.scaleFactor = 2.0
|
||||
|
||||
# add coordinate system rule
|
||||
coordinateSystemRule = scene_api.actor_group.CoordinateSystemRule()
|
||||
coordinateSystemRule.useAdvancedData = False
|
||||
|
||||
# add morph target rule
|
||||
morphTargetRule = scene_api.actor_group.MorphTargetRule()
|
||||
morphTargetRule.targets.select_targets([nodeNameList[0]], nodeNameList)
|
||||
|
||||
# add skeleton optimization rule
|
||||
skeletonOptimizationRule = scene_api.actor_group.SkeletonOptimizationRule()
|
||||
skeletonOptimizationRule.autoSkeletonLOD = True
|
||||
skeletonOptimizationRule.criticalBonesList.select_targets([nodeNameList[0:2]], nodeNameList)
|
||||
|
||||
# add LOD rule
|
||||
lodRule = scene_api.actor_group.LodRule()
|
||||
lodRule0 = lodRule.add_lod_level(0)
|
||||
lodRule0.select_targets([nodeNameList[1:4]], nodeNameList)
|
||||
|
||||
actorGroup = scene_api.actor_group.ActorGroup()
|
||||
actorGroup.name = os.path.basename(scene.sourceFilename)
|
||||
actorGroup.add_rule(actorScaleRule)
|
||||
actorGroup.add_rule(coordinateSystemRule)
|
||||
actorGroup.add_rule(skeletonOptimizationRule)
|
||||
actorGroup.add_rule(morphTargetRule)
|
||||
actorGroup.add_rule(lodRule)
|
||||
actorGroup.add_rule(actorPhysicsSetupRule)
|
||||
actorGroup.add_rule(commentRule)
|
||||
sceneManifest.manifest['values'].append(actorGroup.to_dict())
|
||||
|
||||
def update_manifest(scene):
|
||||
import json, uuid, os
|
||||
import azlmbr.scene.graph
|
||||
import scene_api.scene_data
|
||||
|
||||
graph = scene_api.scene_data.SceneGraph(scene.graph)
|
||||
mesh_name_list, all_node_paths = get_node_names(graph, 'MeshData')
|
||||
scene_manifest = scene_api.scene_data.SceneManifest()
|
||||
generate_actor_group(scene, scene_manifest, mesh_name_list, all_node_paths)
|
||||
generate_mesh_group(scene, scene_manifest, mesh_name_list, all_node_paths)
|
||||
|
||||
# Convert the manifest to a JSON string and return it
|
||||
return scene_manifest.export()
|
||||
|
||||
sceneJobHandler = None
|
||||
|
||||
def on_update_manifest(args):
|
||||
try:
|
||||
scene = args[0]
|
||||
return update_manifest(scene)
|
||||
except RuntimeError as err:
|
||||
print (f'ERROR - {err}')
|
||||
log_exception_traceback()
|
||||
except:
|
||||
log_exception_traceback()
|
||||
|
||||
global sceneJobHandler
|
||||
sceneJobHandler.disconnect()
|
||||
sceneJobHandler = None
|
||||
|
||||
# try to create SceneAPI handler for processing
|
||||
try:
|
||||
import azlmbr.scene
|
||||
|
||||
sceneJobHandler = azlmbr.scene.ScriptBuildingNotificationBusHandler()
|
||||
sceneJobHandler.connect()
|
||||
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
|
||||
except:
|
||||
sceneJobHandler = None
|
||||
@@ -0,0 +1,66 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
import traceback, sys, uuid, os, json
|
||||
|
||||
import scene_export_utils
|
||||
import scene_api.motion_group
|
||||
|
||||
#
|
||||
# Example for exporting MotionGroup scene rules
|
||||
#
|
||||
|
||||
def update_manifest(scene):
|
||||
import azlmbr.scene.graph
|
||||
import scene_api.scene_data
|
||||
|
||||
# create a SceneManifest
|
||||
sceneManifest = scene_api.scene_data.SceneManifest()
|
||||
|
||||
# create a MotionGroup
|
||||
motionGroup = scene_api.motion_group.MotionGroup()
|
||||
motionGroup.name = os.path.basename(scene.sourceFilename.replace('.', '_'))
|
||||
|
||||
motionAdditiveRule = scene_api.motion_group.MotionAdditiveRule()
|
||||
motionAdditiveRule.sampleFrame = 2
|
||||
motionGroup.add_rule(motionAdditiveRule)
|
||||
|
||||
motionScaleRule = motionGroup.create_rule(scene_api.motion_group.MotionScaleRule())
|
||||
motionScaleRule.scaleFactor = 1.1
|
||||
motionGroup.add_rule(motionScaleRule)
|
||||
|
||||
# add motion group to scene manifest
|
||||
sceneManifest.add_motion_group(motionGroup)
|
||||
|
||||
# Convert the manifest to a JSON string and return it
|
||||
return sceneManifest.export()
|
||||
|
||||
sceneJobHandler = None
|
||||
|
||||
def on_update_manifest(args):
|
||||
try:
|
||||
scene = args[0]
|
||||
return update_manifest(scene)
|
||||
except RuntimeError as err:
|
||||
print (f'ERROR - {err}')
|
||||
scene_export_utils.log_exception_traceback()
|
||||
except:
|
||||
scene_export_utils.log_exception_traceback()
|
||||
|
||||
global sceneJobHandler
|
||||
sceneJobHandler.disconnect()
|
||||
sceneJobHandler = None
|
||||
|
||||
# try to create SceneAPI handler for processing
|
||||
try:
|
||||
import azlmbr.scene
|
||||
|
||||
sceneJobHandler = azlmbr.scene.ScriptBuildingNotificationBusHandler()
|
||||
sceneJobHandler.connect()
|
||||
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
|
||||
except:
|
||||
sceneJobHandler = None
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"values": [
|
||||
{
|
||||
"$type": "ScriptProcessorRule",
|
||||
"scriptFilename": "Assets/TestAnim/scene_export_motion.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:011454252e40c927343cce16296412f02f45d1f345c75c036651bdcca473bda5
|
||||
size 2672
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7bafcc4aefab827e1414e64bcdde235500b51392e52c9ccd588b2d7a24b865a0
|
||||
size 20214
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ac841c02e81c9ae23476522b7e517889d746d4ff32b3251ac4360168f39b30a3
|
||||
size 450708
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3653ca7fb42c7e5991815c17fc9ebeab14a1aa861bfb1d37e233ada66a835f00
|
||||
size 7188
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a30b85428202c548e77cdcc0a595f9f26e82d29622be26891d569f4779a75830
|
||||
size 1802388
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a3d76b7c93f8873ca7c4013dcc4eff887c25552c9c5847290481306656b22069
|
||||
size 3668
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:219f57137c5bd44093762ea9d0fc24308679956b980f26bf194edd3a1798fcda
|
||||
size 3668
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:96ed61c66d1d1f71e940ab75899ee253007e704004e1157c900c6308151a8bf1
|
||||
size 1802388
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:70a7dc4e2455624067e842b059954f6c4bb9b0debc3cb1ffa783b14bffc61786
|
||||
size 7188
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7f5a945c61f92f3da77dfd9fdd2c95aa257f4df6bcb82483c608ab660bb72e5f
|
||||
size 450708
|
||||
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
import traceback, sys, uuid, os, json
|
||||
|
||||
#
|
||||
# Utility methods for processing scenes
|
||||
#
|
||||
|
||||
def log_exception_traceback():
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
data = traceback.format_exception(exc_type, exc_value, exc_tb)
|
||||
print(str(data))
|
||||
|
||||
def get_node_names(sceneGraph, nodeTypeName, testEndPoint = False, validList = None):
|
||||
import azlmbr.scene.graph
|
||||
import scene_api.scene_data
|
||||
|
||||
node = sceneGraph.get_root()
|
||||
nodeList = []
|
||||
children = []
|
||||
paths = []
|
||||
|
||||
while node.IsValid():
|
||||
# store children to process after siblings
|
||||
if sceneGraph.has_node_child(node):
|
||||
children.append(sceneGraph.get_node_child(node))
|
||||
|
||||
nodeName = scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node))
|
||||
paths.append(nodeName.get_path())
|
||||
|
||||
include = True
|
||||
|
||||
if (validList is not None):
|
||||
include = False # if a valid list filter provided, assume to not include node name
|
||||
name_parts = nodeName.get_path().split('.')
|
||||
for valid in validList:
|
||||
if (valid in name_parts[-1]):
|
||||
include = True
|
||||
break
|
||||
|
||||
# store any node that has provides specifc data content
|
||||
nodeContent = sceneGraph.get_node_content(node)
|
||||
if include and nodeContent.CastWithTypeName(nodeTypeName):
|
||||
if testEndPoint is not None:
|
||||
include = sceneGraph.is_node_end_point(node) is testEndPoint
|
||||
if include:
|
||||
if (len(nodeName.get_path())):
|
||||
nodeList.append(scene_api.scene_data.SceneGraphName(sceneGraph.get_node_name(node)))
|
||||
|
||||
# advance to next node
|
||||
if sceneGraph.has_node_sibling(node):
|
||||
node = sceneGraph.get_node_sibling(node)
|
||||
elif children:
|
||||
node = children.pop()
|
||||
else:
|
||||
node = azlmbr.scene.graph.NodeIndex()
|
||||
|
||||
return nodeList, paths
|
||||
@@ -1,10 +1,10 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
import traceback, logging, json
|
||||
from typing import Tuple, List
|
||||
|
||||
@@ -14,30 +14,44 @@ from scene_api.scene_data import SceneGraphName
|
||||
|
||||
|
||||
def log_exception_traceback():
|
||||
"""
|
||||
Outputs an exception stacktrace.
|
||||
"""
|
||||
"""Outputs an exception stacktrace."""
|
||||
data = traceback.format_exc()
|
||||
logger = logging.getLogger('python')
|
||||
logger.error(data)
|
||||
|
||||
|
||||
def sanitize_name_for_disk(name: str):
|
||||
"""
|
||||
Removes illegal filename characters from a string.
|
||||
def sanitize_name_for_disk(name: str) -> str:
|
||||
"""Removes illegal filename characters from a string.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name :
|
||||
String to clean.
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Name with illegal characters removed.
|
||||
|
||||
:param name: String to clean.
|
||||
:return: Name with illegal characters removed.
|
||||
"""
|
||||
return "".join(char for char in name if char not in "|<>:\"/?*\\")
|
||||
|
||||
|
||||
def get_mesh_node_names(scene_graph: sceneData.SceneGraph) -> Tuple[List[SceneGraphName], List[str]]:
|
||||
"""
|
||||
Returns a tuple of all the mesh nodes as well as all the node paths
|
||||
"""Returns a tuple of all the mesh nodes as well as all the node paths
|
||||
|
||||
Parameters
|
||||
----------
|
||||
scene_graph :
|
||||
Scene graph to search
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple[List[SceneGraphName], List[str]]
|
||||
Tuple of [Mesh Nodes, All Node Paths]
|
||||
|
||||
:param scene_graph: Scene graph to search
|
||||
:return: Tuple of [Mesh Nodes, All Node Paths]
|
||||
"""
|
||||
import azlmbr.scene as sceneApi
|
||||
import azlmbr.scene.graph
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import azlmbr.bus
|
||||
import azlmbr.math
|
||||
|
||||
from scene_api.scene_data import PrimitiveShape, DecompositionMode
|
||||
from scene_api.scene_data import PrimitiveShape, DecompositionMode, ColorChannel, TangentSpaceSource, TangentSpaceMethod
|
||||
from scene_helpers import *
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ def add_physx_meshes(scene_manifest: sceneData.SceneManifest, source_file_name:
|
||||
triangle = scene_manifest.add_physx_triangle_mesh_group(source_file_name + "_triangle", False, True, True, True, True, True)
|
||||
scene_manifest.physx_mesh_group_add_selected_unselected_nodes(triangle, [first_mesh], all_except_first_mesh)
|
||||
|
||||
|
||||
def update_manifest(scene):
|
||||
import uuid, os
|
||||
import azlmbr.scene.graph
|
||||
@@ -114,10 +115,11 @@ def update_manifest(scene):
|
||||
if node != mesh_path:
|
||||
scene_manifest.mesh_group_unselect_node(mesh_group, node)
|
||||
|
||||
scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", 1, "Col0", 2, "Col0", 2, 3)
|
||||
scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", ColorChannel.GREEN, "Col0",
|
||||
ColorChannel.BLUE, "Col0", ColorChannel.BLUE, ColorChannel.ALPHA)
|
||||
scene_manifest.mesh_group_add_advanced_mesh_rule(mesh_group, True, False, True, "Col0")
|
||||
scene_manifest.mesh_group_add_skin_rule(mesh_group, 3, 0.002)
|
||||
scene_manifest.mesh_group_add_tangent_rule(mesh_group, 1, 0)
|
||||
scene_manifest.mesh_group_add_tangent_rule(mesh_group, TangentSpaceSource.MIKKT_GENERATION, TangentSpaceMethod.TSPACE_BASIC)
|
||||
|
||||
# Create an editor entity
|
||||
entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name)
|
||||
|
||||
@@ -47,18 +47,4 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
COMPONENT
|
||||
Atom
|
||||
)
|
||||
ly_add_pytest(
|
||||
NAME AutomatedTesting::Atom_TestSuite_Main_GPU_Optimized
|
||||
TEST_SUITE main
|
||||
TEST_REQUIRES gpu
|
||||
TEST_SERIAL
|
||||
TIMEOUT 1200
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_GPU_Optimized.py
|
||||
RUNTIME_DEPENDENCIES
|
||||
AssetProcessor
|
||||
AutomatedTesting.Assets
|
||||
Editor
|
||||
COMPONENT
|
||||
Atom
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -4,129 +4,82 @@ For complete copyright and license terms please see the LICENSE at the root of t
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import zipfile
|
||||
|
||||
import pytest
|
||||
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator
|
||||
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
|
||||
from Atom.atom_utils.atom_component_helper import compare_screenshot_to_golden_image, golden_images_directory
|
||||
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
from .atom_utils.atom_component_helper import compare_screenshot_similarity, ImageComparisonTestFailure
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
|
||||
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
|
||||
|
||||
|
||||
def golden_images_directory():
|
||||
"""
|
||||
Uses this file location to return the valid location for golden image files.
|
||||
:return: The path to the golden_images directory, but raises an IOError if the golden_images directory is missing.
|
||||
"""
|
||||
current_file_directory = os.path.join(os.path.dirname(__file__))
|
||||
golden_images_dir = os.path.join(current_file_directory, 'golden_images')
|
||||
|
||||
if not os.path.exists(golden_images_dir):
|
||||
raise IOError(
|
||||
f'golden_images" directory was not found at path "{golden_images_dir}"'
|
||||
f'Please add a "golden_images" directory inside: "{current_file_directory}"'
|
||||
)
|
||||
|
||||
return golden_images_dir
|
||||
|
||||
|
||||
def create_screenshots_archive(screenshot_path):
|
||||
"""
|
||||
Creates a new zip file archive at archive_path containing all files listed within archive_path.
|
||||
:param screenshot_path: location containing the files to archive, the zip archive file will also be saved here.
|
||||
:return: None, but creates a new zip file archive inside path containing all of the files inside archive_path.
|
||||
"""
|
||||
files_to_archive = []
|
||||
|
||||
# Search for .png and .ppm files to add to the zip archive file.
|
||||
for (folder_name, sub_folders, file_names) in os.walk(screenshot_path):
|
||||
for file_name in file_names:
|
||||
if file_name.endswith(".png") or file_name.endswith(".ppm"):
|
||||
file_path = os.path.join(folder_name, file_name)
|
||||
files_to_archive.append(file_path)
|
||||
|
||||
# Setup variables for naming the zip archive file.
|
||||
timestamp = datetime.datetime.now().timestamp()
|
||||
formatted_timestamp = datetime.datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d_%H-%M-%S")
|
||||
screenshots_file = os.path.join(screenshot_path, f'zip_archive_{formatted_timestamp}.zip')
|
||||
|
||||
# Write all of the valid .png and .ppm files to the archive file.
|
||||
with zipfile.ZipFile(screenshots_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive:
|
||||
for file_path in files_to_archive:
|
||||
file_name = os.path.basename(file_path)
|
||||
zip_archive.write(file_path, file_name)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
|
||||
@pytest.mark.parametrize("level", ["Base"])
|
||||
class TestAllComponentsIndepthTests(object):
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
# Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests.
|
||||
global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"]
|
||||
|
||||
enable_prefab_system = False
|
||||
|
||||
@pytest.mark.parametrize("screenshot_name", ["AtomBasicLevelSetup.ppm"])
|
||||
@pytest.mark.test_case_id("C34603773")
|
||||
def test_BasicLevelSetup_SetsUpLevel(
|
||||
self, request, editor, workspace, project, launcher_platform, level, screenshot_name):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests that a basic rendering level setup can be created (lighting, meshes, materials, etc.).
|
||||
"""
|
||||
class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest):
|
||||
use_null_renderer = False # Default is True
|
||||
screenshot_name = "AtomBasicLevelSetup.ppm"
|
||||
test_screenshots = [] # Gets set by setup()
|
||||
screenshot_directory = "" # Gets set by setup()
|
||||
|
||||
# Clear existing test screenshots before starting test.
|
||||
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
|
||||
test_screenshots = [os.path.join(screenshot_directory, screenshot_name)]
|
||||
file_system.delete(test_screenshots, True, True)
|
||||
def setup(self, workspace):
|
||||
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
|
||||
test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)]
|
||||
file_system.delete(test_screenshots, True, True)
|
||||
|
||||
golden_images = [os.path.join(golden_images_directory(), screenshot_name)]
|
||||
|
||||
level_creation_expected_lines = [
|
||||
"Viewport is set to the expected size: True",
|
||||
"Exited game mode"
|
||||
]
|
||||
unexpected_lines = ["Traceback (most recent call last):"]
|
||||
from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
editor,
|
||||
"hydra_GPUTest_BasicLevelSetup.py",
|
||||
timeout=180,
|
||||
expected_lines=level_creation_expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
halt_on_unexpected=True,
|
||||
cfg_args=[level],
|
||||
null_renderer=False,
|
||||
enable_prefab_system=False,
|
||||
)
|
||||
|
||||
similarity_threshold = 0.99
|
||||
for test_screenshot, golden_image in zip(test_screenshots, golden_images):
|
||||
screenshot_comparison_result = compare_screenshot_similarity(
|
||||
test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
|
||||
if screenshot_comparison_result != "Screenshots match":
|
||||
raise Exception(f"Screenshot test failed: {screenshot_comparison_result}")
|
||||
assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True
|
||||
|
||||
@pytest.mark.test_case_id("C34525095")
|
||||
def test_LightComponent_ScreenshotMatchesGoldenImage(
|
||||
self, request, editor, workspace, project, launcher_platform, level):
|
||||
"""
|
||||
Please review the hydra script run by this test for more specific test info.
|
||||
Tests that the Light component screenshots in a rendered level appear the same as the golden images.
|
||||
"""
|
||||
class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages(EditorSharedTest):
|
||||
use_null_renderer = False # Default is True
|
||||
screenshot_names = [
|
||||
"AreaLight_1.ppm",
|
||||
"AreaLight_2.ppm",
|
||||
"AreaLight_3.ppm",
|
||||
"AreaLight_4.ppm",
|
||||
"AreaLight_5.ppm",
|
||||
]
|
||||
test_screenshots = [] # Gets set by setup()
|
||||
screenshot_directory = "" # Gets set by setup()
|
||||
|
||||
# Clear existing test screenshots before starting test.
|
||||
def setup(self, workspace):
|
||||
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
|
||||
for screenshot in self.screenshot_names:
|
||||
screenshot_path = os.path.join(screenshot_directory, screenshot)
|
||||
self.test_screenshots.append(screenshot_path)
|
||||
file_system.delete(self.test_screenshots, True, True)
|
||||
|
||||
golden_images = []
|
||||
for golden_image in screenshot_names:
|
||||
golden_image_path = os.path.join(golden_images_directory(), golden_image)
|
||||
golden_images.append(golden_image_path)
|
||||
|
||||
from Atom.tests import hydra_AtomGPU_AreaLightScreenshotTest as test_module
|
||||
|
||||
assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True
|
||||
|
||||
@pytest.mark.test_case_id("C34525110")
|
||||
class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages(EditorSharedTest):
|
||||
use_null_renderer = False # Default is True
|
||||
screenshot_names = [
|
||||
"SpotLight_1.ppm",
|
||||
"SpotLight_2.ppm",
|
||||
"SpotLight_3.ppm",
|
||||
@@ -134,40 +87,25 @@ class TestAllComponentsIndepthTests(object):
|
||||
"SpotLight_5.ppm",
|
||||
"SpotLight_6.ppm",
|
||||
]
|
||||
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
|
||||
test_screenshots = []
|
||||
for screenshot in screenshot_names:
|
||||
screenshot_path = os.path.join(screenshot_directory, screenshot)
|
||||
test_screenshots.append(screenshot_path)
|
||||
file_system.delete(test_screenshots, True, True)
|
||||
test_screenshots = [] # Gets set by setup()
|
||||
screenshot_directory = "" # Gets set by setup()
|
||||
|
||||
# Clear existing test screenshots before starting test.
|
||||
def setup(self, workspace):
|
||||
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
|
||||
for screenshot in self.screenshot_names:
|
||||
screenshot_path = os.path.join(screenshot_directory, screenshot)
|
||||
self.test_screenshots.append(screenshot_path)
|
||||
file_system.delete(self.test_screenshots, True, True)
|
||||
|
||||
golden_images = []
|
||||
for golden_image in screenshot_names:
|
||||
golden_image_path = os.path.join(golden_images_directory(), golden_image)
|
||||
golden_images.append(golden_image_path)
|
||||
|
||||
expected_lines = ["spot_light Controller|Configuration|Shadows|Shadowmap size: SUCCESS"]
|
||||
unexpected_lines = ["Traceback (most recent call last):"]
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
editor,
|
||||
"hydra_GPUTest_LightComponent.py",
|
||||
timeout=180,
|
||||
expected_lines=expected_lines,
|
||||
unexpected_lines=unexpected_lines,
|
||||
halt_on_unexpected=True,
|
||||
cfg_args=[level],
|
||||
null_renderer=False,
|
||||
enable_prefab_system=False,
|
||||
)
|
||||
from Atom.tests import hydra_AtomGPU_SpotLightScreenshotTest as test_module
|
||||
|
||||
similarity_threshold = 0.99
|
||||
for test_screenshot, golden_image in zip(test_screenshots, golden_images):
|
||||
screenshot_comparison_result = compare_screenshot_similarity(
|
||||
test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
|
||||
if screenshot_comparison_result != "Screenshots match":
|
||||
raise ImageComparisonTestFailure(f"Screenshot test failed: {screenshot_comparison_result}")
|
||||
assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])
|
||||
@@ -198,7 +136,7 @@ class TestPerformanceBenchmarkSuite(object):
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
os.path.join(os.path.dirname(__file__), "tests"),
|
||||
editor,
|
||||
"hydra_GPUTest_AtomFeatureIntegrationBenchmark.py",
|
||||
timeout=600,
|
||||
@@ -235,7 +173,7 @@ class TestMaterialEditor(object):
|
||||
|
||||
hydra.launch_and_validate_results(
|
||||
request,
|
||||
TEST_DIRECTORY,
|
||||
os.path.join(os.path.dirname(__file__), "tests"),
|
||||
generic_launcher,
|
||||
editor_script="",
|
||||
run_python="--runpython",
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
|
||||
from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
|
||||
from .atom_utils.atom_component_helper import create_screenshots_archive, golden_images_directory
|
||||
|
||||
DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
# Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests.
|
||||
global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"]
|
||||
|
||||
enable_prefab_system = False
|
||||
|
||||
@pytest.mark.test_case_id("C34603773")
|
||||
class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest):
|
||||
use_null_renderer = False # Default is True
|
||||
screenshot_name = "AtomBasicLevelSetup.ppm"
|
||||
test_screenshots = [] # Gets set by setup()
|
||||
screenshot_directory = "" # Gets set by setup()
|
||||
|
||||
# Clear existing test screenshots before starting test.
|
||||
def setup(self, workspace):
|
||||
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
|
||||
test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)]
|
||||
file_system.delete(test_screenshots, True, True)
|
||||
|
||||
from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module
|
||||
|
||||
golden_images = [os.path.join(golden_images_directory(), screenshot_name)]
|
||||
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
|
||||
compare_screenshots(test_screenshot, golden_screenshot)
|
||||
create_screenshots_archive(screenshot_directory)
|
||||
@@ -12,6 +12,7 @@ import pytest
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
|
||||
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
|
||||
from Atom.atom_utils.atom_constants import LIGHT_TYPES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -159,3 +160,17 @@ class TestMaterialEditorBasicTests(object):
|
||||
enable_prefab_system=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
|
||||
enable_prefab_system = False
|
||||
|
||||
@pytest.mark.test_case_id("C36529666")
|
||||
class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module
|
||||
|
||||
@pytest.mark.test_case_id("C36525660")
|
||||
class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest):
|
||||
from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module
|
||||
|
||||
@@ -93,161 +93,165 @@ def compare_screenshot_similarity(
|
||||
return result
|
||||
|
||||
|
||||
def create_basic_atom_level(level_name):
|
||||
def compare_screenshot_to_golden_image(
|
||||
screenshot_directory, test_screenshots, golden_images, similarity_threshold=0.99):
|
||||
"""
|
||||
Creates a new level inside the Editor matching level_name & adds the following:
|
||||
1. "default_level" entity to hold all other entities.
|
||||
2. Adds Grid, Global Skylight (IBL), ground Mesh, Directional Light, Sphere w/ material+mesh, & Camera components.
|
||||
3. Each of these components has its settings tweaked slightly to match the ideal scene to test Atom rendering.
|
||||
:param level_name: name of the level to create and apply this basic setup to.
|
||||
Compares a list of test_screenshots to a list of golden_images and return True if they match within the
|
||||
similarity threshold set. Otherwise, it will raise ImageComparisonTestFailure with a failure message.
|
||||
:param screenshot_directory: path to the directory containing screenshots for creating .zip archives.
|
||||
:param test_screenshots: list of test screenshot path strings.
|
||||
:param golden_images: list of golden image path strings.
|
||||
:param similarity_threshold: float threshold tolerance to set when comparing screenshots to golden images.
|
||||
"""
|
||||
for test_screenshot, golden_image in zip(test_screenshots, golden_images):
|
||||
screenshot_comparison_result = compare_screenshot_similarity(
|
||||
test_screenshot, golden_image, similarity_threshold, True, screenshot_directory)
|
||||
if screenshot_comparison_result != "Screenshots match":
|
||||
raise ImageComparisonTestFailure(f"Screenshot test failed: {screenshot_comparison_result}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def initial_viewport_setup(screen_width=1280, screen_height=720):
|
||||
"""
|
||||
For setting up the initial viewport resolution to expected default values before running a screenshot test.
|
||||
Defaults to 1280 x 720 resolution (in pixels).
|
||||
:param screen_width: Width in pixels to set the viewport width size to.
|
||||
:param screen_height: Height in pixels to set the viewport height size to.
|
||||
:return: None
|
||||
"""
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.camera as camera
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as math
|
||||
import azlmbr.object
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
||||
|
||||
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
|
||||
|
||||
# Wait for Editor idle loop before executing Python hydra scripts.
|
||||
general.idle_enable(True)
|
||||
|
||||
# Basic setup for opened level.
|
||||
helper.open_level(level_name="Base")
|
||||
general.idle_wait(1.0)
|
||||
general.update_viewport()
|
||||
general.idle_wait(0.5) # half a second is more than enough for updating the viewport.
|
||||
|
||||
# Close out problematic windows, FPS meters, and anti-aliasing.
|
||||
if general.is_helpers_shown(): # Turn off the helper gizmos if visible
|
||||
general.toggle_helpers()
|
||||
general.idle_wait(1.0)
|
||||
if general.is_pane_visible("Error Report"): # Close Error Report windows that block focus.
|
||||
general.close_pane("Error Report")
|
||||
if general.is_pane_visible("Error Log"): # Close Error Log windows that block focus.
|
||||
general.close_pane("Error Log")
|
||||
general.idle_wait(1.0)
|
||||
general.run_console("r_displayInfo=0")
|
||||
general.idle_wait(1.0)
|
||||
|
||||
# Delete all existing entities & create default_level entity
|
||||
search_filter = azlmbr.entity.SearchFilter()
|
||||
all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
|
||||
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities)
|
||||
default_level = hydra.Entity("default_level")
|
||||
default_position = math.Vector3(0.0, 0.0, 0.0)
|
||||
default_level.create_entity(default_position, ["Grid"])
|
||||
default_level.get_set_test(0, "Controller|Configuration|Secondary Grid Spacing", 1.0)
|
||||
|
||||
# Set the viewport up correctly after adding the parent default_level entity.
|
||||
screen_width = 1280
|
||||
screen_height = 720
|
||||
degree_radian_factor = 0.0174533 # Used by "Rotation" property for the Transform component.
|
||||
general.set_viewport_size(screen_width, screen_height)
|
||||
general.update_viewport()
|
||||
helper.wait_for_condition(
|
||||
function=lambda: helper.isclose(a=general.get_viewport_size().x, b=screen_width, rel_tol=0.1)
|
||||
and helper.isclose(a=general.get_viewport_size().y, b=screen_height, rel_tol=0.1),
|
||||
timeout_in_seconds=4.0
|
||||
)
|
||||
result = helper.isclose(a=general.get_viewport_size().x, b=screen_width, rel_tol=0.1) and helper.isclose(
|
||||
a=general.get_viewport_size().y, b=screen_height, rel_tol=0.1)
|
||||
general.log(general.get_viewport_size().x)
|
||||
general.log(general.get_viewport_size().y)
|
||||
general.log(general.get_viewport_size().z)
|
||||
general.log(f"Viewport is set to the expected size: {result}")
|
||||
general.log("Basic level created")
|
||||
general.run_console("r_DisplayInfo = 0")
|
||||
|
||||
# Create global_skylight entity and set the properties
|
||||
global_skylight = hydra.Entity("global_skylight")
|
||||
global_skylight.create_entity(
|
||||
entity_position=default_position,
|
||||
components=["HDRi Skybox", "Global Skylight (IBL)"],
|
||||
parent_id=default_level.id)
|
||||
global_skylight_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
|
||||
global_skylight_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", global_skylight_asset_path, math.Uuid(), False)
|
||||
global_skylight.get_set_test(0, "Controller|Configuration|Cubemap Texture", global_skylight_asset_value)
|
||||
global_skylight.get_set_test(1, "Controller|Configuration|Diffuse Image", global_skylight_asset_value)
|
||||
global_skylight.get_set_test(1, "Controller|Configuration|Specular Image", global_skylight_asset_value)
|
||||
|
||||
# Create ground_plane entity and set the properties
|
||||
ground_plane = hydra.Entity("ground_plane")
|
||||
ground_plane.create_entity(
|
||||
entity_position=default_position,
|
||||
components=["Material"],
|
||||
parent_id=default_level.id)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
|
||||
def enter_exit_game_mode_take_screenshot(screenshot_name, enter_game_tuple, exit_game_tuple, timeout_in_seconds=4):
|
||||
"""
|
||||
Enters game mode, takes a screenshot named screenshot_name (must include file extension), and exits game mode.
|
||||
:param screenshot_name: string representing the name of the screenshot file, including file extension.
|
||||
:param enter_game_tuple: tuple where the 1st string is success & 2nd string is failure for entering the game.
|
||||
:param exit_game_tuple: tuple where the 1st string is success & 2nd string is failure for exiting the game.
|
||||
:param timeout_in_seconds: int or float seconds to wait for entering/exiting game mode.
|
||||
:return: None
|
||||
"""
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
|
||||
ground_plane.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
|
||||
).GetValue()[0]
|
||||
)
|
||||
from editor_python_test_tools.utils import TestHelper
|
||||
|
||||
from Atom.atom_utils.screenshot_utils import ScreenshotHelper
|
||||
|
||||
TestHelper.enter_game_mode(enter_game_tuple)
|
||||
TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds)
|
||||
ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(screenshot_name)
|
||||
TestHelper.exit_game_mode(exit_game_tuple)
|
||||
TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds)
|
||||
|
||||
|
||||
def create_basic_atom_rendering_scene():
|
||||
"""
|
||||
Sets up a new scene inside the Editor for testing Atom rendering GPU output.
|
||||
Setup: Deletes all existing entities before creating the scene.
|
||||
The created scene includes:
|
||||
1. "Default Level" entity that holds all of the other entities.
|
||||
2. "Grid" entity: Contains a Grid component.
|
||||
3. "Global Skylight (IBL)" entity: Contains HDRI Skybox & Global Skylight (IBL) components.
|
||||
4. "Ground Plane" entity: Contains Material & Mesh components.
|
||||
5. "Directional Light" entity: Contains Directional Light component.
|
||||
6. "Sphere" entity: Contains Material & Mesh components.
|
||||
7. "Camera" entity: Contains Camera component.
|
||||
:return: None
|
||||
"""
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
|
||||
from editor_python_test_tools.asset_utils import Asset
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
# Setup: Deletes all existing entities before creating the scene.
|
||||
search_filter = azlmbr.entity.SearchFilter()
|
||||
all_entities = azlmbr.entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
|
||||
azlmbr.editor.ToolsApplicationRequestBus(azlmbr.bus.Broadcast, "DeleteEntities", all_entities)
|
||||
|
||||
# 1. "Default Level" entity that holds all of the other entities.
|
||||
default_level_entity_name = "Default Level"
|
||||
default_level_entity = EditorEntity.create_editor_entity_at(math.Vector3(0.0, 0.0, 0.0), default_level_entity_name)
|
||||
|
||||
# 2. "Grid" entity: Contains a Grid component.
|
||||
grid_entity = EditorEntity.create_editor_entity(AtomComponentProperties.grid(), default_level_entity.id)
|
||||
grid_component = grid_entity.add_component(AtomComponentProperties.grid())
|
||||
secondary_grid_spacing_value = 1.0
|
||||
grid_component.set_component_property_value(
|
||||
AtomComponentProperties.grid('Secondary Grid Spacing'), secondary_grid_spacing_value)
|
||||
|
||||
# 3. "Global Skylight (IBL)" entity: Contains HDRI Skybox & Global Skylight (IBL) components.
|
||||
global_skylight_entity = EditorEntity.create_editor_entity(
|
||||
AtomComponentProperties.global_skylight(), default_level_entity.id)
|
||||
hdri_skybox_component = global_skylight_entity.add_component(AtomComponentProperties.hdri_skybox())
|
||||
global_skylight_component = global_skylight_entity.add_component(AtomComponentProperties.global_skylight())
|
||||
global_skylight_image_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
|
||||
global_skylight_image_asset = Asset.find_asset_by_path(global_skylight_image_asset_path, False)
|
||||
hdri_skybox_component.set_component_property_value(
|
||||
AtomComponentProperties.hdri_skybox('Cubemap Texture'), global_skylight_image_asset.id)
|
||||
global_skylight_diffuse_image_asset_path = os.path.join(
|
||||
"LightingPresets", "default_iblskyboxcm_ibldiffuse.exr.streamingimage")
|
||||
global_skylight_diffuse_image_asset = Asset.find_asset_by_path(global_skylight_diffuse_image_asset_path, False)
|
||||
global_skylight_component.set_component_property_value(
|
||||
AtomComponentProperties.global_skylight('Diffuse Image'), global_skylight_diffuse_image_asset.id)
|
||||
global_skylight_specular_image_asset_path = os.path.join(
|
||||
"LightingPresets", "default_iblskyboxcm_iblspecular.exr.streamingimage")
|
||||
global_skylight_specular_image_asset = Asset.find_asset_by_path(
|
||||
global_skylight_specular_image_asset_path, False)
|
||||
global_skylight_component.set_component_property_value(
|
||||
AtomComponentProperties.global_skylight('Specular Image'), global_skylight_specular_image_asset.id)
|
||||
|
||||
# 4. "Ground Plane" entity: Contains Material & Mesh components.
|
||||
ground_plane_name = "Ground Plane"
|
||||
ground_plane_entity = EditorEntity.create_editor_entity(ground_plane_name, default_level_entity.id)
|
||||
ground_plane_material_component = ground_plane_entity.add_component(AtomComponentProperties.material())
|
||||
ground_plane_entity.set_local_uniform_scale(32.0)
|
||||
ground_plane_mesh_component = ground_plane_entity.add_component(AtomComponentProperties.mesh())
|
||||
ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
|
||||
ground_plane_mesh_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset_value)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
ground_plane_mesh_asset = Asset.find_asset_by_path(ground_plane_mesh_asset_path, False)
|
||||
ground_plane_mesh_component.set_component_property_value(
|
||||
AtomComponentProperties.mesh('Mesh Asset'), ground_plane_mesh_asset.id)
|
||||
ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
|
||||
ground_plane_material_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset_value)
|
||||
ground_plane_material_asset = Asset.find_asset_by_path(ground_plane_material_asset_path, False)
|
||||
ground_plane_material_component.set_component_property_value(
|
||||
AtomComponentProperties.material('Material Asset'), ground_plane_material_asset.id)
|
||||
|
||||
# Create directional_light entity and set the properties
|
||||
directional_light = hydra.Entity("directional_light")
|
||||
directional_light.create_entity(
|
||||
entity_position=math.Vector3(0.0, 0.0, 10.0),
|
||||
components=["Directional Light"],
|
||||
parent_id=default_level.id)
|
||||
directional_light_rotation = math.Vector3(degree_radian_factor * -90.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "SetLocalRotation", directional_light.id, directional_light_rotation)
|
||||
# 5. "Directional Light" entity: Contains Directional Light component.
|
||||
directional_light_entity = EditorEntity.create_editor_entity_at(
|
||||
math.Vector3(0.0, 0.0, 10.0), AtomComponentProperties.directional_light(), default_level_entity.id)
|
||||
directional_light_entity.add_component(AtomComponentProperties.directional_light())
|
||||
directional_light_entity_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0)
|
||||
directional_light_entity.set_local_rotation(directional_light_entity_rotation)
|
||||
|
||||
# Create sphere entity and set the properties
|
||||
sphere_entity = hydra.Entity("sphere")
|
||||
sphere_entity.create_entity(
|
||||
entity_position=math.Vector3(0.0, 0.0, 1.0),
|
||||
components=["Material"],
|
||||
parent_id=default_level.id)
|
||||
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
sphere_entity.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", sphere_entity.id, [mesh_type_id]
|
||||
).GetValue()[0]
|
||||
)
|
||||
# 6. "Sphere" entity: Contains Material & Mesh components.
|
||||
sphere_entity = EditorEntity.create_editor_entity_at(
|
||||
math.Vector3(0.0, 0.0, 1.0), "Sphere", default_level_entity.id)
|
||||
sphere_mesh_component = sphere_entity.add_component(AtomComponentProperties.mesh())
|
||||
sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel")
|
||||
sphere_mesh_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
|
||||
sphere_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset_value)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
sphere_mesh_asset = Asset.find_asset_by_path(sphere_mesh_asset_path, False)
|
||||
sphere_mesh_component.set_component_property_value(
|
||||
AtomComponentProperties.mesh('Mesh Asset'), sphere_mesh_asset.id)
|
||||
sphere_material_component = sphere_entity.add_component(AtomComponentProperties.material())
|
||||
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
|
||||
sphere_material_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
|
||||
sphere_entity.get_set_test(0, "Default Material|Material Asset", sphere_material_asset_value)
|
||||
sphere_material_asset = Asset.find_asset_by_path(sphere_material_asset_path, False)
|
||||
sphere_material_component.set_component_property_value(
|
||||
AtomComponentProperties.material('Material Asset'), sphere_material_asset.id)
|
||||
|
||||
# Create camera component and set the properties
|
||||
camera_entity = hydra.Entity("camera")
|
||||
camera_entity.create_entity(
|
||||
entity_position=math.Vector3(5.5, -12.0, 9.0),
|
||||
components=["Camera"],
|
||||
parent_id=default_level.id)
|
||||
rotation = math.Vector3(
|
||||
degree_radian_factor * -27.0, degree_radian_factor * -12.0, degree_radian_factor * 25.0
|
||||
)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", camera_entity.id, rotation)
|
||||
camera_entity.get_set_test(0, "Controller|Configuration|Field of view", 60.0)
|
||||
camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
|
||||
# 7. "Camera" entity: Contains Camera component.
|
||||
camera_entity = EditorEntity.create_editor_entity_at(
|
||||
math.Vector3(5.5, -12.0, 9.0), AtomComponentProperties.camera(), default_level_entity.id)
|
||||
camera_component = camera_entity.add_component(AtomComponentProperties.camera())
|
||||
camera_entity_rotation = math.Vector3(
|
||||
DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0)
|
||||
camera_entity.set_local_rotation(camera_entity_rotation)
|
||||
camera_fov_value = 60.0
|
||||
camera_component.set_component_property_value(AtomComponentProperties.camera('Field of view'), camera_fov_value)
|
||||
azlmbr.camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
|
||||
|
||||
@@ -19,6 +19,20 @@ LIGHT_TYPES = {
|
||||
}
|
||||
|
||||
|
||||
# Attenuation Radius Mode options for the Light component.
|
||||
ATTENUATION_RADIUS_MODE = {
|
||||
'automatic': 1,
|
||||
'explicit': 0,
|
||||
}
|
||||
|
||||
# Qualiity Level settings for Diffuse Global Illumination level component
|
||||
GLOBAL_ILLUMINATION_QUALITY = {
|
||||
'Low': 0,
|
||||
'Medium': 1,
|
||||
'High': 2,
|
||||
}
|
||||
|
||||
|
||||
class AtomComponentProperties:
|
||||
"""
|
||||
Holds Atom component related constants
|
||||
@@ -116,6 +130,21 @@ class AtomComponentProperties:
|
||||
}
|
||||
return properties[property]
|
||||
|
||||
@staticmethod
|
||||
def diffuse_global_illumination(property: str = 'name') -> str:
|
||||
"""
|
||||
Diffuse Global Illumination level component properties.
|
||||
Controls global settings for Diffuse Probe Grid components.
|
||||
- 'Quality Level' from atom_constants.py GLOBAL_ILLUMINATION_QUALITY
|
||||
:param property: From the last element of the property tree path. Default 'name' for component name string.
|
||||
:return: Full property path OR component name if no property specified.
|
||||
"""
|
||||
properties = {
|
||||
'name': 'Diffuse Global Illumination',
|
||||
'Quality Level': 'Controller|Configuration|Quality Level'
|
||||
}
|
||||
return properties[property]
|
||||
|
||||
@staticmethod
|
||||
def diffuse_probe_grid(property: str = 'name') -> str:
|
||||
"""
|
||||
@@ -148,7 +177,8 @@ class AtomComponentProperties:
|
||||
@staticmethod
|
||||
def display_mapper(property: str = 'name') -> str:
|
||||
"""
|
||||
Display Mapper component properties.
|
||||
Display Mapper level component properties.
|
||||
- 'Enable LDR color grading LUT' toggles the use of LDR color grading LUT
|
||||
- 'LDR color Grading LUT' is the Low Definition Range (LDR) color grading for Look-up Textures (LUT) which is
|
||||
an Asset.id value corresponding to a lighting asset file.
|
||||
:param property: From the last element of the property tree path. Default 'name' for component name string.
|
||||
@@ -156,6 +186,7 @@ class AtomComponentProperties:
|
||||
"""
|
||||
properties = {
|
||||
'name': 'Display Mapper',
|
||||
'Enable LDR color grading LUT': 'Controller|Configuration|Enable LDR color grading LUT',
|
||||
'LDR color Grading LUT': 'Controller|Configuration|LDR color Grading LUT',
|
||||
}
|
||||
return properties[property]
|
||||
@@ -252,13 +283,27 @@ class AtomComponentProperties:
|
||||
def light(property: str = 'name') -> str:
|
||||
"""
|
||||
Light component properties.
|
||||
- 'Attenuation Radius Mode' controls whether the attenuation radius is calculated automatically or explicitly.
|
||||
- 'Color' the RGB value to set for the color of the light.
|
||||
- 'Enable shadow' toggle for enabling shadows for the light.
|
||||
- 'Enable shutters' toggle for enabling shutters for the light.
|
||||
- 'Inner angle' inner angle value for the shutters (in degrees)
|
||||
- 'Intensity' the intensity of the light in the set photometric unit (float with no ceiling).
|
||||
- 'Light type' from atom_constants.py LIGHT_TYPES
|
||||
- 'Outer angle' outer angle value for the shutters (in degrees)
|
||||
:param property: From the last element of the property tree path. Default 'name' for component name string.
|
||||
:return: Full property path OR component name if no property specified.
|
||||
"""
|
||||
properties = {
|
||||
'name': 'Light',
|
||||
'Attenuation Radius Mode': 'Controller|Configuration|Attenuation radius|Mode',
|
||||
'Color': 'Controller|Configuration|Color',
|
||||
'Enable shadow': 'Controller|Configuration|Shadows|Enable shadow',
|
||||
'Enable shutters': 'Controller|Configuration|Shutters|Enable shutters',
|
||||
'Inner angle': 'Controller|Configuration|Shutters|Inner angle',
|
||||
'Intensity': 'Controller|Configuration|Intensity',
|
||||
'Light type': 'Controller|Configuration|Light type',
|
||||
'Outer angle': 'Controller|Configuration|Shutters|Outer angle',
|
||||
}
|
||||
return properties[property]
|
||||
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO Level component addition success",
|
||||
"UNDO Level component addition failed")
|
||||
creation_redo = (
|
||||
"REDO Level component addition success",
|
||||
"REDO Level component addition failed")
|
||||
diffuse_global_illumination_component = (
|
||||
"Level has a Diffuse Global Illumination component",
|
||||
"Level failed to find Diffuse Global Illumination component")
|
||||
diffuse_global_illumination_quality = (
|
||||
"Quality Level set",
|
||||
"Quality Level could not be set")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
|
||||
|
||||
def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the Diffuse Global Illumination level component can be added to the level entity and is stable.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Add Diffuse Global Illumination level component to the level entity.
|
||||
2) UNDO the level component addition.
|
||||
3) REDO the level component addition.
|
||||
4) Set Quality Level property to Low
|
||||
5) Enter/Exit game mode.
|
||||
6) Look for errors and asserts.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorLevelEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties, GLOBAL_ILLUMINATION_QUALITY
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Add Diffuse Global Illumination level component to the level entity.
|
||||
diffuse_global_illumination_component = EditorLevelEntity.add_component(
|
||||
AtomComponentProperties.diffuse_global_illumination())
|
||||
Report.critical_result(
|
||||
Tests.diffuse_global_illumination_component,
|
||||
EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
|
||||
|
||||
# 2. UNDO the level component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo,
|
||||
not EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
|
||||
|
||||
# 3. REDO the level component addition.
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo,
|
||||
EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
|
||||
|
||||
# 4. Set Quality Level property to Low
|
||||
diffuse_global_illumination_component.set_component_property_value(
|
||||
AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low']))
|
||||
quality = diffuse_global_illumination_component.get_component_property_value(
|
||||
AtomComponentProperties.diffuse_global_illumination('Quality Level'))
|
||||
Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low'])
|
||||
|
||||
# 5. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 6. Look for errors and asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity)
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
class Tests:
|
||||
creation_undo = (
|
||||
"UNDO level component addition success",
|
||||
"UNDO level component addition failed")
|
||||
creation_redo = (
|
||||
"REDO Level component addition success",
|
||||
"REDO Level component addition failed")
|
||||
display_mapper_component = (
|
||||
"Level has a Display Mapper component",
|
||||
"Level failed to find Display Mapper component")
|
||||
ldr_color_grading_lut = (
|
||||
"LDR color Grading LUT asset set",
|
||||
"LDR color Grading LUT asset could not be set")
|
||||
enable_ldr_color_grading_lut = (
|
||||
"Enable LDR color grading LUT set",
|
||||
"Enable LDR color grading LUT could not be set")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
|
||||
|
||||
def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
|
||||
"""
|
||||
Summary:
|
||||
Tests the Display Mapper level component can be added to the level entity and has the expected functionality.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
|
||||
Expected Behavior:
|
||||
The component can be added, used in game mode, and has accurate required components.
|
||||
Creation and deletion undo/redo should also work.
|
||||
|
||||
Test Steps:
|
||||
1) Add Display Mapper level component to the level entity.
|
||||
2) UNDO the level component addition.
|
||||
3) REDO the level component addition.
|
||||
4) Set LDR color Grading LUT asset.
|
||||
5) Set Enable LDR color grading LUT property True
|
||||
6) Enter/Exit game mode.
|
||||
7) Look for errors and asserts.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.asset_utils import Asset
|
||||
from editor_python_test_tools.editor_entity_utils import EditorLevelEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Add Display Mapper level component to the level entity.
|
||||
display_mapper_component = EditorLevelEntity.add_component(AtomComponentProperties.display_mapper())
|
||||
Report.critical_result(
|
||||
Tests.display_mapper_component,
|
||||
EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
|
||||
|
||||
# 2. UNDO the level component addition.
|
||||
# -> UNDO component addition.
|
||||
general.undo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_undo, not EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
|
||||
|
||||
# 3. REDO the level component addition.
|
||||
# -> REDO component addition.
|
||||
general.redo()
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
|
||||
|
||||
# 4. Set LDR color Grading LUT asset.
|
||||
display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset")
|
||||
display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False)
|
||||
display_mapper_component.set_component_property_value(
|
||||
AtomComponentProperties.display_mapper('LDR color Grading LUT'), display_mapper_asset.id)
|
||||
Report.result(
|
||||
Tests.ldr_color_grading_lut,
|
||||
display_mapper_component.get_component_property_value(
|
||||
AtomComponentProperties.display_mapper('LDR color Grading LUT')) == display_mapper_asset.id)
|
||||
|
||||
# 5. Set Enable LDR color grading LUT property True
|
||||
display_mapper_component.set_component_property_value(
|
||||
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
|
||||
Report.result(
|
||||
Test.enable_ldr_color_grading_lut,
|
||||
display_mapper_component.get_component_property_value(
|
||||
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
|
||||
|
||||
# 6. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 7. Look for errors and asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomEditorComponentsLevel_DisplayMapper_AddedToEntity)
|
||||
+39
-36
@@ -7,15 +7,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
|
||||
class Tests:
|
||||
camera_creation = (
|
||||
"Camera Entity successfully created",
|
||||
"Camera Entity failed to be created")
|
||||
camera_component_added = (
|
||||
"Camera component was added to entity",
|
||||
"Camera component failed to be added to entity")
|
||||
camera_component_check = (
|
||||
"Entity has a Camera component",
|
||||
"Entity failed to find Camera component")
|
||||
creation_undo = (
|
||||
"UNDO Entity creation success",
|
||||
"UNDO Entity creation failed")
|
||||
@@ -43,6 +34,9 @@ class Tests:
|
||||
ldr_color_grading_lut = (
|
||||
"LDR color Grading LUT asset set",
|
||||
"LDR color Grading LUT asset could not be set")
|
||||
enable_ldr_color_grading_lut = (
|
||||
"Enable LDR color grading LUT set",
|
||||
"Enable LDR color grading LUT could not be set")
|
||||
entity_deleted = (
|
||||
"Entity deleted",
|
||||
"Entity was not deleted")
|
||||
@@ -72,14 +66,15 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
|
||||
2) Add Display Mapper component to Display Mapper entity.
|
||||
3) UNDO the entity creation and component addition.
|
||||
4) REDO the entity creation and component addition.
|
||||
5) Enter/Exit game mode.
|
||||
6) Test IsHidden.
|
||||
7) Test IsVisible.
|
||||
8) Set LDR color Grading LUT asset.
|
||||
9) Delete Display Mapper entity.
|
||||
10) UNDO deletion.
|
||||
11) REDO deletion.
|
||||
12) Look for errors and asserts.
|
||||
5) Set LDR color Grading LUT asset.
|
||||
6) Set Enable LDR color grading LUT property True
|
||||
7) Enter/Exit game mode.
|
||||
8) Test IsHidden.
|
||||
9) Test IsVisible.
|
||||
10) Delete Display Mapper entity.
|
||||
11) UNDO deletion.
|
||||
12) REDO deletion.
|
||||
13) Look for errors and asserts.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
@@ -133,21 +128,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.creation_redo, display_mapper_entity.exists())
|
||||
|
||||
# 5. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 6. Test IsHidden.
|
||||
display_mapper_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True)
|
||||
|
||||
# 7. Test IsVisible.
|
||||
display_mapper_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True)
|
||||
|
||||
# 8. Set LDR color Grading LUT asset.
|
||||
# 5. Set LDR color Grading LUT asset.
|
||||
display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset")
|
||||
display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False)
|
||||
display_mapper_component.set_component_property_value(
|
||||
@@ -157,19 +138,41 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
|
||||
display_mapper_component.get_component_property_value(
|
||||
AtomComponentProperties.display_mapper("LDR color Grading LUT")) == display_mapper_asset.id)
|
||||
|
||||
# 9. Delete Display Mapper entity.
|
||||
# 6. Set Enable LDR color grading LUT property True
|
||||
display_mapper_component.set_component_property_value(
|
||||
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
|
||||
Report.result(
|
||||
Tests.enable_ldr_color_grading_lut,
|
||||
display_mapper_component.get_component_property_value(
|
||||
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
|
||||
|
||||
# 7. Enter/Exit game mode.
|
||||
TestHelper.enter_game_mode(Tests.enter_game_mode)
|
||||
general.idle_wait_frames(1)
|
||||
TestHelper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 8. Test IsHidden.
|
||||
display_mapper_entity.set_visibility_state(False)
|
||||
Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True)
|
||||
|
||||
# 9. Test IsVisible.
|
||||
display_mapper_entity.set_visibility_state(True)
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True)
|
||||
|
||||
# 10. Delete Display Mapper entity.
|
||||
display_mapper_entity.delete()
|
||||
Report.result(Tests.entity_deleted, not display_mapper_entity.exists())
|
||||
|
||||
# 10. UNDO deletion.
|
||||
# 11. UNDO deletion.
|
||||
general.undo()
|
||||
Report.result(Tests.deletion_undo, display_mapper_entity.exists())
|
||||
|
||||
# 11. REDO deletion.
|
||||
# 12. REDO deletion.
|
||||
general.redo()
|
||||
Report.result(Tests.deletion_redo, not display_mapper_entity.exists())
|
||||
|
||||
# 12. Look for errors and asserts.
|
||||
# 13. Look for errors and asserts.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
class Tests:
|
||||
area_light_entity_created = (
|
||||
"Area Light entity successfully created",
|
||||
"Area Light entity failed to be created")
|
||||
area_light_entity_deleted = (
|
||||
"Area Light entity was deleted",
|
||||
"Area Light entity was not deleted")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
light_component_added = (
|
||||
"Light component was added",
|
||||
"Light component wasn't added")
|
||||
light_component_attenuation_radius_property_set = (
|
||||
"Light component Attenuation Radius Property was set",
|
||||
"Light component Attenuation Radius Property was not set")
|
||||
light_component_color_property_set = (
|
||||
"Light component Color property was set",
|
||||
"Light component Color property was not set")
|
||||
light_component_intensity_property_set = (
|
||||
"Light component Intensity property was set",
|
||||
"Light component Intensity property was not set")
|
||||
light_component_light_type_property_set = (
|
||||
"Light type property was set",
|
||||
"Light type property was not set")
|
||||
|
||||
|
||||
def AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages():
|
||||
"""
|
||||
Summary:
|
||||
Light component test using the Capsule, Spot (disk), and Point (sphere) Light type property options.
|
||||
Sets each scene up and then takes a screenshot of each scene for test comparison.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
- Close error windows and display helpers then update the viewport size.
|
||||
- Runs the create_basic_atom_rendering_scene() function to setup the test scene.
|
||||
|
||||
Expected Behavior:
|
||||
The test scripts sets up the scenes correctly and takes accurate screenshots.
|
||||
|
||||
Test Steps:
|
||||
1. Create Area Light entity with no components.
|
||||
2. Add a Light component to the Area Light entity.
|
||||
3. Set the Light type property to Capsule for the Light component.
|
||||
4. Set the Light component's Color property to 255, 0, 0.
|
||||
5. Enter game mode and take a screenshot then exit game mode.
|
||||
6. Set the Intensity property of the Light component to 0.0.
|
||||
7. Set the Attenuation Radius Mode property of the Light component to 1 (automatic).
|
||||
8. Enter game mode and take a screenshot then exit game mode.
|
||||
9. Set the Intensity property of the Light component to 1000.0
|
||||
10. Enter game mode and take a screenshot then exit game mode.
|
||||
11. Set the Light type property to Spot (disk) for the Light component & rotate DEGREE_RADIAN_FACTOR * 90 degrees.
|
||||
12. Enter game mode and take a screenshot then exit game mode.
|
||||
13. Set the Light type property to Point (sphere) instead of Spot (disk) for the Light component.
|
||||
14. Enter game mode and take a screenshot then exit game mode.
|
||||
15. Delete the Area Light entity.
|
||||
16. Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.paths
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties, ATTENUATION_RADIUS_MODE, LIGHT_TYPES
|
||||
from Atom.atom_utils.atom_component_helper import (
|
||||
initial_viewport_setup, create_basic_atom_rendering_scene, enter_exit_game_mode_take_screenshot)
|
||||
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Setup: Close error windows and display helpers then update the viewport size.
|
||||
TestHelper.close_error_windows()
|
||||
TestHelper.close_display_helpers()
|
||||
initial_viewport_setup()
|
||||
general.update_viewport()
|
||||
|
||||
# Setup: Runs the create_basic_atom_rendering_scene() function to setup the test scene.
|
||||
create_basic_atom_rendering_scene()
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Create Area Light entity with no components.
|
||||
area_light_entity_name = "Area Light"
|
||||
area_light_entity = EditorEntity.create_editor_entity_at(
|
||||
azlmbr.math.Vector3(0.0, 0.0, 0.0), area_light_entity_name)
|
||||
Report.critical_result(Tests.area_light_entity_created, area_light_entity.exists())
|
||||
|
||||
# 2. Add a Light component to the Area Light entity.
|
||||
light_component = area_light_entity.add_component(AtomComponentProperties.light())
|
||||
Report.critical_result(
|
||||
Tests.light_component_added, area_light_entity.has_component(AtomComponentProperties.light()))
|
||||
|
||||
# 3. Set the Light type property to Capsule for the Light component.
|
||||
light_component.set_component_property_value(
|
||||
AtomComponentProperties.light('Light type'), LIGHT_TYPES['capsule'])
|
||||
Report.result(
|
||||
Tests.light_component_light_type_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Light type')) == LIGHT_TYPES['capsule'])
|
||||
|
||||
# 4. Set the Light component's Color property to 255, 0, 0.
|
||||
light_component_color_value = azlmbr.math.Color(255.0, 0.0, 0.0, 0.0)
|
||||
light_component.set_component_property_value(
|
||||
AtomComponentProperties.light('Color'), light_component_color_value)
|
||||
Report.result(
|
||||
Tests.light_component_color_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Color')) == light_component_color_value)
|
||||
|
||||
# 5. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("AreaLight_1.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 6. Set the Intensity property of the Light component to 0.0.
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Intensity'), 0.0)
|
||||
Report.result(
|
||||
Tests.light_component_intensity_property_set,
|
||||
light_component.get_component_property_value(AtomComponentProperties.light('Intensity')) == 0.0)
|
||||
|
||||
# 7. Set the Attenuation Radius Mode property of the Light component to 1 (automatic).
|
||||
light_component.set_component_property_value(
|
||||
AtomComponentProperties.light('Attenuation Radius Mode'), ATTENUATION_RADIUS_MODE['automatic'])
|
||||
Report.result(
|
||||
Tests.light_component_attenuation_radius_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Attenuation Radius Mode')) == ATTENUATION_RADIUS_MODE['automatic'])
|
||||
|
||||
# 8. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("AreaLight_2.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 9. Set the Intensity property of the Light component to 1000.0
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Intensity'), 1000.0)
|
||||
Report.result(
|
||||
Tests.light_component_intensity_property_set,
|
||||
light_component.get_component_property_value(AtomComponentProperties.light('Intensity')) == 1000.0)
|
||||
|
||||
# 10. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("AreaLight_3.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 11. Set the Light type property to Spot (disk) for the Light component &
|
||||
# rotate DEGREE_RADIAN_FACTOR * 90 degrees.
|
||||
light_component.set_component_property_value(
|
||||
AtomComponentProperties.light('Light type'), LIGHT_TYPES['spot_disk'])
|
||||
area_light_rotation = azlmbr.math.Vector3(DEGREE_RADIAN_FACTOR * 90.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", area_light_entity.id, area_light_rotation)
|
||||
Report.result(
|
||||
Tests.light_component_light_type_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Light type')) == LIGHT_TYPES['spot_disk'])
|
||||
|
||||
# 12. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("AreaLight_4.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 13. Set the Light type property to Point (sphere) instead of Spot (disk) for the Light component.
|
||||
light_component.set_component_property_value(
|
||||
AtomComponentProperties.light('Light type'), LIGHT_TYPES['sphere'])
|
||||
Report.result(
|
||||
Tests.light_component_light_type_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Light type')) == LIGHT_TYPES['sphere'])
|
||||
|
||||
# 14. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("AreaLight_5.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 15. Delete the Area Light entity.
|
||||
area_light_entity.delete()
|
||||
Report.result(Tests.area_light_entity_deleted, not area_light_entity.exists())
|
||||
|
||||
# 16. Look for errors.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages)
|
||||
@@ -80,6 +80,7 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
- Deletes all existing entities before creating the scene.
|
||||
|
||||
Expected Behavior:
|
||||
The scene can be setup for a basic level.
|
||||
@@ -115,7 +116,6 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
|
||||
"""
|
||||
|
||||
import os
|
||||
from math import isclose
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as math
|
||||
@@ -126,21 +126,11 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties
|
||||
from Atom.atom_utils.atom_component_helper import initial_viewport_setup
|
||||
from Atom.atom_utils.screenshot_utils import ScreenshotHelper
|
||||
|
||||
SCREENSHOT_NAME = "AtomBasicLevelSetup"
|
||||
SCREEN_WIDTH = 1280
|
||||
SCREEN_HEIGHT = 720
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
def initial_viewport_setup(screen_width, screen_height):
|
||||
general.set_viewport_size(screen_width, screen_height)
|
||||
general.update_viewport()
|
||||
TestHelper.wait_for_condition(
|
||||
function=lambda: isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1)
|
||||
and isclose(a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1),
|
||||
timeout_in_seconds=4.0
|
||||
)
|
||||
SCREENSHOT_NAME = "AtomBasicLevelSetup"
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
@@ -148,11 +138,16 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Setup: Deletes all existing entities before creating the scene.
|
||||
search_filter = azlmbr.entity.SearchFilter()
|
||||
all_entities = azlmbr.entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
|
||||
azlmbr.editor.ToolsApplicationRequestBus(azlmbr.bus.Broadcast, "DeleteEntities", all_entities)
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Close error windows and display helpers then update the viewport size.
|
||||
TestHelper.close_error_windows()
|
||||
TestHelper.close_display_helpers()
|
||||
initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||
initial_viewport_setup()
|
||||
general.update_viewport()
|
||||
|
||||
# 2. Create Default Level Entity.
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
class Tests:
|
||||
directional_light_component_disabled = (
|
||||
"Disabled Directional Light component",
|
||||
"Couldn't disable Directional Light component")
|
||||
enter_game_mode = (
|
||||
"Entered game mode",
|
||||
"Failed to enter game mode")
|
||||
exit_game_mode = (
|
||||
"Exited game mode",
|
||||
"Couldn't exit game mode")
|
||||
global_skylight_component_disabled = (
|
||||
"Disabled Global Skylight (IBL) component",
|
||||
"Couldn't disable Global Skylight (IBL) component")
|
||||
hdri_skybox_component_disabled = (
|
||||
"Disabled HDRi Skybox component",
|
||||
"Couldn't disable HDRi Skybox component")
|
||||
light_component_added = (
|
||||
"Light component added",
|
||||
"Couldn't add Light component")
|
||||
light_component_color_property_set = (
|
||||
"Color property was set",
|
||||
"Color property was not set")
|
||||
light_component_enable_shadow_property_set = (
|
||||
"Enable shadow property was set",
|
||||
"Enable shadow property was not set")
|
||||
light_component_enable_shutters_property_set = (
|
||||
"Enable shutters property was set",
|
||||
"Enable shutters property was not set")
|
||||
light_component_inner_angle_property_set = (
|
||||
"Inner angle property was set",
|
||||
"Inner angle property was not set")
|
||||
light_component_intensity_property_set = (
|
||||
"Intensity property was set",
|
||||
"Intensity property was not set")
|
||||
light_component_light_type_property_set = (
|
||||
"Light type property was set",
|
||||
"Light type property was not set")
|
||||
light_component_outer_angle_property_set = (
|
||||
"Outer angle property was set",
|
||||
"Outer angle property was not set")
|
||||
material_component_material_asset_property_set = (
|
||||
"Material Asset property was set",
|
||||
"Material Asset property was not set")
|
||||
spot_light_entity_created = (
|
||||
"Spot Light entity created",
|
||||
"Couldn't create Spot Light entity")
|
||||
|
||||
|
||||
def AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages():
|
||||
"""
|
||||
Summary:
|
||||
Light component test using the Spot (disk) Light type property option and modifying the shadows and colors.
|
||||
Sets each scene up and then takes a screenshot of each scene for test comparison.
|
||||
|
||||
Test setup:
|
||||
- Wait for Editor idle loop.
|
||||
- Open the "Base" level.
|
||||
- Close error windows and display helpers then update the viewport size.
|
||||
- Runs the create_basic_atom_rendering_scene() function to setup the test scene.
|
||||
|
||||
Expected Behavior:
|
||||
The test scripts sets up the scenes correctly and takes accurate screenshots.
|
||||
|
||||
Test Steps:
|
||||
1. Find the Directional Light entity then disable its Directional Light component.
|
||||
2. Disable Global Skylight (IBL) component on the Global Skylight (IBL) entity.
|
||||
3. Disable HDRi Skybox component on the Global Skylight (IBL) entity.
|
||||
4. Create a Spot Light entity and rotate it.
|
||||
5. Attach a Light component to the Spot Light entity.
|
||||
6. Set the Light component Light Type to Spot (disk).
|
||||
7. Enter game mode and take a screenshot then exit game mode.
|
||||
8. Change the default material asset for the Ground Plane entity.
|
||||
9. Enter game mode and take a screenshot then exit game mode.
|
||||
10. Increase the Intensity value of the Light component.
|
||||
11. Enter game mode and take a screenshot then exit game mode.
|
||||
12. Change the Light component Color property value.
|
||||
13. Enter game mode and take a screenshot then exit game mode.
|
||||
14. Change the Light component Enable shutters, Inner angle, and Outer angle property values.
|
||||
15. Enter game mode and take a screenshot then exit game mode.
|
||||
16. Change the Light component Enable shadow and Shadowmap size property values then move Spot Light entity.
|
||||
17. Enter game mode and take a screenshot then exit game mode.
|
||||
18. Look for errors.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.paths
|
||||
|
||||
from editor_python_test_tools.asset_utils import Asset
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report, Tracer, TestHelper
|
||||
|
||||
from Atom.atom_utils.atom_constants import AtomComponentProperties, LIGHT_TYPES
|
||||
from Atom.atom_utils.atom_component_helper import (
|
||||
initial_viewport_setup, create_basic_atom_rendering_scene, enter_exit_game_mode_take_screenshot)
|
||||
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Test setup begins.
|
||||
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
|
||||
TestHelper.init_idle()
|
||||
TestHelper.open_level("", "Base")
|
||||
|
||||
# Setup: Close error windows and display helpers then update the viewport size.
|
||||
TestHelper.close_error_windows()
|
||||
TestHelper.close_display_helpers()
|
||||
initial_viewport_setup()
|
||||
general.update_viewport()
|
||||
|
||||
# Setup: Runs the create_basic_atom_rendering_scene() function to setup the test scene.
|
||||
create_basic_atom_rendering_scene()
|
||||
|
||||
# Test steps begin.
|
||||
# 1. Find the Directional Light entity then disable its Directional Light component.
|
||||
directional_light_entity = EditorEntity.find_editor_entity(AtomComponentProperties.directional_light())
|
||||
directional_light_component = directional_light_entity.get_components_of_type(
|
||||
[AtomComponentProperties.directional_light()])[0]
|
||||
directional_light_component.disable_component()
|
||||
Report.critical_result(Tests.directional_light_component_disabled, not directional_light_component.is_enabled())
|
||||
|
||||
# 2. Disable Global Skylight (IBL) component on the Global Skylight (IBL) entity.
|
||||
global_skylight_entity = EditorEntity.find_editor_entity(AtomComponentProperties.global_skylight())
|
||||
global_skylight_component = global_skylight_entity.get_components_of_type(
|
||||
[AtomComponentProperties.global_skylight()])[0]
|
||||
global_skylight_component.disable_component()
|
||||
Report.critical_result(Tests.global_skylight_component_disabled, not global_skylight_component.is_enabled())
|
||||
|
||||
# 3. Disable HDRi Skybox component on the Global Skylight (IBL) entity.
|
||||
hdri_skybox_component = global_skylight_entity.get_components_of_type(
|
||||
[AtomComponentProperties.hdri_skybox()])[0]
|
||||
hdri_skybox_component.disable_component()
|
||||
Report.critical_result(Tests.hdri_skybox_component_disabled, not hdri_skybox_component.is_enabled())
|
||||
|
||||
# 4. Create a Spot Light entity and rotate it.
|
||||
spot_light_name = "Spot Light"
|
||||
spot_light_entity = EditorEntity.create_editor_entity_at(
|
||||
azlmbr.math.Vector3(0.7, -2.0, 1.0), spot_light_name)
|
||||
rotation = azlmbr.math.Vector3(DEGREE_RADIAN_FACTOR * 300.0, 0.0, 0.0)
|
||||
spot_light_entity.set_local_rotation(rotation)
|
||||
Report.critical_result(Tests.spot_light_entity_created, spot_light_entity.exists())
|
||||
|
||||
# 5. Attach a Light component to the Spot Light entity.
|
||||
light_component = spot_light_entity.add_component(AtomComponentProperties.light())
|
||||
Report.critical_result(Tests.light_component_added, light_component.is_enabled())
|
||||
|
||||
# 6. Set the Light component Light Type to Spot (disk).
|
||||
light_component.set_component_property_value(
|
||||
AtomComponentProperties.light('Light type'), LIGHT_TYPES['spot_disk'])
|
||||
Report.result(
|
||||
Tests.light_component_light_type_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Light type')) == LIGHT_TYPES['spot_disk'])
|
||||
|
||||
# 7. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("SpotLight_1.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 8. Change the default material asset for the Ground Plane entity.
|
||||
ground_plane_name = "Ground Plane"
|
||||
ground_plane_entity = EditorEntity.find_editor_entity(ground_plane_name)
|
||||
ground_plane_material_component_name = AtomComponentProperties.material()
|
||||
ground_plane_material_component = ground_plane_entity.get_components_of_type(
|
||||
[ground_plane_material_component_name])[0]
|
||||
ground_plane_material_asset_path = os.path.join(
|
||||
"Materials", "Presets", "Macbeth", "22_neutral_5-0_0-70d.azmaterial")
|
||||
ground_plane_material_asset = Asset.find_asset_by_path(ground_plane_material_asset_path, False)
|
||||
ground_plane_material_component.set_component_property_value(
|
||||
AtomComponentProperties.material('Material Asset'), ground_plane_material_asset.id)
|
||||
Report.result(
|
||||
Tests.material_component_material_asset_property_set,
|
||||
ground_plane_material_component.get_component_property_value(
|
||||
AtomComponentProperties.material('Material Asset')) == ground_plane_material_asset.id)
|
||||
|
||||
# 9. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("SpotLight_2.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 10. Increase the Intensity value of the Light component.
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Intensity'), 800.0)
|
||||
Report.result(
|
||||
Tests.light_component_intensity_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Intensity')) == 800.0)
|
||||
|
||||
# 11. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("SpotLight_3.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 12. Change the Light component Color property value.
|
||||
color_value = azlmbr.math.Color(47.0 / 255.0, 75.0 / 255.0, 37.0 / 255.0, 255.0 / 255.0)
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Color'), color_value)
|
||||
Report.result(
|
||||
Tests.light_component_color_property_set,
|
||||
light_component.get_component_property_value(AtomComponentProperties.light('Color')) == color_value)
|
||||
|
||||
# 13. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("SpotLight_4.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 14. Change the Light component Enable shutters, Inner angle, and Outer angle property values.
|
||||
enable_shutters = True
|
||||
inner_angle = 60.0
|
||||
outer_angle = 75.0
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Enable shutters'), enable_shutters)
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Inner angle'), inner_angle)
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Outer angle'), outer_angle)
|
||||
Report.result(
|
||||
Tests.light_component_enable_shutters_property_set,
|
||||
light_component.get_component_property_value(
|
||||
AtomComponentProperties.light('Enable shutters')) == enable_shutters)
|
||||
Report.result(
|
||||
Tests.light_component_inner_angle_property_set,
|
||||
light_component.get_component_property_value(AtomComponentProperties.light('Inner angle')) == inner_angle)
|
||||
Report.result(
|
||||
Tests.light_component_outer_angle_property_set,
|
||||
light_component.get_component_property_value(AtomComponentProperties.light('Outer angle')) == outer_angle)
|
||||
|
||||
# 15. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("SpotLight_5.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 16. Change the Light component Enable shadow and slightly move Spot Light entity.
|
||||
light_component.set_component_property_value(AtomComponentProperties.light('Enable shadow'), True)
|
||||
Report.result(
|
||||
Tests.light_component_enable_shadow_property_set,
|
||||
light_component.get_component_property_value(AtomComponentProperties.light('Enable shadow')) is True)
|
||||
spot_light_entity.set_world_rotation(azlmbr.math.Vector3(0.7, -2.0, 1.9))
|
||||
|
||||
# 17. Enter game mode and take a screenshot then exit game mode.
|
||||
enter_exit_game_mode_take_screenshot("SpotLight_6.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
|
||||
|
||||
# 18. Look for errors.
|
||||
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
|
||||
for error_info in error_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in error_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages)
|
||||
@@ -1,200 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
||||
from Atom.atom_utils.screenshot_utils import ScreenshotHelper
|
||||
|
||||
SCREEN_WIDTH = 1280
|
||||
SCREEN_HEIGHT = 720
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
helper = EditorTestHelper(log_prefix="Test_Atom_BasicLevelSetup")
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
1. View -> Layouts -> Restore Default Layout, sets the viewport to ratio 16:9 @ 1280 x 720
|
||||
2. Runs console command r_DisplayInfo = 0
|
||||
3. Deletes all entities currently present in the level.
|
||||
4. Creates a "default_level" entity to hold all other entities, setting the translate values to x:0, y:0, z:0
|
||||
5. Adds a Grid component to the "default_level" & updates its Grid Spacing to 1.0m
|
||||
6. Adds a "global_skylight" entity to "default_level", attaching an HDRi Skybox w/ a Cubemap Texture.
|
||||
7. Adds a Global Skylight (IBL) component w/ diffuse image and specular image to "global_skylight" entity.
|
||||
8. Adds a "ground_plane" entity to "default_level", attaching a Mesh component & Material component.
|
||||
9. Adds a "directional_light" entity to "default_level" & adds a Directional Light component.
|
||||
10. Adds a "sphere" entity to "default_level" & adds a Mesh component with a Material component to it.
|
||||
11. Adds a "camera" entity to "default_level" & adds a Camera component with 80 degree FOV and Transform values:
|
||||
Translate - x:5.5m, y:-12.0m, z:9.0m
|
||||
Rotate - x:-27.0, y:-12.0, z:25.0
|
||||
12. Finally enters game mode, takes a screenshot, & exits game mode.
|
||||
:return: None
|
||||
"""
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.camera as camera
|
||||
import azlmbr.entity as entity
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
import azlmbr.editor as editor
|
||||
|
||||
def initial_viewport_setup(screen_width, screen_height):
|
||||
general.set_viewport_size(screen_width, screen_height)
|
||||
general.update_viewport()
|
||||
helper.wait_for_condition(
|
||||
function=lambda: helper.isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1)
|
||||
and helper.isclose(a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1),
|
||||
timeout_in_seconds=4.0
|
||||
)
|
||||
result = helper.isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and helper.isclose(
|
||||
a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1)
|
||||
general.log(general.get_viewport_size().x)
|
||||
general.log(general.get_viewport_size().y)
|
||||
general.log(general.get_viewport_size().z)
|
||||
general.log(f"Viewport is set to the expected size: {result}")
|
||||
general.run_console("r_DisplayInfo = 0")
|
||||
|
||||
def after_level_load():
|
||||
"""Function to call after creating/opening a level to ensure it loads."""
|
||||
# Give everything a second to initialize.
|
||||
general.idle_enable(True)
|
||||
general.idle_wait(1.0)
|
||||
general.update_viewport()
|
||||
general.idle_wait(0.5) # half a second is more than enough for updating the viewport.
|
||||
|
||||
# Close out problematic windows, FPS meters, and anti-aliasing.
|
||||
if general.is_helpers_shown(): # Turn off the helper gizmos if visible
|
||||
general.toggle_helpers()
|
||||
general.idle_wait(1.0)
|
||||
if general.is_pane_visible("Error Report"): # Close Error Report windows that block focus.
|
||||
general.close_pane("Error Report")
|
||||
if general.is_pane_visible("Error Log"): # Close Error Log windows that block focus.
|
||||
general.close_pane("Error Log")
|
||||
general.idle_wait(1.0)
|
||||
general.run_console("r_displayInfo=0")
|
||||
general.idle_wait(1.0)
|
||||
|
||||
# Wait for Editor idle loop before executing Python hydra scripts.
|
||||
general.idle_enable(True)
|
||||
|
||||
# Basic setup for opened level.
|
||||
helper.open_level(level_name="Base")
|
||||
after_level_load()
|
||||
initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||
|
||||
# Create default_level entity
|
||||
search_filter = azlmbr.entity.SearchFilter()
|
||||
all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
|
||||
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities)
|
||||
|
||||
default_level = hydra.Entity("default_level")
|
||||
position = math.Vector3(0.0, 0.0, 0.0)
|
||||
default_level.create_entity(position, ["Grid"])
|
||||
default_level.get_set_test(0, "Controller|Configuration|Secondary Grid Spacing", 1.0)
|
||||
|
||||
# Create global_skylight entity and set the properties
|
||||
global_skylight = hydra.Entity("global_skylight")
|
||||
global_skylight.create_entity(
|
||||
entity_position=math.Vector3(0.0, 0.0, 0.0),
|
||||
components=["HDRi Skybox", "Global Skylight (IBL)"],
|
||||
parent_id=default_level.id
|
||||
)
|
||||
global_skylight_image_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage")
|
||||
global_skylight_image_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", global_skylight_image_asset_path, math.Uuid(), False)
|
||||
global_skylight.get_set_test(0, "Controller|Configuration|Cubemap Texture", global_skylight_image_asset)
|
||||
hydra.get_set_test(global_skylight, 1, "Controller|Configuration|Diffuse Image", global_skylight_image_asset)
|
||||
hydra.get_set_test(global_skylight, 1, "Controller|Configuration|Specular Image", global_skylight_image_asset)
|
||||
|
||||
# Create ground_plane entity and set the properties
|
||||
ground_plane = hydra.Entity("ground_plane")
|
||||
ground_plane.create_entity(
|
||||
entity_position=math.Vector3(0.0, 0.0, 0.0),
|
||||
components=["Material"],
|
||||
parent_id=default_level.id
|
||||
)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
|
||||
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
|
||||
ground_plane.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
|
||||
).GetValue()[0]
|
||||
)
|
||||
ground_plane_mesh_asset_path = os.path.join("TestData", "Objects", "plane.azmodel")
|
||||
ground_plane_mesh_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
|
||||
hydra.get_set_test(ground_plane, 1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
ground_plane_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_chrome.azmaterial")
|
||||
ground_plane_material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
|
||||
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset)
|
||||
|
||||
# Create directional_light entity and set the properties
|
||||
directional_light = hydra.Entity("directional_light")
|
||||
directional_light.create_entity(
|
||||
entity_position=math.Vector3(0.0, 0.0, 10.0),
|
||||
components=["Directional Light"],
|
||||
parent_id=default_level.id
|
||||
)
|
||||
rotation = math.Vector3(DEGREE_RADIAN_FACTOR * -90.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", directional_light.id, rotation)
|
||||
|
||||
# Create sphere entity and set the properties
|
||||
sphere = hydra.Entity("sphere")
|
||||
sphere.create_entity(
|
||||
entity_position=math.Vector3(0.0, 0.0, 1.0),
|
||||
components=["Material"],
|
||||
parent_id=default_level.id
|
||||
)
|
||||
|
||||
# Work around to add the correct Atom Mesh component and asset.
|
||||
sphere.components.append(
|
||||
editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", sphere.id, [mesh_type_id]
|
||||
).GetValue()[0]
|
||||
)
|
||||
sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel")
|
||||
sphere_mesh_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
|
||||
hydra.get_set_test(sphere, 1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset)
|
||||
|
||||
# Add Atom Material component and asset.
|
||||
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
|
||||
sphere_material_asset = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
|
||||
sphere.get_set_test(0, "Default Material|Material Asset", sphere_material_asset)
|
||||
|
||||
# Create camera component and set the properties
|
||||
camera_entity = hydra.Entity("camera")
|
||||
position = math.Vector3(5.5, -12.0, 9.0)
|
||||
camera_entity.create_entity(components=["Camera"], entity_position=position, parent_id=default_level.id)
|
||||
rotation = math.Vector3(
|
||||
DEGREE_RADIAN_FACTOR * -27.0, DEGREE_RADIAN_FACTOR * -12.0, DEGREE_RADIAN_FACTOR * 25.0
|
||||
)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", camera_entity.id, rotation)
|
||||
camera_entity.get_set_test(0, "Controller|Configuration|Field of view", 60.0)
|
||||
camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
|
||||
|
||||
# Enter game mode, take screenshot, & exit game mode.
|
||||
general.idle_wait(0.5)
|
||||
general.enter_game_mode()
|
||||
general.idle_wait(1.0)
|
||||
helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=2.0)
|
||||
ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{'AtomBasicLevelSetup'}.ppm")
|
||||
general.exit_game_mode()
|
||||
helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=2.0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -1,261 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils
|
||||
from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
||||
|
||||
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
|
||||
|
||||
LEVEL_NAME = "Base"
|
||||
LIGHT_COMPONENT = "Light"
|
||||
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Sets up the tests by making sure the required level is created & setup correctly.
|
||||
It then executes 2 test cases - see each associated test function's docstring for more info.
|
||||
|
||||
Finally prints the string "Light component tests completed" after completion
|
||||
|
||||
Tests will fail immediately if any of these log lines are found:
|
||||
1. Trace::Assert
|
||||
2. Trace::Error
|
||||
3. Traceback (most recent call last):
|
||||
|
||||
:return: None
|
||||
"""
|
||||
atom_component_helper.create_basic_atom_level(level_name=LEVEL_NAME)
|
||||
|
||||
# Run tests.
|
||||
area_light_test()
|
||||
spot_light_test()
|
||||
general.log("Light component tests completed.")
|
||||
|
||||
|
||||
def area_light_test():
|
||||
"""
|
||||
Basic test for the "Light" component attached to an "area_light" entity.
|
||||
|
||||
Test Case - Light Component: Capsule, Spot (disk), and Point (sphere):
|
||||
1. Creates "area_light" entity w/ a Light component that has a Capsule Light type w/ the color set to 255, 0, 0
|
||||
2. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
3. Sets the Light component Intensity Mode to Lumens (default).
|
||||
4. Ensures the Light component Mode is Automatic (default).
|
||||
5. Sets the Intensity value of the Light component to 0.0
|
||||
6. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
7. Updates the Intensity value of the Light component to 1000.0
|
||||
8. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
9. Swaps the Capsule light type option to Spot (disk) light type on the Light component
|
||||
10. Updates "area_light" entity Transform rotate value to x: 90.0, y:0.0, z:0.0
|
||||
11. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
12. Swaps the Spot (disk) light type for the Point (sphere) light type in the Light component.
|
||||
13. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
14. Deletes the Light component from the "area_light" entity and verifies its successful.
|
||||
"""
|
||||
# Create an "area_light" entity with "Light" component using Light type of "Capsule"
|
||||
area_light_entity_name = "area_light"
|
||||
area_light = hydra.Entity(area_light_entity_name)
|
||||
area_light.create_entity(math.Vector3(-1.0, -2.0, 3.0), [LIGHT_COMPONENT])
|
||||
general.log(
|
||||
f"{area_light_entity_name}_test: Component added to the entity: "
|
||||
f"{hydra.has_components(area_light.id, [LIGHT_COMPONENT])}")
|
||||
light_component_id_pair = hydra.attach_component_to_entity(area_light.id, LIGHT_COMPONENT)
|
||||
|
||||
# Select the "Capsule" light type option.
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_id_pair,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_constants.LIGHT_TYPES['capsule']
|
||||
)
|
||||
|
||||
# Update color and take screenshot in game mode
|
||||
color = math.Color(255.0, 0.0, 0.0, 0.0)
|
||||
area_light.get_set_test(0, "Controller|Configuration|Color", color)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_1", area_light_entity_name)
|
||||
|
||||
# Update intensity value to 0.0 and take screenshot in game mode
|
||||
area_light.get_set_test(0, "Controller|Configuration|Attenuation Radius|Mode", 1)
|
||||
area_light.get_set_test(0, "Controller|Configuration|Intensity", 0.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_2", area_light_entity_name)
|
||||
|
||||
# Update intensity value to 1000.0 and take screenshot in game mode
|
||||
area_light.get_set_test(0, "Controller|Configuration|Intensity", 1000.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_3", area_light_entity_name)
|
||||
|
||||
# Swap the "Capsule" light type option to "Spot (disk)" light type
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_id_pair,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_constants.LIGHT_TYPES['spot_disk']
|
||||
)
|
||||
area_light_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 90.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", area_light.id, area_light_rotation)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_4", area_light_entity_name)
|
||||
|
||||
# Swap the "Spot (disk)" light type to the "Point (sphere)" light type and take screenshot.
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_id_pair,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_constants.LIGHT_TYPES['sphere']
|
||||
)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_5", area_light_entity_name)
|
||||
|
||||
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", area_light.id)
|
||||
|
||||
|
||||
def spot_light_test():
|
||||
"""
|
||||
Basic test for the Light component attached to a "spot_light" entity.
|
||||
|
||||
Test Case - Light Component: Spot (disk) with shadows & colors:
|
||||
1. Creates "spot_light" entity w/ a Light component attached to it.
|
||||
2. Selects the "directional_light" entity already present in the level and disables it.
|
||||
3. Selects the "global_skylight" entity already present in the level and disables the HDRi Skybox component,
|
||||
as well as the Global Skylight (IBL) component.
|
||||
4. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
5. Selects the "ground_plane" entity and changes updates the material to a new material.
|
||||
6. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
7. Selects the "spot_light" entity and increases the Light component Intensity to 800 lm
|
||||
8. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
9. Selects the "spot_light" entity and sets the Light component Color to 47, 75, 37
|
||||
10. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
11. Selects the "spot_light" entity and modifies the Shutter controls to the following values:
|
||||
- Enable shutters: True
|
||||
- Inner Angle: 60.0
|
||||
- Outer Angle: 75.0
|
||||
12. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
13. Selects the "spot_light" entity and modifies the Shadow controls to the following values:
|
||||
- Enable Shadow: True
|
||||
- ShadowmapSize: 256
|
||||
14. Modifies the world translate position of the "spot_light" entity to 0.7, -2.0, 1.9 (for casting shadows better)
|
||||
15. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
"""
|
||||
# Disable "Directional Light" component for the "directional_light" entity
|
||||
# "directional_light" entity is created by the create_basic_atom_level() function by default.
|
||||
directional_light_entity_id = hydra.find_entity_by_name("directional_light")
|
||||
directional_light = hydra.Entity(name='directional_light', id=directional_light_entity_id)
|
||||
directional_light_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Directional Light"], 0)[0]
|
||||
directional_light_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', directional_light.id, directional_light_component_type
|
||||
).GetValue()
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [directional_light_component])
|
||||
general.idle_wait(0.5)
|
||||
|
||||
# Disable "Global Skylight (IBL)" and "HDRi Skybox" components for the "global_skylight" entity
|
||||
global_skylight_entity_id = hydra.find_entity_by_name("global_skylight")
|
||||
global_skylight = hydra.Entity(name='global_skylight', id=global_skylight_entity_id)
|
||||
global_skylight_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Global Skylight (IBL)"], 0)[0]
|
||||
global_skylight_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, global_skylight_component_type
|
||||
).GetValue()
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [global_skylight_component])
|
||||
hdri_skybox_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["HDRi Skybox"], 0)[0]
|
||||
hdri_skybox_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, hdri_skybox_component_type
|
||||
).GetValue()
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [hdri_skybox_component])
|
||||
general.idle_wait(0.5)
|
||||
|
||||
# Create a "spot_light" entity with "Light" component using Light Type of "Spot (disk)"
|
||||
spot_light_entity_name = "spot_light"
|
||||
spot_light = hydra.Entity(spot_light_entity_name)
|
||||
spot_light.create_entity(math.Vector3(0.7, -2.0, 1.0), [LIGHT_COMPONENT])
|
||||
general.log(
|
||||
f"{spot_light_entity_name}_test: Component added to the entity: "
|
||||
f"{hydra.has_components(spot_light.id, [LIGHT_COMPONENT])}")
|
||||
rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 300.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", spot_light.id, rotation)
|
||||
light_component_type = hydra.attach_component_to_entity(spot_light.id, LIGHT_COMPONENT)
|
||||
editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_type,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_constants.LIGHT_TYPES['spot_disk']
|
||||
)
|
||||
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_1", spot_light_entity_name)
|
||||
|
||||
# Change default material of ground plane entity and take screenshot
|
||||
ground_plane_entity_id = hydra.find_entity_by_name("ground_plane")
|
||||
ground_plane = hydra.Entity(name='ground_plane', id=ground_plane_entity_id)
|
||||
ground_plane_asset_path = os.path.join("Materials", "Presets", "MacBeth", "22_neutral_5-0_0-70d.azmaterial")
|
||||
ground_plane_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_asset_path, math.Uuid(), False)
|
||||
material_property_path = "Default Material|Material Asset"
|
||||
material_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Material"], 0)[0]
|
||||
material_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', ground_plane.id, material_component_type).GetValue()
|
||||
editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
material_component,
|
||||
material_property_path,
|
||||
ground_plane_asset_value
|
||||
)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_2", spot_light_entity_name)
|
||||
|
||||
# Increase intensity value of the Spot light and take screenshot in game mode
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Intensity", 800.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_3", spot_light_entity_name)
|
||||
|
||||
# Update the Spot light color and take screenshot in game mode
|
||||
color_value = math.Color(47.0 / 255.0, 75.0 / 255.0, 37.0 / 255.0, 255.0 / 255.0)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Color", color_value)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_4", spot_light_entity_name)
|
||||
|
||||
# Update the Shutter controls of the Light component and take screenshot
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Enable shutters", True)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Inner angle", 60.0)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Outer angle", 75.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_5", spot_light_entity_name)
|
||||
|
||||
# Update the Shadow controls, move the spot_light entity world translate position and take screenshot
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shadows|Enable shadow", True)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shadows|Shadowmap size", 256.0)
|
||||
azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "SetWorldTranslation", spot_light.id, math.Vector3(0.7, -2.0, 1.9))
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_6", spot_light_entity_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
+112
-8
@@ -25,7 +25,7 @@ class EditorComponent:
|
||||
"""
|
||||
EditorComponent class used to set and get the component property value using path
|
||||
EditorComponent object is returned from either of
|
||||
EditorEntity.add_component() or Entity.add_components() or EditorEntity.get_component_objects()
|
||||
EditorEntity.add_component() or Entity.add_components() or EditorEntity.get_components_of_type()
|
||||
which also assigns self.id and self.type_id to the EditorComponent object.
|
||||
"""
|
||||
|
||||
@@ -94,6 +94,13 @@ class EditorComponent:
|
||||
"""
|
||||
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", self.id)
|
||||
|
||||
def disable_component(self):
|
||||
"""
|
||||
Used to disable the component using its id value.
|
||||
:return: None
|
||||
"""
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [self.id])
|
||||
|
||||
@staticmethod
|
||||
def get_type_ids(component_names: list) -> list:
|
||||
"""
|
||||
@@ -107,7 +114,6 @@ class EditorComponent:
|
||||
return type_ids
|
||||
|
||||
|
||||
|
||||
def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
|
||||
"""
|
||||
Converts a vector3-like element into a azlmbr.math.Vector3
|
||||
@@ -120,6 +126,7 @@ def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
|
||||
else:
|
||||
raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
|
||||
|
||||
|
||||
class EditorEntity:
|
||||
"""
|
||||
Entity class is used to create and interact with Editor Entities.
|
||||
@@ -136,10 +143,11 @@ class EditorEntity:
|
||||
|
||||
# Creation functions
|
||||
@classmethod
|
||||
def find_editor_entity(cls, entity_name: str, must_be_unique : bool = False) -> EditorEntity:
|
||||
def find_editor_entity(cls, entity_name: str, must_be_unique: bool = False) -> EditorEntity:
|
||||
"""
|
||||
Given Entity name, outputs entity object
|
||||
:param entity_name: Name of entity to find
|
||||
:param must_be_unique: bool that asserts the entity_name specified is unique when set to True
|
||||
:return: EditorEntity class object
|
||||
"""
|
||||
entities = cls.find_editor_entities([entity_name])
|
||||
@@ -147,14 +155,14 @@ class EditorEntity:
|
||||
if must_be_unique:
|
||||
assert len(entities) == 1, f"Failure: Multiple entities with name: '{entity_name}' when expected only one"
|
||||
|
||||
entity = cls(entities[0])
|
||||
entity = entities[0]
|
||||
return entity
|
||||
|
||||
@classmethod
|
||||
def find_editor_entities(cls, entity_names: List[str]) -> EditorEntity:
|
||||
def find_editor_entities(cls, entity_names: List[str]) -> List[EditorEntity]:
|
||||
"""
|
||||
Given Entities names, returns a list of EditorEntity
|
||||
:param entity_name: Name of entity to find
|
||||
:param entity_names: List of entity names to find
|
||||
:return: List[EditorEntity] class object
|
||||
"""
|
||||
searchFilter = azlmbr.entity.SearchFilter()
|
||||
@@ -438,7 +446,7 @@ class EditorEntity:
|
||||
def set_local_rotation(self, new_rotation) -> None:
|
||||
"""
|
||||
Sets the set the local rotation(relative to the parent) of the current entity.
|
||||
:param vector3_rotation: The math.Vector3 value to use for rotation on the entity (uses radians).
|
||||
:param new_rotation: The math.Vector3 value to use for rotation on the entity (uses radians).
|
||||
:return: None
|
||||
"""
|
||||
new_rotation = convert_to_azvector3(new_rotation)
|
||||
@@ -454,7 +462,7 @@ class EditorEntity:
|
||||
def set_local_translation(self, new_translation) -> None:
|
||||
"""
|
||||
Sets the local translation(relative to the parent) of the current entity.
|
||||
:param vector3_translation: The math.Vector3 value to use for translation on the entity.
|
||||
:param new_translation: The math.Vector3 value to use for translation on the entity.
|
||||
:return: None
|
||||
"""
|
||||
new_translation = convert_to_azvector3(new_translation)
|
||||
@@ -470,3 +478,99 @@ class EditorEntity:
|
||||
assert self.id.isValid(), "A valid entity id is required to focus on its owning prefab."
|
||||
focus_prefab_result = azlmbr.prefab.PrefabFocusPublicRequestBus(bus.Broadcast, "FocusOnOwningPrefab", self.id)
|
||||
assert focus_prefab_result.IsSuccess(), f"Prefab operation 'FocusOnOwningPrefab' failed. Error: {focus_prefab_result.GetError()}"
|
||||
|
||||
|
||||
class EditorLevelEntity:
|
||||
"""
|
||||
EditorLevel class used to add and fetch level components.
|
||||
Level entity is a special entity that you do not create/destroy independently of larger systems of level creation.
|
||||
This collects a number of staticmethods that do not rely on entityId since Level entity is found internally by
|
||||
EditorLevelComponentAPIBus requests.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_type_ids(component_names: list) -> list:
|
||||
"""
|
||||
Used to get type ids of given components list for EntityType Level
|
||||
:param: component_names: List of components to get type ids
|
||||
:return: List of type ids of given components.
|
||||
"""
|
||||
type_ids = editor.EditorComponentAPIBus(
|
||||
bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Level
|
||||
)
|
||||
return type_ids
|
||||
|
||||
@staticmethod
|
||||
def add_component(component_name: str) -> EditorComponent:
|
||||
"""
|
||||
Used to add new component to Level.
|
||||
:param component_name: String of component name to add.
|
||||
:return: Component object of newly added component.
|
||||
"""
|
||||
component = EditorLevelEntity.add_components([component_name])[0]
|
||||
return component
|
||||
|
||||
@staticmethod
|
||||
def add_components(component_names: list) -> List[EditorComponent]:
|
||||
"""
|
||||
Used to add multiple components
|
||||
:param: component_names: List of components to add to level
|
||||
:return: List of newly added components to the level
|
||||
"""
|
||||
components = []
|
||||
type_ids = EditorLevelEntity.get_type_ids(component_names)
|
||||
for type_id in type_ids:
|
||||
new_comp = EditorComponent()
|
||||
new_comp.type_id = type_id
|
||||
add_component_outcome = editor.EditorLevelComponentAPIBus(
|
||||
bus.Broadcast, "AddComponentsOfType", [type_id]
|
||||
)
|
||||
assert (
|
||||
add_component_outcome.IsSuccess()
|
||||
), f"Failure: Could not add component: '{new_comp.get_component_name()}' to level"
|
||||
new_comp.id = add_component_outcome.GetValue()[0]
|
||||
components.append(new_comp)
|
||||
return components
|
||||
|
||||
@staticmethod
|
||||
def get_components_of_type(component_names: list) -> List[EditorComponent]:
|
||||
"""
|
||||
Used to get components of type component_name that already exists on the level
|
||||
:param component_names: List of names of components to check
|
||||
:return: List of Level Component objects of given component name
|
||||
"""
|
||||
component_list = []
|
||||
type_ids = EditorLevelEntity.get_type_ids(component_names)
|
||||
for type_id in type_ids:
|
||||
component = EditorComponent()
|
||||
component.type_id = type_id
|
||||
get_component_of_type_outcome = editor.EditorLevelComponentAPIBus(
|
||||
bus.Broadcast, "GetComponentOfType", type_id
|
||||
)
|
||||
assert (
|
||||
get_component_of_type_outcome.IsSuccess()
|
||||
), f"Failure: Level does not have component:'{component.get_component_name()}'"
|
||||
component.id = get_component_of_type_outcome.GetValue()
|
||||
component_list.append(component)
|
||||
|
||||
return component_list
|
||||
|
||||
@staticmethod
|
||||
def has_component(component_name: str) -> bool:
|
||||
"""
|
||||
Used to verify if the level has the specified component
|
||||
:param component_name: Name of component to check for
|
||||
:return: True, if level has specified component. Else, False
|
||||
"""
|
||||
type_ids = EditorLevelEntity.get_type_ids([component_name])
|
||||
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "HasComponentOfType", type_ids[0])
|
||||
|
||||
@staticmethod
|
||||
def count_components_of_type(component_name: str) -> int:
|
||||
"""
|
||||
Used to get a count of the specified level component attached to the level
|
||||
:param component_name: Name of component to check for
|
||||
:return: integer count of occurences of level component attached to level or zero if none are present
|
||||
"""
|
||||
type_ids = EditorLevelEntity.get_type_ids([component_name])
|
||||
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "CountComponentsOfType", type_ids[0])
|
||||
|
||||
+26
@@ -46,6 +46,18 @@ def get_component_type_id(component_name):
|
||||
return component_type_id
|
||||
|
||||
|
||||
def get_level_component_type_id(component_name):
|
||||
"""
|
||||
Gets the component_type_id from a given component name
|
||||
:param component_name: String of component name to search for
|
||||
:return component type ID
|
||||
"""
|
||||
type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component_name],
|
||||
entity.EntityType().Level)
|
||||
component_type_id = type_ids_list[0]
|
||||
return component_type_id
|
||||
|
||||
|
||||
def add_level_component(component_name):
|
||||
"""
|
||||
Adds the specified component to the Level Inspector
|
||||
@@ -145,6 +157,20 @@ def get_component_property_value(component, component_propertyPath):
|
||||
print(f'FAILURE: Could not get value from {component_propertyPath}')
|
||||
return None
|
||||
|
||||
def set_component_property_value(component, component_propertyPath, value):
|
||||
"""
|
||||
Given a component name and component property path, set component property value
|
||||
:param component: Component object to act on.
|
||||
:param componentPropertyPath: String of component property. (e.g. 'Settings|Visible')
|
||||
:param value: new value for the variable being changed in the component
|
||||
"""
|
||||
componentPropertyObj = editor.EditorComponentAPIBus(bus.Broadcast, 'SetComponentProperty', component,
|
||||
component_propertyPath, value)
|
||||
if componentPropertyObj.IsSuccess():
|
||||
print(f'{component_propertyPath} set to {value}')
|
||||
else:
|
||||
print(f'FAILURE: Could not set value in {component_propertyPath}')
|
||||
|
||||
|
||||
def get_property_tree(component):
|
||||
"""
|
||||
|
||||
+4
-1
@@ -58,7 +58,10 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
|
||||
if null_renderer:
|
||||
editor.args.extend(["-rhi=Null"])
|
||||
if enable_prefab_system:
|
||||
editor.args.extend(["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
|
||||
from os import path
|
||||
editor.args.extend([
|
||||
"--regset=/Amazon/Preferences/EnablePrefabSystem=true",
|
||||
f"--regset-file={os.path.join(workspace.paths.engine_root(), 'Registry', 'prefab.test.setreg')}"])
|
||||
else:
|
||||
editor.args.extend(["--regset=/Amazon/Preferences/EnablePrefabSystem=false"])
|
||||
|
||||
|
||||
-1
@@ -39,7 +39,6 @@ def get_prefab_file_path(prefab_path):
|
||||
prefab_path = name + ".prefab"
|
||||
return prefab_path
|
||||
|
||||
|
||||
def get_all_entity_ids():
|
||||
return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter())
|
||||
|
||||
|
||||
+8
-6
@@ -88,8 +88,7 @@ class TestHelper:
|
||||
general.idle_wait_frames(200)
|
||||
|
||||
@staticmethod
|
||||
def enter_game_mode(msgtuple_success_fail : Tuple[str, str]):
|
||||
# type: (tuple) -> None
|
||||
def enter_game_mode(msgtuple_success_fail: Tuple[str, str]) -> None:
|
||||
"""
|
||||
:param msgtuple_success_fail: The tuple with the expected/unexpected messages for entering game mode.
|
||||
|
||||
@@ -102,8 +101,7 @@ class TestHelper:
|
||||
Report.critical_result(msgtuple_success_fail, general.is_in_game_mode())
|
||||
|
||||
@staticmethod
|
||||
def multiplayer_enter_game_mode(msgtuple_success_fail : Tuple[str, str], sv_default_player_spawn_asset : str):
|
||||
# type: (tuple) -> None
|
||||
def multiplayer_enter_game_mode(msgtuple_success_fail: Tuple[str, str], sv_default_player_spawn_asset: str) -> None:
|
||||
"""
|
||||
:param msgtuple_success_fail: The tuple with the expected/unexpected messages for entering game mode.
|
||||
:param sv_default_player_spawn_asset: The path to the network player prefab that will be automatically spawned upon entering gamemode. The engine default is "prefabs/player.network.spawnable"
|
||||
@@ -142,12 +140,16 @@ class TestHelper:
|
||||
# make sure the server launcher is running
|
||||
waiter.wait_for(lambda: process_utils.process_exists("AutomatedTesting.ServerLauncher", ignore_extensions=True), timeout=5.0, exc=AssertionError("AutomatedTesting.ServerLauncher has NOT launched!"), interval=1.0)
|
||||
|
||||
# make sure the editor connects to the editor-server and sends the level data packet
|
||||
wait_for_critical_expected_line("MultiplayerEditorConnection: Editor-server activation has found and connected to the editor.", section_tracer.prints, 15.0)
|
||||
|
||||
wait_for_critical_expected_line("Editor is sending the editor-server the level data packet.", section_tracer.prints, 5.0)
|
||||
|
||||
# make sure the editor finally connects to the editor-server network simulation
|
||||
wait_for_critical_expected_line("Logger: Editor Server completed receiving the editor's level assets, responding to Editor...", section_tracer.prints, 5.0)
|
||||
|
||||
wait_for_critical_expected_line("Editor-server ready. Editor has successfully connected to the editor-server's network simulation.", section_tracer.prints, 5.0)
|
||||
|
||||
wait_for_critical_unexpected_line(f"MultiplayerSystemComponent: SpawnDefaultPlayerPrefab failed. Missing sv_defaultPlayerSpawnAsset at path '{sv_default_player_spawn_asset.lower()}'.", section_tracer.prints, 0.5)
|
||||
|
||||
TestHelper.wait_for_condition(lambda : multiplayer.PythonEditorFuncs_is_in_game_mode(), 5.0)
|
||||
Report.critical_result(msgtuple_success_fail, multiplayer.PythonEditorFuncs_is_in_game_mode())
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ class TestAutomation(TestAutomationBase):
|
||||
|
||||
def test_NvCloth_AddClothSimulationToMesh(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import NvCloth_AddClothSimulationToMesh as test_module
|
||||
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer, enable_prefab_system=False)
|
||||
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer)
|
||||
|
||||
def test_NvCloth_AddClothSimulationToActor(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import NvCloth_AddClothSimulationToActor as test_module
|
||||
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer, enable_prefab_system=False)
|
||||
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer)
|
||||
|
||||
@@ -98,38 +98,32 @@ class FileManagement:
|
||||
"""
|
||||
file_map = FileManagement._load_file_map()
|
||||
backup_path = FileManagement.backup_folder_path
|
||||
backup_file_name = "{}.bak".format(file_name)
|
||||
backup_file = os.path.join(backup_path, backup_file_name)
|
||||
# If backup directory DNE, make one
|
||||
if not os.path.exists(backup_path):
|
||||
os.mkdir(backup_path)
|
||||
# If "traditional" backup file exists, delete it (myFile.txt.bak)
|
||||
if os.path.exists(backup_file):
|
||||
fs.delete([backup_file], True, False)
|
||||
# Find my next storage name (myFile_1.txt.bak)
|
||||
backup_storage_file_name = FileManagement._next_available_name(backup_file_name, file_map)
|
||||
if backup_storage_file_name is None:
|
||||
|
||||
# Find my next storage name (myFile_1.txt)
|
||||
backup_file_name = FileManagement._next_available_name(file_name, file_map)
|
||||
if backup_file_name is None:
|
||||
# If _next_available_name returns None, we have backed up MAX_BACKUPS of files name [file_name]
|
||||
raise Exception(
|
||||
"FileManagement class ran out of backups per name. Max: {}".format(FileManagement.MAX_BACKUPS)
|
||||
)
|
||||
backup_storage_file = os.path.join(backup_path, backup_storage_file_name)
|
||||
|
||||
# If this backup file already exists, delete it.
|
||||
backup_storage_file = "{}.bak".format(os.path.normpath(os.path.join(backup_path, backup_file_name)))
|
||||
if os.path.exists(backup_storage_file):
|
||||
# This file should not exists, but if it does it's about to get clobbered!
|
||||
fs.unlock_file(backup_storage_file)
|
||||
# Create "traditional" backup file (myFile.txt.bak)
|
||||
fs.create_backup(os.path.join(file_path, file_name), backup_path)
|
||||
# Copy "traditional" backup file into storage backup (myFile_1.txt.bak)
|
||||
FileManagement._copy_file(backup_file_name, backup_path, backup_storage_file_name, backup_path)
|
||||
fs.lock_file(backup_storage_file)
|
||||
# Delete "traditional" back up file
|
||||
fs.unlock_file(backup_file)
|
||||
fs.delete([backup_file], True, False)
|
||||
fs.delete([backup_storage_file], True, False)
|
||||
|
||||
# Create backup file (myFile_1.txt.bak)
|
||||
original_file = os.path.normpath(os.path.join(file_path, file_name))
|
||||
fs.create_backup(original_file, backup_path, backup_file_name)
|
||||
|
||||
# Update file map with new file
|
||||
file_map[os.path.join(file_path, file_name)] = backup_storage_file_name
|
||||
file_map[original_file] = backup_file_name
|
||||
FileManagement._save_file_map(file_map)
|
||||
# Unlock original file to get it ready to be edited by the test
|
||||
fs.unlock_file(os.path.join(file_path, file_name))
|
||||
fs.unlock_file(original_file)
|
||||
|
||||
@staticmethod
|
||||
def _restore_file(file_name, file_path):
|
||||
@@ -143,20 +137,15 @@ class FileManagement:
|
||||
"""
|
||||
file_map = FileManagement._load_file_map()
|
||||
backup_path = FileManagement.backup_folder_path
|
||||
src_file = os.path.join(file_path, file_name)
|
||||
src_file = os.path.normpath(os.path.join(file_path, file_name))
|
||||
if src_file in file_map:
|
||||
backup_file = os.path.join(backup_path, file_map[src_file])
|
||||
if os.path.exists(backup_file):
|
||||
fs.unlock_file(backup_file)
|
||||
fs.unlock_file(src_file)
|
||||
# Make temporary copy of backed up file to restore from
|
||||
temp_file = "{}.bak".format(file_name)
|
||||
FileManagement._copy_file(file_map[src_file], backup_path, temp_file, backup_path)
|
||||
fs.restore_backup(src_file, backup_path)
|
||||
fs.lock_file(src_file)
|
||||
# Delete backup file
|
||||
fs.delete([os.path.join(backup_path, temp_file)], True, False)
|
||||
backup_file_name = file_map[src_file]
|
||||
backup_file = "{}.bak".format(os.path.join(backup_path, backup_file_name))
|
||||
|
||||
fs.unlock_file(src_file)
|
||||
if fs.restore_backup(src_file, backup_path, backup_file_name):
|
||||
fs.delete([backup_file], True, False)
|
||||
|
||||
# Remove from file map
|
||||
del file_map[src_file]
|
||||
FileManagement._save_file_map(file_map)
|
||||
|
||||
@@ -12,7 +12,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
NAME AutomatedTesting::PrefabTests
|
||||
TEST_SUITE main
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
AZ::AssetProcessor
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
|
||||
|
||||
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
class TestAutomationNoAutoTestMode(EditorTestSuite):
|
||||
|
||||
# Enable only -BatchMode for these tests. Some tests cannot run in -autotest_mode due to UI interactions
|
||||
global_extra_cmdline_args = ["-BatchMode"]
|
||||
|
||||
class test_CreatePrefab_UnderAnEntity(EditorSharedTest):
|
||||
from .tests.create_prefab import CreatePrefab_UnderAnEntity as test_module
|
||||
|
||||
class test_CreatePrefab_UnderAnotherPrefab(EditorSharedTest):
|
||||
from .tests.create_prefab import CreatePrefab_UnderAnotherPrefab as test_module
|
||||
|
||||
class test_DeleteEntity_UnderAnotherPrefab(EditorSharedTest):
|
||||
from .tests.delete_entity import DeleteEntity_UnderAnotherPrefab as test_module
|
||||
|
||||
class test_DeleteEntity_UnderLevelPrefab(EditorSharedTest):
|
||||
from .tests.delete_entity import DeleteEntity_UnderLevelPrefab as test_module
|
||||
|
||||
class test_ReparentPrefab_UnderAnotherPrefab(EditorSharedTest):
|
||||
from .tests.reparent_prefab import ReparentPrefab_UnderAnotherPrefab as test_module
|
||||
|
||||
class test_DetachPrefab_UnderAnotherPrefab(EditorSharedTest):
|
||||
from .tests.detach_prefab import DetachPrefab_UnderAnotherPrefab as test_module
|
||||
|
||||
class test_OpenLevel_ContainingTwoEntities(EditorSharedTest):
|
||||
from .tests.open_level import OpenLevel_ContainingTwoEntities as test_module
|
||||
|
||||
class test_CreatePrefab_WithSingleEntity(EditorSharedTest):
|
||||
from .tests.create_prefab import CreatePrefab_WithSingleEntity as test_module
|
||||
|
||||
class test_InstantiatePrefab_ContainingASingleEntity(EditorSharedTest):
|
||||
from .tests.instantiate_prefab import InstantiatePrefab_ContainingASingleEntity as test_module
|
||||
|
||||
class test_DeletePrefab_ContainingASingleEntity(EditorSharedTest):
|
||||
from .tests.delete_prefab import DeletePrefab_ContainingASingleEntity as test_module
|
||||
|
||||
class test_DuplicatePrefab_ContainingASingleEntity(EditorSharedTest):
|
||||
from .tests.duplicate_prefab import DuplicatePrefab_ContainingASingleEntity as test_module
|
||||
+1
@@ -24,6 +24,7 @@ def CreatePrefab_WithSingleEntity():
|
||||
# Creates a prefab from the new entity
|
||||
Prefab.create_prefab(car_prefab_entities, CAR_PREFAB_FILE_NAME)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(CreatePrefab_WithSingleEntity)
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
def DetachPrefab_UnderAnotherPrefab():
|
||||
|
||||
CAR_PREFAB_FILE_NAME = 'car_prefab'
|
||||
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab'
|
||||
CAR_PREFAB_FILE_NAME = 'car_prefab2'
|
||||
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab2'
|
||||
|
||||
import editor_python_test_tools.pyside_utils as pyside_utils
|
||||
|
||||
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
class MacroMaterialTests:
|
||||
setup_test = (
|
||||
"Setup successful",
|
||||
"Setup failed"
|
||||
)
|
||||
material_changed_not_called_when_inactive = (
|
||||
"OnTerrainMacroMaterialRegionChanged not called successfully",
|
||||
"OnTerrainMacroMaterialRegionChanged called when component inactive."
|
||||
)
|
||||
material_created = (
|
||||
"MaterialCreated called successfully",
|
||||
"MaterialCreated failed"
|
||||
)
|
||||
material_destroyed = (
|
||||
"MaterialDestroyed called successfully",
|
||||
"MaterialDestroyed failed"
|
||||
)
|
||||
material_recreated = (
|
||||
"MaterialCreated called successfully on second test",
|
||||
"MaterialCreated failed on second test"
|
||||
)
|
||||
material_changed_call_on_aabb_change = (
|
||||
"OnTerrainMacroMaterialRegionChanged called successfully",
|
||||
"Timed out waiting for OnTerrainMacroMaterialRegionChanged"
|
||||
)
|
||||
|
||||
def TerrainMacroMaterialComponent_MacroMaterialActivates():
|
||||
"""
|
||||
Summary:
|
||||
Load an empty level, create a MacroMaterialComponent and check assigning textures results in the correct callbacks.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import math as sys_math
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as math
|
||||
import azlmbr.terrain as terrain
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.vegetation as vegetation
|
||||
import azlmbr.entity as EntityId
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import editor_python_test_tools.pyside_utils as pyside_utils
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.asset_utils import Asset
|
||||
|
||||
material_created_called = False
|
||||
material_changed_called = False
|
||||
material_region_changed_called = False
|
||||
material_destroyed_called = False
|
||||
|
||||
def create_entity_at(entity_name, components_to_add, x, y, z):
|
||||
entity = EditorEntity.create_editor_entity_at([x, y, z], entity_name)
|
||||
for component in components_to_add:
|
||||
entity.add_component(component)
|
||||
|
||||
return entity
|
||||
|
||||
def on_macro_material_created(args):
|
||||
nonlocal material_created_called
|
||||
material_created_called = True
|
||||
|
||||
def on_macro_material_changed(args):
|
||||
nonlocal material_changed_called
|
||||
material_changed_called = True
|
||||
|
||||
def on_macro_material_region_changed(args):
|
||||
nonlocal material_region_changed_called
|
||||
material_region_changed_called = True
|
||||
|
||||
def on_macro_material_destroyed(args):
|
||||
nonlocal material_destroyed_called
|
||||
material_destroyed_called = True
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# Open a level.
|
||||
helper.open_level("Physics", "Base")
|
||||
helper.wait_for_condition(lambda: general.get_current_level_name() == "Base", 2.0)
|
||||
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
# Set up a handler to wait for notifications from the TerrainSystem.
|
||||
handler = terrain.TerrainMacroMaterialAutomationBusHandler()
|
||||
handler.connect()
|
||||
handler.add_callback("OnTerrainMacroMaterialCreated", on_macro_material_created)
|
||||
handler.add_callback("OnTerrainMacroMaterialChanged", on_macro_material_changed)
|
||||
handler.add_callback("OnTerrainMacroMaterialRegionChanged", on_macro_material_region_changed)
|
||||
handler.add_callback("OnTerrainMacroMaterialDestroyed", on_macro_material_destroyed)
|
||||
|
||||
macro_material_entity = create_entity_at("macro", ["Terrain Macro Material", "Axis Aligned Box Shape"], 0.0, 0.0, 0.0)
|
||||
|
||||
# Check that no macro material callbacks happened. It should be "inactive" as it has no assets assigned.
|
||||
setup_success = not material_created_called and not material_changed_called and not material_region_changed_called and not material_destroyed_called
|
||||
Report.result(MacroMaterialTests.setup_test, setup_success)
|
||||
|
||||
# Find the aabb component.
|
||||
aabb_component_type_id_type = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Axis Aligned Box Shape"], 0)[0]
|
||||
aabb_component_id = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'GetComponentOfType', macro_material_entity.id, aabb_component_type_id_type).GetValue()
|
||||
|
||||
# Change the aabb dimensions
|
||||
material_region_changed_called = False
|
||||
box_dimensions_path = "Axis Aligned Box Shape|Box Configuration|Dimensions"
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", aabb_component_id, box_dimensions_path, math.Vector3(1.0, 1.0, 1.0))
|
||||
|
||||
# Check we don't receive a callback. The macro material component should be inactive as it has no images assigned.
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(MacroMaterialTests.material_changed_not_called_when_inactive, material_region_changed_called == False)
|
||||
|
||||
# Find the macro material component.
|
||||
macro_material_id_type = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Terrain Macro Material"], 0)[0]
|
||||
macro_material_component_id = azlmbr.editor.EditorComponentAPIBus(azlmbr.bus.Broadcast, 'GetComponentOfType', macro_material_entity.id, macro_material_id_type).GetValue()
|
||||
|
||||
# Find a color image asset.
|
||||
color_image_path = os.path.join("assets", "textures", "image.png.streamingimage")
|
||||
color_image_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", color_image_path, math.Uuid(), False)
|
||||
|
||||
# Assign the image to the MacroMaterial component, which should result in a created message.
|
||||
material_created_called = False
|
||||
color_texture_path = "Configuration|Color Texture"
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", macro_material_component_id, color_texture_path, color_image_asset)
|
||||
|
||||
call_result = helper.wait_for_condition(lambda: material_created_called == True, 2.0)
|
||||
Report.result(MacroMaterialTests.material_created, call_result)
|
||||
|
||||
# Find a normal image asset.
|
||||
normal_image_path = os.path.join("assets", "textures", "normal.png.streamingimage")
|
||||
normal_image_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", normal_image_path, math.Uuid(), False)
|
||||
|
||||
# Assign the normal image to the MacroMaterial component, which should result in a created message.
|
||||
material_created_called = False
|
||||
material_destroyed_called = False
|
||||
normal_texture_path = "Configuration|Normal Texture"
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", macro_material_component_id, normal_texture_path, normal_image_asset)
|
||||
|
||||
# Check the MacroMaterial was destroyed and recreated.
|
||||
destroyed_call_result = helper.wait_for_condition(lambda: material_destroyed_called == True, 2.0)
|
||||
Report.result(MacroMaterialTests.material_destroyed, destroyed_call_result)
|
||||
|
||||
recreated_call_result = helper.wait_for_condition(lambda: material_created_called == True, 2.0)
|
||||
Report.result(MacroMaterialTests.material_recreated, recreated_call_result)
|
||||
|
||||
# Change the aabb dimensions.
|
||||
box_dimensions_path = "Axis Aligned Box Shape|Box Configuration|Dimensions"
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", aabb_component_id, box_dimensions_path, math.Vector3(1.0, 1.0, 1.0))
|
||||
|
||||
# Check that a callback is received.
|
||||
region_changed_call_result = helper.wait_for_condition(lambda: material_region_changed_called == True, 2.0)
|
||||
Report.result(MacroMaterialTests.material_changed_call_on_aabb_change, region_changed_call_result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(TerrainMacroMaterialComponent_MacroMaterialActivates)
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
class VegetationTests:
|
||||
vegetation_on_gradient_1 = (
|
||||
"Vegetation detected at correct position on Gradient1",
|
||||
"Vegetation not detected at correct position on Gradient1"
|
||||
)
|
||||
vegetation_on_gradient_2 = (
|
||||
"Vegetation detected at correct position on Gradient2",
|
||||
"Vegetation not detected at correct position on Gradient2"
|
||||
)
|
||||
unfiltered_vegetation_count_correct = (
|
||||
"Unfiltered vegetation spawn count correct",
|
||||
"Unfiltered vegetation spawn count incorrect"
|
||||
)
|
||||
|
||||
testTag2_excluded_vegetation_count_correct = (
|
||||
"TestTag2 filtered vegetation count correct",
|
||||
"TestTag2 filtered vegetation count incorrect"
|
||||
)
|
||||
testTag2_excluded_vegetation_z_correct = (
|
||||
"TestTag2 filtered vegetation spawned in correct position",
|
||||
"TestTag2 filtered vegetation failed to spawn in correct position"
|
||||
)
|
||||
|
||||
testTag3_excluded_vegetation_count_correct = (
|
||||
"TestTag3 filtered vegetation count correct",
|
||||
"TestTag3 filtered vegetation count incorrect"
|
||||
)
|
||||
testTag3_excluded_vegetation_z_correct = (
|
||||
"TestTag3 filtered vegetation spawned in correct position",
|
||||
"TestTag3 filtered vegetation failed to spawn in correct position"
|
||||
)
|
||||
|
||||
cleared_exclusion_vegetation_count_correct = (
|
||||
"Cleared filter vegetation count correct",
|
||||
"Cleared filter vegetation count incorrect"
|
||||
)
|
||||
|
||||
def TerrainSystem_VegetationSpawnsOnTerrainSurfaces():
|
||||
"""
|
||||
Summary:
|
||||
Load an empty level,
|
||||
Create two entities with constant gradient components with different values.
|
||||
Create two entities with TerrainLayerSpawners
|
||||
Create an entity to spawn vegetation
|
||||
Ensure that vegetation spawns at the correct heights
|
||||
Add a VegetationSurfaceMaskFilter and ensure it responds correctly to surface changes.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import math as sys_math
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as math
|
||||
|
||||
import azlmbr.areasystem as areasystem
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.vegetation as vegetation
|
||||
import azlmbr.terrain as terrain
|
||||
import azlmbr.entity as EntityId
|
||||
import azlmbr.surface_data as surface_data
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
def create_entity_at(entity_name, components_to_add, x, y, z):
|
||||
entity = hydra.Entity(entity_name)
|
||||
entity.create_entity(math.Vector3(x, y, z), components_to_add)
|
||||
|
||||
return entity
|
||||
|
||||
def FindHighestAndLowestZValuesInArea(aabb):
|
||||
vegetation_items = areasystem.AreaSystemRequestBus(bus.Broadcast, 'GetInstancesInAabb', aabb)
|
||||
|
||||
lowest_z = min([item.position.z for item in vegetation_items])
|
||||
highest_z = max([item.position.z for item in vegetation_items])
|
||||
|
||||
return highest_z, lowest_z
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# Open an empty level.
|
||||
helper.open_level("Physics", "Base")
|
||||
helper.wait_for_condition(lambda: general.get_current_level_name() == "Base", 2.0)
|
||||
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
box_height = 20.0
|
||||
box_y_position = 10.0
|
||||
box_dimensions = math.Vector3(20.0, 20.0, box_height)
|
||||
|
||||
# Add Terrain Rendering
|
||||
hydra.add_level_component("Terrain World")
|
||||
hydra.add_level_component("Terrain World Renderer")
|
||||
|
||||
# Create two terrain entities at adjoining positions
|
||||
terrain_entity_1 = create_entity_at("Terrain1", ["Terrain Layer Spawner", "Axis Aligned Box Shape", "Terrain Height Gradient List", "Terrain Surface Gradient List"], 0.0, box_y_position, box_height/2.0)
|
||||
terrain_entity_1.get_set_test(1, "Axis Aligned Box Shape|Box Configuration|Dimensions", box_dimensions)
|
||||
|
||||
terrain_entity_2 = create_entity_at("Terrain2", ["Terrain Layer Spawner", "Axis Aligned Box Shape", "Terrain Height Gradient List", "Terrain Surface Gradient List"], 20.0, box_y_position, box_height/2.0)
|
||||
terrain_entity_2.get_set_test(1, "Axis Aligned Box Shape|Box Configuration|Dimensions", box_dimensions)
|
||||
|
||||
# Create two gradient entities.
|
||||
gradient_value_1 = 0.25
|
||||
gradient_value_2 = 0.5
|
||||
|
||||
gradient_entity_1 = create_entity_at("Gradient1", ["Constant Gradient"], 0.0, 0.0, 0.0)
|
||||
gradient_entity_1.get_set_test(0, "Configuration|Value", gradient_value_1)
|
||||
|
||||
gradient_entity_2 = create_entity_at("Gradient2", ["Constant Gradient"], 0.0, 0.0, 0.0)
|
||||
gradient_entity_2.get_set_test(0, "Configuration|Value", gradient_value_2)
|
||||
|
||||
mapping = terrain.TerrainSurfaceGradientMapping()
|
||||
mapping.gradientEntityId = gradient_entity_1.id
|
||||
pte = hydra.get_property_tree(terrain_entity_1.components[3])
|
||||
pte.add_container_item("Configuration|Gradient to Surface Mappings", 0, mapping)
|
||||
|
||||
mapping = terrain.TerrainSurfaceGradientMapping()
|
||||
mapping.gradientEntityId = gradient_entity_2.id
|
||||
pte = hydra.get_property_tree(terrain_entity_2.components[3])
|
||||
pte.add_container_item("Configuration|Gradient to Surface Mappings", 0, mapping)
|
||||
|
||||
# create a vegetation entity that overlaps both terrain entities.
|
||||
vegetation_entity = create_entity_at("Vegetation", ["Vegetation Layer Spawner", "Axis Aligned Box Shape", "Vegetation Asset List", "Vegetation Surface Mask Filter"], 10.0, box_y_position, box_height/2.0)
|
||||
vegetation_entity.get_set_test(1, "Axis Aligned Box Shape|Box Configuration|Dimensions", box_dimensions)
|
||||
|
||||
# Set the vegetation area to a PrefabInstanceSpawner with a specific prefab asset selected.
|
||||
prefab_spawner = vegetation.PrefabInstanceSpawner()
|
||||
prefab_spawner.SetPrefabAssetPath(os.path.join("Prefabs", "PinkFlower.spawnable"))
|
||||
descriptor = hydra.get_component_property_value(vegetation_entity.components[2], 'Configuration|Embedded Assets|[0]')
|
||||
descriptor.spawner = prefab_spawner
|
||||
vegetation_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
|
||||
|
||||
# Assign gradients to layer spawners.
|
||||
terrain_entity_1.get_set_test(2, "Configuration|Gradient Entities", [gradient_entity_1.id])
|
||||
terrain_entity_2.get_set_test(2, "Configuration|Gradient Entities", [gradient_entity_2.id])
|
||||
|
||||
# Move view so that the entities are visible.
|
||||
general.set_current_view_position(17.0, -66.0, 41.0)
|
||||
general.set_current_view_rotation(-15, 0, 0)
|
||||
|
||||
# Expected item counts under conditions to be tested.
|
||||
# By default, vegetation spawns at a density of 20 items per 16 meters,
|
||||
# so in a 20m square, there should be around 25 ^ 2 items depending on whether area edges are included.
|
||||
# In this case there are 26 ^ 2 items.
|
||||
expected_surface_tag_excluded_item_count = 338
|
||||
expected_no_exclusions_item_count = 676
|
||||
|
||||
# Wait for the vegetation to spawn
|
||||
helper.wait_for_condition(lambda: vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id) == expected_no_exclusions_item_count, 5.0)
|
||||
|
||||
# Check the spawn count is correct.
|
||||
item_count = vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id)
|
||||
Report.result(VegetationTests.unfiltered_vegetation_count_correct, item_count == expected_no_exclusions_item_count)
|
||||
|
||||
test_aabb = math.Aabb_CreateFromMinMax(math.Vector3(-10.0, -10.0, 0.0), math.Vector3(30.0, 10.0, box_height))
|
||||
|
||||
# Find the z positions of the items with the lowest and highest x values, this will avoid the overlap area where z values are blended between the surface heights.
|
||||
highest_z, lowest_z = FindHighestAndLowestZValuesInArea(test_aabb)
|
||||
|
||||
# Check that the z values are as expected.
|
||||
Report.result(VegetationTests.vegetation_on_gradient_1, sys_math.isclose(lowest_z, box_height * gradient_value_1, abs_tol=0.01))
|
||||
Report.result(VegetationTests.vegetation_on_gradient_2, sys_math.isclose(highest_z, box_height * gradient_value_2, abs_tol=0.01))
|
||||
|
||||
# Assign SurfaceTags to the SurfaceGradientLists
|
||||
terrain_entity_1.get_set_test(3, "Configuration|Gradient to Surface Mappings|[0]|Surface Tag", surface_data.SurfaceTag("test_tag2"))
|
||||
terrain_entity_2.get_set_test(3, "Configuration|Gradient to Surface Mappings|[0]|Surface Tag", surface_data.SurfaceTag("test_tag3"))
|
||||
|
||||
# Give the VegetationSurfaceFilter an exclusion list, set it to exclude test_tag2 which should remove all the lower items which are in terrain_entity_1.
|
||||
vegetation_entity.get_set_test(3, "Configuration|Exclusion|Surface Tags", [surface_data.SurfaceTag()])
|
||||
vegetation_entity.get_set_test(3, "Configuration|Exclusion|Surface Tags|[0]", surface_data.SurfaceTag("test_tag2"))
|
||||
|
||||
# Wait for the vegetation to respawn and check z values.
|
||||
helper.wait_for_condition(lambda: vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id) == expected_surface_tag_excluded_item_count, 5.0)
|
||||
|
||||
item_count = vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id)
|
||||
Report.result(VegetationTests.testTag2_excluded_vegetation_count_correct, item_count == expected_surface_tag_excluded_item_count)
|
||||
|
||||
highest_z, lowest_z = FindHighestAndLowestZValuesInArea(test_aabb)
|
||||
|
||||
Report.result(VegetationTests.testTag2_excluded_vegetation_z_correct, lowest_z > box_height * gradient_value_1)
|
||||
|
||||
# Clear the filter and ensure vegetation respawns.
|
||||
vegetation_entity.get_set_test(3, "Configuration|Exclusion|Surface Tags|[0]", surface_data.SurfaceTag("invalid"))
|
||||
helper.wait_for_condition(lambda: vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id) == expected_no_exclusions_item_count, 5.0)
|
||||
|
||||
item_count = vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id)
|
||||
Report.result(VegetationTests.cleared_exclusion_vegetation_count_correct, item_count == expected_no_exclusions_item_count)
|
||||
|
||||
# Exclude test_tag3 to exclude the higher items in terrain_entity_2 and recheck.
|
||||
vegetation_entity.get_set_test(3, "Configuration|Exclusion|Surface Tags|[0]", surface_data.SurfaceTag("test_tag3"))
|
||||
|
||||
helper.wait_for_condition(lambda: vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id) == expected_surface_tag_excluded_item_count, 5.0)
|
||||
|
||||
item_count = vegetation.VegetationSpawnerRequestBus(bus.Event, "GetAreaProductCount", vegetation_entity.id)
|
||||
Report.result(VegetationTests.testTag3_excluded_vegetation_count_correct, item_count == expected_surface_tag_excluded_item_count)
|
||||
|
||||
highest_z, lowest_z = FindHighestAndLowestZValuesInArea(test_aabb)
|
||||
|
||||
Report.result(VegetationTests.testTag3_excluded_vegetation_z_correct, highest_z < box_height * gradient_value_2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(TerrainSystem_VegetationSpawnsOnTerrainSurfaces)
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
#fmt: off
|
||||
class Tests():
|
||||
level_components_added = ("Level components added correctly", "Failed to create level components")
|
||||
create_terrain_spawner_entity = ("Terrain_spawner_entity created successfully", "Failed to create terrain_spawner_entity")
|
||||
create_height_provider_entity = ("Height_provider_entity created successfully", "Failed to create height_provider_entity")
|
||||
bounds_max_changed = ("Terrain World Bounds Max changed successfully", "Failed to change Terrain World Bounds Max")
|
||||
bounds_min_changed = ("Terrain World Bounds Min changed successfully", "Failed to change Terrain World Bounds Min")
|
||||
height_query_changed = ("Terrain World Height Query Resolution changed successfully", "Failed to change Height Query Resolution")
|
||||
box_dimensions_changed = ("Aabb dimensions changed successfully", "Failed to change Aabb dimensions")
|
||||
shape_changed = ("Shape changed successfully", "Failed Shape change")
|
||||
frequency_changed = ("Frequency changed successfully", "Failed Frequency change")
|
||||
entity_added = ("Entity added successfully", "Failed Entity add")
|
||||
terrain_exists = ("Terrain exists at the provided point", "Terrain does not exist at the provided point")
|
||||
terrain_does_not_exist = ("Terrain does not exist at the provided point", "Terrain exists at the provided point")
|
||||
values_not_the_same = ("The tested values are not the same", "The tested values are the same")
|
||||
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
|
||||
#fmt: on
|
||||
|
||||
def Terrain_World_ConfigurationWorks():
|
||||
"""
|
||||
Summary:
|
||||
Test the Terrain World configuration changes when parameters are changed in the component
|
||||
|
||||
Test Steps:
|
||||
Expected Behavior:
|
||||
The Editor is stable there are no warnings or errors.
|
||||
|
||||
Test Steps:
|
||||
1) Start the Tracer to catch any errors and warnings
|
||||
2) Load the base level
|
||||
3) Load the level components
|
||||
4) Create 2 test entities, one parent at 512.0, 512.0, 50.0 and one child at the default position and add the required components
|
||||
5) Set the base Terrain World values
|
||||
6) Change the Axis Aligned Box Shape dimensions
|
||||
7) Set the Shape Reference to terrain_spawner_entity
|
||||
8) Set the FastNoise Gradient frequency to 0.01
|
||||
9) Set the Gradient List to height_provider_entity
|
||||
10) Disable and Enable the Terrain Gradient List so that it is recognised
|
||||
11) Check terrain exists at a known position in the world
|
||||
12) Check terrain does not exist at a known position outside the world
|
||||
13) Check height value is the expected one when query resolution is changed
|
||||
"""
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import TestHelper as helper, Report
|
||||
from editor_python_test_tools.utils import Report, Tracer
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
import azlmbr.math as azmath
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.terrain as terrain
|
||||
import math
|
||||
|
||||
SET_BOX_X_SIZE = 2048.0
|
||||
SET_BOX_Y_SIZE = 2048.0
|
||||
SET_BOX_Z_SIZE = 100.0
|
||||
CLAMP = 1
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Start the Tracer to catch any errors and warnings
|
||||
with Tracer() as section_tracer:
|
||||
# 2) Load the level
|
||||
helper.open_level("", "Base")
|
||||
helper.wait_for_condition(lambda: general.get_current_level_name() == "Base", 2.0)
|
||||
|
||||
# 3) Load the level components
|
||||
terrain_world_component = hydra.add_level_component("Terrain World")
|
||||
terrain_world_renderer = hydra.add_level_component("Terrain World Renderer")
|
||||
Report.critical_result(Tests.level_components_added,
|
||||
terrain_world_component is not None and terrain_world_renderer is not None)
|
||||
|
||||
# 4) Create 2 test entities, one parent at 512.0, 512.0, 50.0 and one child at the default position and add the required components
|
||||
entity1_components_to_add = ["Axis Aligned Box Shape", "Terrain Layer Spawner", "Terrain Height Gradient List", "Terrain Physics Heightfield Collider"]
|
||||
entity2_components_to_add = ["Shape Reference", "Gradient Transform Modifier", "FastNoise Gradient"]
|
||||
terrain_spawner_entity = hydra.Entity("TerrainEntity")
|
||||
terrain_spawner_entity.create_entity(azmath.Vector3(512.0, 512.0, 50.0), entity1_components_to_add)
|
||||
Report.result(Tests.create_terrain_spawner_entity, terrain_spawner_entity.id.IsValid())
|
||||
height_provider_entity = hydra.Entity("HeightProviderEntity")
|
||||
height_provider_entity.create_entity(azmath.Vector3(0.0, 0.0, 0.0), entity2_components_to_add,terrain_spawner_entity.id)
|
||||
Report.result(Tests.create_height_provider_entity, height_provider_entity.id.IsValid())
|
||||
|
||||
# Give everything a chance to finish initializing.
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
# 5) Set the base Terrain World values
|
||||
world_bounds_max = azmath.Vector3(1100.0, 1100.0, 1100.0)
|
||||
world_bounds_min = azmath.Vector3(10.0, 10.0, 10.0)
|
||||
height_query_resolution = azmath.Vector2(1.0, 1.0)
|
||||
hydra.set_component_property_value(terrain_world_component, "Configuration|World Bounds (Max)", world_bounds_max)
|
||||
hydra.set_component_property_value(terrain_world_component, "Configuration|World Bounds (Min)", world_bounds_min)
|
||||
hydra.set_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)", height_query_resolution)
|
||||
world_max = hydra.get_component_property_value(terrain_world_component, "Configuration|World Bounds (Max)")
|
||||
world_min = hydra.get_component_property_value(terrain_world_component, "Configuration|World Bounds (Min)")
|
||||
world_query = hydra.get_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)")
|
||||
Report.result(Tests.bounds_max_changed, world_max == world_bounds_max)
|
||||
Report.result(Tests.bounds_min_changed, world_min == world_bounds_min)
|
||||
Report.result(Tests.height_query_changed, world_query == height_query_resolution)
|
||||
|
||||
# 6) Change the Axis Aligned Box Shape dimensions
|
||||
box_dimensions = azmath.Vector3(SET_BOX_X_SIZE, SET_BOX_Y_SIZE, SET_BOX_Z_SIZE)
|
||||
terrain_spawner_entity.get_set_test(0, "Axis Aligned Box Shape|Box Configuration|Dimensions", box_dimensions)
|
||||
box_shape_dimensions = hydra.get_component_property_value(terrain_spawner_entity.components[0], "Axis Aligned Box Shape|Box Configuration|Dimensions")
|
||||
Report.result(Tests.box_dimensions_changed, box_dimensions == box_shape_dimensions)
|
||||
|
||||
# 7) Set the Shape Reference to terrain_spawner_entity
|
||||
height_provider_entity.get_set_test(0, "Configuration|Shape Entity Id", terrain_spawner_entity.id)
|
||||
entityId = hydra.get_component_property_value(height_provider_entity.components[0], "Configuration|Shape Entity Id")
|
||||
Report.result(Tests.shape_changed, entityId == terrain_spawner_entity.id)
|
||||
|
||||
# 8) Set the FastNoise Gradient frequency to 0.01
|
||||
frequency = 0.01
|
||||
height_provider_entity.get_set_test(2, "Configuration|Frequency", frequency)
|
||||
frequencyVal = hydra.get_component_property_value(height_provider_entity.components[2], "Configuration|Frequency")
|
||||
Report.result(Tests.frequency_changed, math.isclose(frequency, frequencyVal, abs_tol = 0.00001))
|
||||
|
||||
# 9) Set the Gradient List to height_provider_entity
|
||||
propertyTree = hydra.get_property_tree(terrain_spawner_entity.components[2])
|
||||
propertyTree.add_container_item("Configuration|Gradient Entities", 0, height_provider_entity.id)
|
||||
checkID = propertyTree.get_container_item("Configuration|Gradient Entities", 0)
|
||||
Report.result(Tests.entity_added, checkID.GetValue() == height_provider_entity.id)
|
||||
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
# 10) Disable and Enable the Terrain Gradient List so that it is recognised, EnableComponents performs both actions.
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, 'EnableComponents', [terrain_spawner_entity.components[2]])
|
||||
|
||||
# 11) Check terrain exists at a known position in the world
|
||||
terrainExists = not terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 10.0, 10.0, CLAMP)
|
||||
Report.result(Tests.terrain_exists, terrainExists)
|
||||
|
||||
terrainExists = not terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 1100.0, 1100.0, CLAMP)
|
||||
Report.result(Tests.terrain_exists, terrainExists)
|
||||
|
||||
# 12) Check terrain does not exist at a known position outside the world
|
||||
terrainDoesNotExist = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 1101.0, 1101.0, CLAMP)
|
||||
Report.result(Tests.terrain_does_not_exist, terrainDoesNotExist)
|
||||
|
||||
terrainDoesNotExist = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetIsHoleFromFloats', 9.0, 9.0, CLAMP)
|
||||
Report.result(Tests.terrain_does_not_exist, terrainDoesNotExist)
|
||||
|
||||
# 13) Check height value is the expected one when query resolution is changed
|
||||
testpoint = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetHeightFromFloats', 10.5, 10.5, CLAMP)
|
||||
height_query_resolution = azmath.Vector2(0.5, 0.5)
|
||||
hydra.set_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)", height_query_resolution)
|
||||
general.idle_wait_frames(1)
|
||||
testpoint2 = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetHeightFromFloats', 10.5, 10.5, CLAMP)
|
||||
Report.result(Tests.values_not_the_same, not math.isclose(testpoint, testpoint2, abs_tol = 0.000000001))
|
||||
|
||||
helper.wait_for_condition(lambda: section_tracer.has_errors or section_tracer.has_asserts, 1.0)
|
||||
for error_info in section_tracer.errors:
|
||||
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
|
||||
for assert_info in section_tracer.asserts:
|
||||
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(Terrain_World_ConfigurationWorks)
|
||||
|
||||
@@ -27,6 +27,15 @@ class TestAutomation(EditorTestSuite):
|
||||
|
||||
class test_Terrain_SupportsPhysics(EditorSharedTest):
|
||||
from .EditorScripts import Terrain_SupportsPhysics as test_module
|
||||
|
||||
|
||||
class test_TerrainHeightGradientList_AddRemoveGradientWorks(EditorSharedTest):
|
||||
from .EditorScripts import TerrainHeightGradientList_AddRemoveGradientWorks as test_module
|
||||
|
||||
class test_TerrainSystem_VegetationSpawnsOnTerrainSurfaces(EditorSharedTest):
|
||||
from .EditorScripts import TerrainSystem_VegetationSpawnsOnTerrainSurfaces as test_module
|
||||
|
||||
class test_TerrainMacroMaterialComponent_MacroMaterialActivates(EditorSharedTest):
|
||||
from .EditorScripts import TerrainMacroMaterialComponent_MacroMaterialActivates as test_module
|
||||
|
||||
class test_TerrainWorld_ConfigurationWorks(EditorSharedTest):
|
||||
from .EditorScripts import Terrain_World_ConfigurationWorks as test_module
|
||||
|
||||
@@ -8,10 +8,12 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
import os
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
import pytest
|
||||
import time
|
||||
|
||||
from os import path
|
||||
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
@@ -98,7 +100,9 @@ class TestAutomationBase:
|
||||
if autotest_mode:
|
||||
pycmd += ["-autotest_mode"]
|
||||
if enable_prefab_system:
|
||||
pycmd += ["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
|
||||
pycmd += [
|
||||
"--regset=/Amazon/Preferences/EnablePrefabSystem=true",
|
||||
f"--regset-file={path.join(workspace.paths.engine_root(), 'Registry', 'prefab.test.setreg')}"]
|
||||
else:
|
||||
pycmd += ["--regset=/Amazon/Preferences/EnablePrefabSystem=false"]
|
||||
|
||||
@@ -128,7 +132,8 @@ class TestAutomationBase:
|
||||
errors.append(TestRunError("FAILED TEST", error_str))
|
||||
if return_code and return_code != TestAutomationBase.TEST_FAIL_RETCODE: # Crashed
|
||||
crash_info = "-- No crash log available --"
|
||||
crash_log = os.path.join(workspace.paths.project_log(), 'error.log')
|
||||
crash_log = workspace.paths.crash_log()
|
||||
|
||||
try:
|
||||
waiter.wait_for(lambda: os.path.exists(crash_log), timeout=TestAutomationBase.WAIT_FOR_CRASH_LOG)
|
||||
except AssertionError:
|
||||
@@ -177,7 +182,7 @@ class TestAutomationBase:
|
||||
@staticmethod
|
||||
def _kill_ly_processes(include_asset_processor=True):
|
||||
LY_PROCESSES = [
|
||||
'Editor', 'Profiler', 'RemoteConsole',
|
||||
'Editor', 'Profiler', 'RemoteConsole', 'AutomatedTesting.ServerLauncher'
|
||||
]
|
||||
AP_PROCESSES = [
|
||||
'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder', 'CrySCompileServer',
|
||||
|
||||
+9
-3
@@ -51,19 +51,22 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude():
|
||||
|
||||
import os
|
||||
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as math
|
||||
import azlmbr.prefab as prefab
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
# 1) Open an existing simple level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "Base")
|
||||
helper.open_level("", "Base")
|
||||
|
||||
# Set view of planting area for visual debugging
|
||||
general.set_current_view_position(512.0, 500.0, 38.0)
|
||||
@@ -71,8 +74,11 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude():
|
||||
|
||||
# 2) Create a new entity with required vegetation area components
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
|
||||
|
||||
flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel")
|
||||
flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0]
|
||||
|
||||
spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, flower_prefab)
|
||||
|
||||
# Add a Vegetation Altitude Filter
|
||||
spawner_entity.add_component("Vegetation Altitude Filter")
|
||||
|
||||
+8
-3
@@ -32,7 +32,9 @@ def AltitudeFilter_FilterStageToggle():
|
||||
import os
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as math
|
||||
import azlmbr.prefab as prefab
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
|
||||
@@ -44,13 +46,16 @@ def AltitudeFilter_FilterStageToggle():
|
||||
|
||||
# Open an existing simple level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "Base")
|
||||
helper.open_level("", "Base")
|
||||
general.set_current_view_position(512.0, 480.0, 38.0)
|
||||
|
||||
# Create basic vegetation entity
|
||||
position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
|
||||
|
||||
flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel")
|
||||
flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0]
|
||||
|
||||
vegetation = dynveg.create_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, flower_prefab)
|
||||
|
||||
# Add a Vegetation Altitude Filter to the vegetation area entity
|
||||
vegetation.add_component("Vegetation Altitude Filter")
|
||||
|
||||
+6
-3
@@ -57,7 +57,7 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude():
|
||||
|
||||
# 1) Open an existing simple level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "Base")
|
||||
helper.open_level("", "Base")
|
||||
|
||||
# Set view of planting area for visual debugging
|
||||
general.set_current_view_position(512.0, 500.0, 38.0)
|
||||
@@ -65,8 +65,11 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude():
|
||||
|
||||
# 2) Create a new entity with required vegetation area components
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, asset_path)
|
||||
|
||||
flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel")
|
||||
flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0]
|
||||
|
||||
spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, flower_prefab)
|
||||
|
||||
# Add a Vegetation Altitude Filter
|
||||
spawner_entity.add_component("Vegetation Altitude Filter")
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea():
|
||||
|
||||
# 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow
|
||||
# spawning empty assets
|
||||
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None)
|
||||
spawner_entity.remove_component("Vegetation Asset List")
|
||||
spawner_entity.add_component("Vegetation Asset List Combiner")
|
||||
spawner_entity.add_component("Vegetation Asset Weight Selector")
|
||||
|
||||
+2
-2
@@ -67,8 +67,8 @@ def AssetWeightSelector_InstancesExpressBasedOnWeight():
|
||||
# valid slice entity, and one set to None
|
||||
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
desc_asset = hydra.get_component_property_value(spawner_entity.components[2],
|
||||
"Configuration|Embedded Assets")[0]
|
||||
desc_list = [desc_asset, desc_asset]
|
||||
|
||||
+2
-2
@@ -72,8 +72,8 @@ def DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius():
|
||||
# 2) Create a new entity with required vegetation area components
|
||||
spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
|
||||
# 3) Create a surface to plant on
|
||||
surface_center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ def DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius():
|
||||
# 2) Create a new entity with required vegetation area components
|
||||
spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
|
||||
# 3) Create a surface to plant on
|
||||
surface_center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ def DynamicSliceInstanceSpawner_Embedded_E2E():
|
||||
# 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path)
|
||||
spawner_entity.add_component("Script Canvas")
|
||||
instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas")
|
||||
instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ def InstanceSpawnerPriority_LayerAndSubPriority():
|
||||
# 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area
|
||||
spawner_center_point = math.Vector3(508.0, 508.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0,
|
||||
asset_path)
|
||||
blocker_center_point = math.Vector3(516.0, 516.0, 32.0)
|
||||
blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0)
|
||||
|
||||
|
||||
+8
-8
@@ -86,17 +86,17 @@ def LayerBlender_E2E_Editor():
|
||||
# 2) Create 2 vegetation areas with different meshes
|
||||
purple_position = math.Vector3(504.0, 512.0, 32.0)
|
||||
purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity_1 = dynveg.create_vegetation_area("Purple Spawner",
|
||||
purple_position,
|
||||
16.0, 16.0, 1.0,
|
||||
purple_asset_path)
|
||||
spawner_entity_1 = dynveg.create_dynamic_slice_vegetation_area("Purple Spawner",
|
||||
purple_position,
|
||||
16.0, 16.0, 1.0,
|
||||
purple_asset_path)
|
||||
|
||||
pink_position = math.Vector3(520.0, 512.0, 32.0)
|
||||
pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity_2 = dynveg.create_vegetation_area("Pink Spawner",
|
||||
pink_position,
|
||||
16.0, 16.0, 1.0,
|
||||
pink_asset_path)
|
||||
spawner_entity_2 = dynveg.create_dynamic_slice_vegetation_area("Pink Spawner",
|
||||
pink_position,
|
||||
16.0, 16.0, 1.0,
|
||||
pink_asset_path)
|
||||
|
||||
base_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
dynveg.create_surface_entity("Surface Entity",
|
||||
|
||||
+2
-2
@@ -68,8 +68,8 @@ def LayerBlocker_InstancesBlockedInConfiguredArea():
|
||||
# 2) Create a new instance spawner entity
|
||||
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
|
||||
# 3) Create surface for planting on
|
||||
dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0)
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ def LayerSpawner_FilterStageToggle():
|
||||
# Create a vegetation area with all needed components
|
||||
position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
vegetation_entity = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
|
||||
vegetation_entity = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
|
||||
vegetation_entity.add_component("Vegetation Altitude Filter")
|
||||
vegetation_entity.add_component("Vegetation Position Modifier")
|
||||
|
||||
|
||||
+4
-4
@@ -62,10 +62,10 @@ def LayerSpawner_InstancesPlantInAllSupportedShapes():
|
||||
# 2) Create basic vegetation area entity and set the properties
|
||||
entity_position = math.Vector3(125.0, 136.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
vegetation = dynveg.create_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
vegetation = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
vegetation.remove_component("Box Shape")
|
||||
vegetation.add_component("Shape Reference")
|
||||
|
||||
|
||||
+4
-4
@@ -101,10 +101,10 @@ def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera():
|
||||
|
||||
# Create the two vegetation areas
|
||||
test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
first_veg_entity = dynveg.create_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size,
|
||||
box_size, test_slice_asset_path)
|
||||
second_veg_entity = dynveg.create_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size,
|
||||
box_size, test_slice_asset_path)
|
||||
first_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size,
|
||||
box_size, test_slice_asset_path)
|
||||
second_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size,
|
||||
box_size, test_slice_asset_path)
|
||||
|
||||
# When the first viewport is active, the first area should be full of instances, and the second should be empty
|
||||
general.set_active_viewport(0)
|
||||
|
||||
+4
-4
@@ -59,10 +59,10 @@ def MeshBlocker_InstancesBlockedByMesh():
|
||||
# Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
|
||||
# Create surface entity to plant on
|
||||
dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)
|
||||
|
||||
+4
-4
@@ -62,10 +62,10 @@ def MeshBlocker_InstancesBlockedByMeshHeightTuning():
|
||||
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
|
||||
# 3) Create surface entity to plant on
|
||||
dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ def PhysXColliderSurfaceTagEmitter_E2E_Editor():
|
||||
|
||||
# Create a new entity with required vegetation area components
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path)
|
||||
|
||||
# Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag
|
||||
spawner_entity.add_component("Vegetation Surface Mask Filter")
|
||||
|
||||
+2
-2
@@ -74,8 +74,8 @@ def PositionModifier_AutoSnapToSurfaceWorks():
|
||||
# 2) Create a new entity with required vegetation area components and a Position Modifier
|
||||
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
|
||||
# Add a Vegetation Position Modifier and set offset values to 0
|
||||
spawner_entity.add_component("Vegetation Position Modifier")
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ def PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets():
|
||||
# 2) Create a new entity with required vegetation area components
|
||||
spawner_center_point = math.Vector3(16.0, 16.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path)
|
||||
|
||||
# Add a Vegetation Position Modifier and set offset values to 0
|
||||
spawner_entity.add_component("Vegetation Position Modifier")
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ def RotationModifierOverrides_InstancesRotateWithinRange():
|
||||
# 2) Create vegetation entity and add components
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path)
|
||||
spawner_entity.add_component("Vegetation Rotation Modifier")
|
||||
# Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances.
|
||||
num_expected = 20 * 20
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ def RotationModifier_InstancesRotateWithinRange():
|
||||
|
||||
# 2) Set up vegetation entities
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path)
|
||||
|
||||
additional_components = [
|
||||
"Vegetation Rotation Modifier"
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ def ScaleModifierOverrides_InstancesProperlyScale():
|
||||
# 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path)
|
||||
|
||||
# Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
|
||||
dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0)
|
||||
|
||||
+2
-2
@@ -94,8 +94,8 @@ def ScaleModifier_InstancesProperlyScale():
|
||||
# Vegetation Scale Modifier
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity.add_component("Vegetation Scale Modifier")
|
||||
|
||||
# Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ def ShapeIntersectionFilter_FilterStageToggle():
|
||||
# Create basic vegetation entity
|
||||
position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
|
||||
vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
|
||||
|
||||
# Create Surface for instances to plant on
|
||||
dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ def ShapeIntersectionFilter_InstancesPlantInAssignedShape():
|
||||
# 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0,
|
||||
asset_path)
|
||||
spawner_entity.add_component("Vegetation Shape Intersection Filter")
|
||||
|
||||
# Create a planting surface
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ def SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment():
|
||||
# Create a spawner entity setup with all needed components
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
|
||||
|
||||
# Create a sloped mesh surface for the instances to plant on
|
||||
center_point = math.Vector3(502.0, 512.0, 24.0)
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ def SlopeAlignmentModifier_InstanceSurfaceAlignment():
|
||||
# Create a spawner entity setup with all needed components
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
|
||||
|
||||
# Create a sloped mesh surface for the instances to plant on
|
||||
center_point = math.Vector3(502.0, 512.0, 24.0)
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ def SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes():
|
||||
# 2) Create a new entity with required vegetation area components
|
||||
center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
|
||||
|
||||
# Add a Vegetation Slope Filter
|
||||
spawner_entity.add_component("Vegetation Slope Filter")
|
||||
|
||||
+2
-2
@@ -66,7 +66,7 @@ def SpawnerSlices_SliceCreationAndVisibilityToggleWorks():
|
||||
# 2.1) Create basic vegetation entity
|
||||
position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
veg_1 = dynveg.create_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path)
|
||||
veg_1 = dynveg.create_dynamic_slice_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path)
|
||||
|
||||
# 2.2) Create slice from the entity
|
||||
slice_path = os.path.join("slices", "TestSlice_1.slice")
|
||||
@@ -94,7 +94,7 @@ def SpawnerSlices_SliceCreationAndVisibilityToggleWorks():
|
||||
|
||||
# 4) C2627905 A slice containing the Vegetation Layer Blender component can be created.
|
||||
# 4.1) Create another vegetation entity to add to blender component
|
||||
veg_2 = dynveg.create_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "")
|
||||
veg_2 = dynveg.create_dynamic_slice_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "")
|
||||
|
||||
# 4.2) Create entity with Vegetation Layer Blender
|
||||
components_to_add = ["Box Shape", "Vegetation Layer Blender"]
|
||||
|
||||
+2
-2
@@ -77,8 +77,8 @@ def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected():
|
||||
# 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors
|
||||
spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
|
||||
asset_path)
|
||||
asset_list_component = spawner_entity.components[2]
|
||||
desc_asset = hydra.get_component_property_value(asset_list_component,
|
||||
"Configuration|Embedded Assets")[0]
|
||||
|
||||
+4
-4
@@ -99,10 +99,10 @@ def SurfaceMaskFilter_ExclusionList():
|
||||
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
|
||||
# 3) Add a Vegetation Surface Mask Filter component to the entity.
|
||||
spawner_entity.add_component("Vegetation Surface Mask Filter")
|
||||
|
||||
+4
-4
@@ -100,10 +100,10 @@ def SurfaceMaskFilter_InclusionList():
|
||||
# 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
|
||||
entity_position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
|
||||
entity_position,
|
||||
10.0, 10.0, 10.0,
|
||||
asset_path)
|
||||
|
||||
# 3) Add a Vegetation Surface Mask Filter component to the entity.
|
||||
spawner_entity.add_component("Vegetation Surface Mask Filter")
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ def SystemSettings_SectorPointDensity():
|
||||
# Create basic vegetation entity
|
||||
position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
|
||||
dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
|
||||
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
|
||||
|
||||
# Count the number of vegetation instances in the vegetation area
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ def SystemSettings_SectorSize():
|
||||
# Create basic vegetation entity
|
||||
position = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
|
||||
vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
|
||||
vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
|
||||
dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
|
||||
|
||||
# Add the Vegetation Debugger component to the Level Inspector
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ def VegetationInstances_DespawnWhenOutOfRange():
|
||||
# Create vegetation layer spawner
|
||||
world_center = math.Vector3(512.0, 512.0, 32.0)
|
||||
asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
|
||||
spawner_entity = dynveg.create_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path)
|
||||
spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path)
|
||||
|
||||
# Create a surface to spawn on
|
||||
dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0)
|
||||
|
||||
@@ -16,6 +16,20 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, E
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
|
||||
from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
|
||||
|
||||
class test_AltitudeFilter_FilterStageToggle(EditorParallelTest):
|
||||
from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
|
||||
|
||||
class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
|
||||
from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
|
||||
|
||||
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
class TestAutomation_PrefabNotEnabled(EditorTestSuite):
|
||||
|
||||
enable_prefab_system = False
|
||||
|
||||
@@ -36,19 +50,10 @@ class TestAutomation(EditorTestSuite):
|
||||
class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorParallelTest):
|
||||
from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module
|
||||
|
||||
class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
|
||||
from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
|
||||
|
||||
class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
|
||||
from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
|
||||
|
||||
class test_AltitudeFilter_FilterStageToggle(EditorParallelTest):
|
||||
from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
|
||||
|
||||
class test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(EditorSingleTest):
|
||||
# Custom teardown to remove slice asset created during test
|
||||
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_slices(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_slices(self, workspace)
|
||||
from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module
|
||||
|
||||
class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorParallelTest):
|
||||
@@ -161,27 +166,27 @@ class TestAutomation(EditorTestSuite):
|
||||
|
||||
# Custom setup/teardown to remove test level created during test
|
||||
def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_level(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
|
||||
|
||||
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_level(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
|
||||
|
||||
class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest):
|
||||
from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module
|
||||
|
||||
# Custom setup/teardown to remove test level created during test
|
||||
def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_level(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
|
||||
|
||||
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_level(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
|
||||
|
||||
class test_LayerBlender_E2E_Editor(EditorSingleTest):
|
||||
from .EditorScripts import LayerBlender_E2E_Editor as test_module
|
||||
|
||||
# Custom setup/teardown to remove test level created during test
|
||||
def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_level(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
|
||||
|
||||
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
|
||||
TestAutomation.cleanup_test_level(self, workspace)
|
||||
TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
|
||||
|
||||
@@ -52,15 +52,15 @@ class TestAutomation(TestAutomationBase):
|
||||
|
||||
def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
|
||||
from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
|
||||
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
|
||||
from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
|
||||
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform):
|
||||
from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
|
||||
self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform):
|
||||
from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module
|
||||
|
||||
+40
-4
@@ -8,14 +8,16 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import azlmbr.areasystem as areasystem
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components as components
|
||||
import azlmbr.math as math
|
||||
import azlmbr.vegetation as vegetation
|
||||
import azlmbr.areasystem as areasystem
|
||||
import azlmbr.paths
|
||||
import azlmbr.prefab as prefab
|
||||
import azlmbr.vegetation as vegetation
|
||||
|
||||
sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests'))
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
@@ -91,7 +93,7 @@ def create_mesh_surface_entity_with_slopes(name, center_point, uniform_scale):
|
||||
return surface_entity
|
||||
|
||||
|
||||
def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, dynamic_slice_asset_path):
|
||||
def create_dynamic_slice_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, dynamic_slice_asset_path):
|
||||
# Create a vegetation area entity to use as our test vegetation spawner
|
||||
spawner_entity = hydra.Entity(name)
|
||||
spawner_entity.create_entity(
|
||||
@@ -104,14 +106,48 @@ def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_
|
||||
box_size_z))
|
||||
|
||||
# Set the vegetation area to a Dynamic Slice spawner with a specific slice asset selected
|
||||
descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
|
||||
dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner()
|
||||
dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path)
|
||||
descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
|
||||
descriptor.spawner = dynamic_slice_spawner
|
||||
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
|
||||
return spawner_entity
|
||||
|
||||
|
||||
def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, target_prefab):
|
||||
# Create a vegetation area entity to use as our test vegetation spawner
|
||||
spawner_entity = hydra.Entity(name)
|
||||
spawner_entity.create_entity(
|
||||
center_point,
|
||||
["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
|
||||
)
|
||||
if spawner_entity.id.IsValid():
|
||||
print(f"'{spawner_entity.name}' created")
|
||||
spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(box_size_x, box_size_y,
|
||||
box_size_z))
|
||||
# Get the in-memory spawnable asset id if exists
|
||||
spawnable_name = Path(target_prefab.file_path).stem
|
||||
spawnable_asset_id = prefab.PrefabPublicRequestBus(bus.Broadcast, 'GetInMemorySpawnableAssetId',
|
||||
spawnable_name)
|
||||
|
||||
# Create the in-memory spawnable asset from given prefab if the spawnable does not exist
|
||||
if not spawnable_asset_id.is_valid():
|
||||
create_spawnable_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreateInMemorySpawnableAsset',
|
||||
target_prefab.file_path,
|
||||
spawnable_name)
|
||||
assert create_spawnable_result.IsSuccess(), \
|
||||
f"Prefab operation 'CreateInMemorySpawnableAssets' failed. Error: {create_spawnable_result.GetError()}"
|
||||
spawnable_asset_id = create_spawnable_result.GetValue()
|
||||
|
||||
# Set the vegetation area to a prefab instance spawner with a specific prefab asset selected
|
||||
descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
|
||||
prefab_spawner = vegetation.PrefabInstanceSpawner()
|
||||
prefab_spawner.SetPrefabAssetId(spawnable_asset_id)
|
||||
descriptor.spawner = prefab_spawner
|
||||
spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
|
||||
return spawner_entity
|
||||
|
||||
|
||||
def create_blocker_area(name, center_point, box_size_x, box_size_y, box_size_z):
|
||||
# Create a Vegetation Layer Blocker area
|
||||
blocker_entity = hydra.Entity(name)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/.maya_data/*
|
||||
/.mayaSwatches/*
|
||||
*.swatch
|
||||
[Uu]ser_env.bat
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:57848334af0220b7348a8f2583080acf1d9c139a78c9fb1a93a7d2bce61f3c40
|
||||
size 41335413
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9b936b72b5b45b52c188bf9f930d6066af65f0d79ff8abf5dc08927d97ac465e
|
||||
size 55264
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"emissive": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"emissive": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1.0
|
||||
],
|
||||
"textureMap": "Textures/arch_1k_basecolor.png"
|
||||
},
|
||||
"general": {
|
||||
"applySpecularAA": true
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
1.0,
|
||||
0.885053813457489,
|
||||
0.801281750202179,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"metallic": {
|
||||
"textureMap": "Textures/arch_1k_metallic.png"
|
||||
},
|
||||
"normal": {
|
||||
"textureMap": "Textures/arch_1k_normal.jpg"
|
||||
},
|
||||
"occlusion": {
|
||||
"diffuseTextureMap": "Textures/arch_1k_ao.png"
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
},
|
||||
"parallax": {
|
||||
"factor": 0.050999999046325687,
|
||||
"pdo": true,
|
||||
"quality": "High",
|
||||
"useTexture": false
|
||||
},
|
||||
"roughness": {
|
||||
"textureMap": "Textures/arch_1k_roughness.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1.0
|
||||
],
|
||||
"textureMap": "Textures/bricks_1k_basecolor.png"
|
||||
},
|
||||
"clearCoat": {
|
||||
"factor": 0.5,
|
||||
"normalMap": "Textures/bricks_1k_normal.jpg",
|
||||
"roughness": 0.5
|
||||
},
|
||||
"general": {
|
||||
"applySpecularAA": true
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
1.0,
|
||||
0.9703211784362793,
|
||||
0.9703211784362793,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"metallic": {
|
||||
"textureMap": "Textures/bricks_1k_metallic.png"
|
||||
},
|
||||
"normal": {
|
||||
"textureMap": "Textures/bricks_1k_normal.jpg"
|
||||
},
|
||||
"occlusion": {
|
||||
"diffuseTextureMap": "Textures/bricks_1k_ao.png"
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
},
|
||||
"parallax": {
|
||||
"algorithm": "ContactRefinement",
|
||||
"factor": 0.03500000014901161,
|
||||
"quality": "Medium",
|
||||
"useTexture": false
|
||||
},
|
||||
"roughness": {
|
||||
"textureMap": "Textures/bricks_1k_roughness.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1.0
|
||||
],
|
||||
"textureMap": "Textures/floor_1k_basecolor.png"
|
||||
},
|
||||
"clearCoat": {
|
||||
"enable": true,
|
||||
"influenceMap": "Textures/floor_1k_ao.png",
|
||||
"normalMap": "Textures/floor_1k_normal.png",
|
||||
"roughness": 0.25
|
||||
},
|
||||
"general": {
|
||||
"applySpecularAA": true
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
1.0,
|
||||
0.9404135346412659,
|
||||
0.8688944578170776,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"normal": {
|
||||
"textureMap": "Textures/floor_1k_normal.png"
|
||||
},
|
||||
"occlusion": {
|
||||
"diffuseTextureMap": "Textures/floor_1k_ao.png"
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
},
|
||||
"parallax": {
|
||||
"factor": 0.012000000104308129,
|
||||
"pdo": true,
|
||||
"useTexture": false
|
||||
},
|
||||
"roughness": {
|
||||
"textureMap": "Textures/floor_1k_roughness.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1.0
|
||||
],
|
||||
"textureBlendMode": "Lerp",
|
||||
"textureMap": "Textures/roof_1k_basecolor.png"
|
||||
},
|
||||
"general": {
|
||||
"applySpecularAA": true
|
||||
},
|
||||
"metallic": {
|
||||
"useTexture": false
|
||||
},
|
||||
"normal": {
|
||||
"factor": 0.5,
|
||||
"flipY": true,
|
||||
"textureMap": "Textures/roof_1k_normal.jpg"
|
||||
},
|
||||
"occlusion": {
|
||||
"diffuseTextureMap": "Textures/roof_1k_ao.png"
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
},
|
||||
"parallax": {
|
||||
"algorithm": "ContactRefinement",
|
||||
"factor": 0.019999999552965165,
|
||||
"quality": "Medium",
|
||||
"useTexture": false
|
||||
},
|
||||
"roughness": {
|
||||
"textureMap": "Textures/roof_1k_roughness.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"emissive": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.800000011920929,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"emissive": {
|
||||
"color": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"irradiance": {
|
||||
"color": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"opacity": {
|
||||
"factor": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user