adding the testing files for testing for python asset building and scripting

the scene_api gets a small update for mesh_group_add_advanced_coordinate_system(self,
main
jackalbe 5 years ago
parent 0997a2cfbf
commit f0cf27b8d3

@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""

@ -0,0 +1,13 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../PythonTests')
from PythonAssetBuilder import bootstrap_tests

@ -0,0 +1,57 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
#
# This launches the AssetProcessor and Editor then attempts to find the expected
# assets created by a Python Asset Builder and the output of a scene pipeline script
#
import sys
import os
import pytest
import logging
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import ly_test_tools.log.log_monitor
import ly_test_tools.environment.waiter as waiter
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize('launcher_platform', ['windows_editor'])
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['auto_test'])
class TestPythonAssetProcessing(object):
def test_DetectPythonCreatedAsset(self, request, editor, level, launcher_platform):
unexpected_lines = []
expected_lines = [
'Mock asset exists',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_negative.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_positive.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found'
]
timeout = 180
halt_on_unexpected = False
test_directory = os.path.join(os.path.dirname(__file__))
testFile = os.path.join(test_directory, 'AssetBuilder_test_case.py')
editor.args.extend(['-NullRenderer', "--skipWelcomeScreenDialog", "--autotest_mode", "--runpythontest", testFile])
with editor.start():
editorlog_file = os.path.join(editor.workspace.paths.project_log(), 'Editor.log')
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(editor, editorlog_file)
waiter.wait_for(
lambda: editor.is_alive(),
timeout,
exc=("Log file '{}' was never opened by another process.".format(editorlog_file)),
interval=1)
log_monitor.monitor_log_for_lines(expected_lines, unexpected_lines, halt_on_unexpected, timeout)

@ -0,0 +1,52 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import azlmbr.bus
import azlmbr.asset
import azlmbr.editor
import azlmbr.math
import azlmbr.legacy.general
def raise_and_stop(msg):
print (msg)
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
# These tests are meant to check that the test_asset.mock source asset turned into
# a test_asset.mock_asset product asset via the Python asset builder system
mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
mockAssetPath = 'gem/pythontests/pythonassetbuilder/test_asset.mock_asset'
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', mockAssetPath, mockAssetType, False)
if (assetId.is_valid() is False):
raise_and_stop(f'Mock AssetId is not valid!')
if (assetId.to_string().endswith(':54c06b89') is False):
raise_and_stop(f'Mock AssetId has unexpected sub-id for {mockAssetPath}!')
print ('Mock asset exists')
# These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
def test_azmodel_product(generatedModelAssetPath, expectedSubId):
azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
assetIdString = assetId.to_string()
if (assetIdString.endswith(':' + expectedSubId) is False):
raise_and_stop(f'Asset has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath})!')
else:
print(f'Expected subId for asset ({generatedModelAssetPath}) found')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel', '10412075')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_positive.azmodel', '10d16e68')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_X_negative.azmodel', '10a71973')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Y_positive.azmodel', '10130556')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Y_negative.azmodel', '1065724d')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Z_positive.azmodel', '1024be55')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_Z_negative.azmodel', '1052c94e')
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')

@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""

@ -0,0 +1,17 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import os
import sys
try:
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import mock_asset_builder
except:
print ('skipping asset builder testing via mock_asset_builder')

@ -0,0 +1,88 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import uuid, os
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
def get_mesh_node_names(sceneGraph):
meshDataList = []
node = sceneGraph.get_root()
children = []
while node.IsValid():
# store children to process after siblings
if sceneGraph.has_node_child(node):
children.append(sceneGraph.get_node_child(node))
# store any node that has mesh data content
nodeContent = sceneGraph.get_node_content(node)
if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'):
if sceneGraph.is_node_end_point(node) is False:
meshDataList.append(sceneData.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 meshDataList
def update_manifest(scene):
graph = sceneData.SceneGraph(scene.graph)
meshNameList = get_mesh_node_names(graph)
sceneManifest = sceneData.SceneManifest()
sourceFilenameOnly = os.path.basename(scene.sourceFilename)
sourceFilenameOnly = sourceFilenameOnly.replace('.','_')
for activeMeshIndex in range(len(meshNameList)):
chunkName = meshNameList[activeMeshIndex]
chunkPath = chunkName.get_path()
meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name())
meshGroup = sceneManifest.add_mesh_group(meshGroupName)
meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}'
sceneManifest.mesh_group_add_comment(meshGroup, 'auto generated by scene manifest')
sceneManifest.mesh_group_add_advanced_coordinate_system(meshGroup, None, None, None, 1.0)
# create selection node list
pathSet = set()
for meshIndex in range(len(meshNameList)):
targetPath = meshNameList[meshIndex].get_path()
if (activeMeshIndex == meshIndex):
sceneManifest.mesh_group_select_node(meshGroup, targetPath)
else:
if targetPath not in pathSet:
pathSet.update(targetPath)
sceneManifest.mesh_group_unselect_node(meshGroup, targetPath)
return sceneManifest.export()
mySceneJobHandler = None
def on_update_manifest(args):
scene = args[0]
result = update_manifest(scene)
global mySceneJobHandler
mySceneJobHandler.disconnect()
mySceneJobHandler = None
return result
def main():
global mySceneJobHandler
mySceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
mySceneJobHandler.connect()
mySceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
if __name__ == "__main__":
main()

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66d38948309ef273adf74b63eaa38f8fc2e2bdfbab3933d2ee082ce6a8cb108e
size 30496

@ -0,0 +1,9 @@
{
"values":
[
{
"$type": "ScriptProcessorRule",
"scriptFilename": "Gem/PythonTests/PythonAssetBuilder/export_chunks_builder.py"
}
]
}

@ -0,0 +1,121 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import azlmbr.asset
import azlmbr.asset.builder
import azlmbr.bus
import azlmbr.math
import os, traceback, binascii, sys
jobKeyName = 'Mock Asset'
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))
# creates a single job to compile for each platform
def create_jobs(request):
# create job descriptor for each platform
jobDescriptorList = []
for platformInfo in request.enabledPlatforms:
jobDesc = azlmbr.asset.builder.JobDescriptor()
jobDesc.jobKey = jobKeyName
jobDesc.set_platform_identifier(platformInfo.identifier)
jobDescriptorList.append(jobDesc)
response = azlmbr.asset.builder.CreateJobsResponse()
response.result = azlmbr.asset.builder.CreateJobsResponse_ResultSuccess
response.createJobOutputs = jobDescriptorList
return response
def on_create_jobs(args):
try:
request = args[0]
return create_jobs(request)
except:
log_exception_traceback()
# returing back a default CreateJobsResponse() records an asset error
return azlmbr.asset.builder.CreateJobsResponse()
def process_file(request):
# prepare output folder
basePath, _ = os.path.split(request.sourceFile)
outputPath = os.path.join(request.tempDirPath, basePath)
os.makedirs(outputPath, exist_ok=True)
# write out a mock file
basePath, sourceFile = os.path.split(request.sourceFile)
mockFilename = os.path.splitext(sourceFile)[0] + '.mock_asset'
mockFilename = os.path.join(basePath, mockFilename)
mockFilename = mockFilename.replace('\\', '/').lower()
tempFilename = os.path.join(request.tempDirPath, mockFilename)
# write out a tempFilename like a JSON or something?
fileOutput = open(tempFilename, "w")
fileOutput.write('{}')
fileOutput.close()
# generate a product asset file entry
subId = binascii.crc32(mockFilename.encode())
mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
product = azlmbr.asset.builder.JobProduct(mockFilename, mockAssetType, subId)
product.dependenciesHandled = True
productOutputs = []
productOutputs.append(product)
# fill out response object
response = azlmbr.asset.builder.ProcessJobResponse()
response.outputProducts = productOutputs
response.resultCode = azlmbr.asset.builder.ProcessJobResponse_Success
response.dependenciesHandled = True
return response
# using the incoming 'request' find the type of job via 'jobKey' to determine what to do
def on_process_job(args):
try:
request = args[0]
if (request.jobDescription.jobKey.startswith(jobKeyName)):
return process_file(request)
except:
log_exception_traceback()
# returning back an empty ProcessJobResponse() will record an error
return azlmbr.asset.builder.ProcessJobResponse()
# register asset builder
def register_asset_builder(busId):
assetPattern = azlmbr.asset.builder.AssetBuilderPattern()
assetPattern.pattern = '*.mock'
assetPattern.type = azlmbr.asset.builder.AssetBuilderPattern_Wildcard
builderDescriptor = azlmbr.asset.builder.AssetBuilderDesc()
builderDescriptor.name = "Mock Builder"
builderDescriptor.patterns = [assetPattern]
builderDescriptor.busId = busId
builderDescriptor.version = 1
outcome = azlmbr.asset.builder.PythonAssetBuilderRequestBus(azlmbr.bus.Broadcast, 'RegisterAssetBuilder', builderDescriptor)
if outcome.IsSuccess():
# created the asset builder to hook into the notification bus
handler = azlmbr.asset.builder.PythonBuilderNotificationBusHandler()
handler.connect(busId)
handler.add_callback('OnCreateJobsRequest', on_create_jobs)
handler.add_callback('OnProcessJobRequest', on_process_job)
return handler
# create the asset builder handler
busIdString = '{CF5C74C1-9ED4-5851-95B1-0B15090DBEC7}'
busId = azlmbr.math.Uuid_CreateString(busIdString, 0)
handler = None
try:
handler = register_asset_builder(busId)
except:
handler = None
log_exception_traceback()

@ -114,12 +114,17 @@ class SceneManifest():
def mesh_group_unselect_node(self, meshGroup, nodeName): def mesh_group_unselect_node(self, meshGroup, nodeName):
meshGroup['nodeSelectionList']['unselectedNodes'].append(nodeName) meshGroup['nodeSelectionList']['unselectedNodes'].append(nodeName)
def mesh_group_set_origin(self, meshGroup, originNodeName, x, y, z, scale): def mesh_group_add_advanced_coordinate_system(self, meshGroup, originNodeName, translation, rotation, scale):
originRule = {} originRule = {}
originRule['$type'] = 'OriginRule' originRule['$type'] = 'CoordinateSystemRule'
originRule['originNodeName'] = 'World' if originNodeName is None else originNodeName originRule['useAdvancedData'] = True
originRule['translation'] = [x, y, z] originRule['originNodeName'] = '' if originNodeName is None else originNodeName
originRule['scale'] = scale if translation is not None:
originRule['translation'] = translation
if rotation is not None:
originRule['rotation'] = rotation
if scale != 1.0:
originRule['scale'] = scale
meshGroup['rules']['rules'].append(originRule) meshGroup['rules']['rules'].append(originRule)
def mesh_group_add_comment(self, meshGroup, comment): def mesh_group_add_comment(self, meshGroup, comment):

Loading…
Cancel
Save