diff --git a/Assets/Editor/Prefabs/Default_Level.prefab b/Assets/Editor/Prefabs/Default_Level.prefab index d02d669f53..0267131fa0 100644 --- a/Assets/Editor/Prefabs/Default_Level.prefab +++ b/Assets/Editor/Prefabs/Default_Level.prefab @@ -185,7 +185,7 @@ { "id": { "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -197,7 +197,7 @@ "id": { "lodIndex": 0, "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } diff --git a/Assets/Editor/Scripts/lua_symbols.py b/Assets/Editor/Scripts/lua_symbols.py new file mode 100644 index 0000000000..b21edb41d0 --- /dev/null +++ b/Assets/Editor/Scripts/lua_symbols.py @@ -0,0 +1,116 @@ +# +# 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 +# +# + + +# This script shows basic usage of LuaSymbolsReporterBus, +# Which can be used to report all symbols available for +# game scripting with Lua. + +import sys +import os + +import azlmbr.bus as azbus +import azlmbr.script as azscript +import azlmbr.legacy.general as azgeneral + + +def _dump_class_symbol(class_symbol: azlmbr.script.LuaClassSymbol): + print(f"** {class_symbol}") + print("Properties:") + for property_symbol in class_symbol.properties: + print(f" - {property_symbol}") + print("Methods:") + for method_symbol in class_symbol.methods: + print(f" - {method_symbol}") + + +def _dump_lua_classes(): + class_symbols = azscript.LuaSymbolsReporterBus(azbus.Broadcast, + "GetListOfClasses") + print("======== Classes ==========") + sorted_classes_by_named = sorted(class_symbols, key=lambda class_symbol: class_symbol.name) + for class_symbol in sorted_classes_by_named: + _dump_class_symbol(class_symbol) + print("\n\n") + + +def _dump_lua_globals(): + global_properties = azscript.LuaSymbolsReporterBus(azbus.Broadcast, + "GetListOfGlobalProperties") + print("======== Global Properties ==========") + sorted_properties_by_name = sorted(global_properties, key=lambda symbol: symbol.name) + for property_symbol in sorted_properties_by_name: + print(f"- {property_symbol}") + print("\n\n") + global_functions = azscript.LuaSymbolsReporterBus(azbus.Broadcast, + "GetListOfGlobalFunctions") + print("======== Global Functions ==========") + sorted_functions_by_name = sorted(global_functions, key=lambda symbol: symbol.name) + for function_symbol in sorted_functions_by_name: + print(f"- {function_symbol}") + print("\n\n") + + +def _dump_lua_ebus(ebus_symbol: azlmbr.script.LuaEBusSymbol): + print(f">> {ebus_symbol}") + sorted_senders = sorted(ebus_symbol.senders, key=lambda symbol: symbol.name) + for sender in sorted_senders: + print(f" - {sender}") + print("\n") + + +def _dump_lua_ebuses(): + ebuses = azscript.LuaSymbolsReporterBus(azbus.Broadcast, + "GetListOfEBuses") + print("======== Ebus List ==========") + sorted_ebuses_by_name = sorted(ebuses, key=lambda symbol: symbol.name) + for ebus_symbol in sorted_ebuses_by_name: + _dump_lua_ebus(ebus_symbol) + print("\n\n") + + +class WhatToDo: + DumpClasses = "c" + DumpGlobals = "g" + DumpEBuses = "e" + +if __name__ == "__main__": + redirecting_stdout = False + orig_stdout = sys.stdout + if len(sys.argv) > 1: + output_file_name = sys.argv[1] + if not os.path.isabs(output_file_name): + game_root_path = os.path.normpath(azgeneral.get_game_folder()) + output_file_name = os.path.join(game_root_path, output_file_name) + try: + file_obj = open(output_file_name, 'wt') + sys.stdout = file_obj + redirecting_stdout = True + except Exception as e: + print(f"Failed to open {output_file_name}: {e}") + sys.exit(-1) + + what_to_do = [action.lower() for action in sys.argv[2:]] + + # If the user did not specify what to do, then let's dump + # all the symbols. + if len(what_to_do) < 1: + what_to_do = [WhatToDo.DumpClasses, WhatToDo.DumpGlobals, WhatToDo.DumpEBuses] + + for action in what_to_do: + if action == WhatToDo.DumpClasses: + _dump_lua_classes() + elif action == WhatToDo.DumpGlobals: + _dump_lua_globals() + elif action == WhatToDo.DumpEBuses: + _dump_lua_ebuses() + + if redirecting_stdout: + sys.stdout.close() + sys.stdout = orig_stdout + print(f" Lua Symbols Are available in: {output_file_name}") diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png new file mode 100644 index 0000000000..415ca3e521 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/_dev_shaderball_00_basecolor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93a7e033d9fb0fcac221647322bde03716643d789390f79078c4fcc37ecfd005 +size 68327 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg new file mode 100644 index 0000000000..7d070fcea0 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.dbgsg @@ -0,0 +1,3111 @@ +ProductName: shaderball.dbgsg +debugSceneGraphVersion: 1 +shaderball +Node Name: RootNode +Node Path: RootNode +Node Type: RootBoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, 1.000000, 0.000000> + BasisZ: < 0.000000, 0.000000, 1.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: ShaderBall_1m +Node Path: RootNode.ShaderBall_1m +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: transform +Node Path: RootNode.ShaderBall_1m.transform +Node Type: TransformData + Matrix: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerPortion +Node Path: RootNode.ShaderBall_1m.InnerPortion +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: MaterialBase +Node Path: RootNode.ShaderBall_1m.MaterialBase +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InlayRings +Node Path: RootNode.ShaderBall_1m.InlayRings +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: MainSphere +Node Path: RootNode.ShaderBall_1m.MainSphere +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: HubCap_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1 +Node Type: MeshData + Positions: Count 20476. Hash: 5411361036988924549 + Normals: Count 20476. Hash: 5915154682063029054 + FaceList: Count 8676. Hash: 9142863582186575896 + FaceMaterialIds: Count 8676. Hash: 723360536895379791 + +Node Name: HubCap_2 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: HubCap_1_optimized +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized +Node Type: MeshData + Positions: Count 4724. Hash: 448283466401665158 + Normals: Count 4724. Hash: 345267294337234954 + FaceList: Count 8676. Hash: 11155608373229651496 + FaceMaterialIds: Count 8676. Hash: 723360536895379791 + +Node Name: InnerSphere_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1 +Node Type: MeshData + Positions: Count 19800. Hash: 17342106809405761922 + Normals: Count 19800. Hash: 602384960091561079 + FaceList: Count 9800. Hash: 15975352410309879244 + FaceMaterialIds: Count 9800. Hash: 2219364576630417284 + +Node Name: InnerSphere_2 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerSphere_1_optimized +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized +Node Type: MeshData + Positions: Count 5846. Hash: 18229431498904963984 + Normals: Count 5846. Hash: 12980164457801827192 + FaceList: Count 9800. Hash: 2914108932212582430 + FaceMaterialIds: Count 9800. Hash: 2219364576630417284 + +Node Name: InnerCone_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1 +Node Type: MeshData + Positions: Count 192. Hash: 3022774266117638288 + Normals: Count 192. Hash: 12863565187316537150 + FaceList: Count 64. Hash: 1255131899577053537 + FaceMaterialIds: Count 64. Hash: 6312841653578246165 + +Node Name: InnerCone_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerCone_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized +Node Type: MeshData + Positions: Count 65. Hash: 832325255726840940 + Normals: Count 65. Hash: 14121159448916141450 + FaceList: Count 64. Hash: 9660474080562375138 + FaceMaterialIds: Count 64. Hash: 6312841653578246165 + +Node Name: InnerPost_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1 +Node Type: MeshData + Positions: Count 4864. Hash: 7530018531436061848 + Normals: Count 4864. Hash: 534420091635424803 + FaceList: Count 2432. Hash: 4330575616942580147 + FaceMaterialIds: Count 2432. Hash: 12393250111858627709 + +Node Name: InnerPost_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerPost_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized +Node Type: MeshData + Positions: Count 1300. Hash: 15277132209442728123 + Normals: Count 1300. Hash: 1247055552073340932 + FaceList: Count 2432. Hash: 16808521883586752319 + FaceMaterialIds: Count 2432. Hash: 12393250111858627709 + +Node Name: InnerBaseCuff_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1 +Node Type: MeshData + Positions: Count 4096. Hash: 1814004361979755447 + Normals: Count 4096. Hash: 1760582409511017750 + FaceList: Count 2048. Hash: 6642755656211284824 + FaceMaterialIds: Count 2048. Hash: 2795877824940392899 + +Node Name: InnerBaseCuff_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerBaseCuff_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized +Node Type: MeshData + Positions: Count 1122. Hash: 15009434720129565864 + Normals: Count 1122. Hash: 7245598986723209052 + FaceList: Count 2048. Hash: 2887888015166354330 + FaceMaterialIds: Count 2048. Hash: 2795877824940392899 + +Node Name: Inset_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1 +Node Type: MeshData + Positions: Count 1536. Hash: 6247721054948161820 + Normals: Count 1536. Hash: 16362051041722971623 + FaceList: Count 768. Hash: 3142075668387852387 + FaceMaterialIds: Count 768. Hash: 2979441869813298271 + +Node Name: Inset_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Inset_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized +Node Type: MeshData + Positions: Count 448. Hash: 9520190263881589378 + Normals: Count 448. Hash: 8658432920439039136 + FaceList: Count 768. Hash: 8621713142583851338 + FaceMaterialIds: Count 768. Hash: 2979441869813298271 + +Node Name: BottomCap_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1 +Node Type: MeshData + Positions: Count 960. Hash: 13502046855286963834 + Normals: Count 960. Hash: 7165134632415312413 + FaceList: Count 448. Hash: 11458684524699877690 + FaceMaterialIds: Count 448. Hash: 15444662993354423801 + +Node Name: BottomCap_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: BottomCap_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized +Node Type: MeshData + Positions: Count 257. Hash: 6437758550313952689 + Normals: Count 257. Hash: 9382966961287919705 + FaceList: Count 448. Hash: 16093592375171717669 + FaceMaterialIds: Count 448. Hash: 15444662993354423801 + +Node Name: InnerCushion_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1 +Node Type: MeshData + Positions: Count 54208. Hash: 17548679297809443982 + Normals: Count 54208. Hash: 18279651495381460361 + FaceList: Count 27104. Hash: 12714006452420198903 + FaceMaterialIds: Count 27104. Hash: 12674758055018775830 + +Node Name: InnerCushion_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: InnerCushion_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized +Node Type: MeshData + Positions: Count 13860. Hash: 15965614532178328362 + Normals: Count 13860. Hash: 7297376140184766040 + FaceList: Count 27104. Hash: 2523751700832022798 + FaceMaterialIds: Count 27104. Hash: 12674758055018775830 + +Node Name: OuterBaseCuff_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1 +Node Type: MeshData + Positions: Count 20048. Hash: 3861768905934340307 + Normals: Count 20048. Hash: 12292053423401502106 + FaceList: Count 10024. Hash: 4821138438569111218 + FaceMaterialIds: Count 10024. Hash: 9849297365743356453 + +Node Name: OuterBaseCuff_2 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: OuterBaseCuff_1_optimized +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized +Node Type: MeshData + Positions: Count 5206. Hash: 5201932106801476439 + Normals: Count 5206. Hash: 15696187265416627056 + FaceList: Count 10024. Hash: 10907381836011223214 + FaceMaterialIds: Count 10024. Hash: 9849297365743356453 + +Node Name: RingLeft_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1 +Node Type: MeshData + Positions: Count 2560. Hash: 10944366330558725569 + Normals: Count 2560. Hash: 11471590896496428199 + FaceList: Count 1280. Hash: 2548580276766813978 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RingLeft_2 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: RingLeft_1_optimized +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized +Node Type: MeshData + Positions: Count 720. Hash: 14374869925777029719 + Normals: Count 720. Hash: 2252820527750115179 + FaceList: Count 1280. Hash: 17583744445000895264 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RingRight_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1 +Node Type: MeshData + Positions: Count 2560. Hash: 17639843025153488175 + Normals: Count 2560. Hash: 8564843488923790338 + FaceList: Count 1280. Hash: 2548580276766813978 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RingRight_2 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: RingRight_1_optimized +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized +Node Type: MeshData + Positions: Count 720. Hash: 17033597990859129189 + Normals: Count 720. Hash: 14263134585377771060 + FaceList: Count 1280. Hash: 13824345071081010014 + FaceMaterialIds: Count 1280. Hash: 6371267399123018661 + +Node Name: RightHub_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1 +Node Type: MeshData + Positions: Count 6304. Hash: 11021385291123971383 + Normals: Count 6304. Hash: 10267084099841111837 + FaceList: Count 3152. Hash: 17728162485525521181 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: RightHub_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: RightHub_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized +Node Type: MeshData + Positions: Count 1617. Hash: 9820946434895369083 + Normals: Count 1617. Hash: 16951123277321747448 + FaceList: Count 3152. Hash: 15437295155178226041 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: LeftHub_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1 +Node Type: MeshData + Positions: Count 6304. Hash: 7564731352768958355 + Normals: Count 6304. Hash: 8133714814490222392 + FaceList: Count 3152. Hash: 17728162485525521181 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: LeftHub_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: LeftHub_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized +Node Type: MeshData + Positions: Count 1617. Hash: 8811470420139443913 + Normals: Count 1617. Hash: 11224729257132823435 + FaceList: Count 3152. Hash: 16696390019846851737 + FaceMaterialIds: Count 3152. Hash: 17405713692885844041 + +Node Name: Inside_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1 +Node Type: MeshData + Positions: Count 3520. Hash: 13476777876937219698 + Normals: Count 3520. Hash: 10561277746451021236 + FaceList: Count 1760. Hash: 1345243954764462275 + FaceMaterialIds: Count 1760. Hash: 3100204266221257056 + +Node Name: Inside_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: Inside_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized +Node Type: MeshData + Positions: Count 960. Hash: 1074011803485396393 + Normals: Count 960. Hash: 16318911598614464642 + FaceList: Count 1760. Hash: 3382287220404120115 + FaceMaterialIds: Count 1760. Hash: 3100204266221257056 + +Node Name: MainOuterSphere_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1 +Node Type: MeshData + Positions: Count 18912. Hash: 15956257973657753552 + Normals: Count 18912. Hash: 9328348641512406334 + FaceList: Count 9456. Hash: 9836933646038198686 + FaceMaterialIds: Count 9456. Hash: 13982281543095132650 + +Node Name: MainOuterSphere_2 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2 +Node Type: BoneData + WorldTransform: + BasisX: < 1.000000, 0.000000, 0.000000> + BasisY: < 0.000000, -0.000000, 1.000000> + BasisZ: < 0.000000, -1.000000, -0.000000> + Transl: < 0.000000, 0.000000, 0.000000> + +Node Name: MainOuterSphere_1_optimized +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized +Node Type: MeshData + Positions: Count 4891. Hash: 9579089515723090907 + Normals: Count 4891. Hash: 10089610259011330329 + FaceList: Count 9456. Hash: 13541126452398082145 + FaceMaterialIds: Count 9456. Hash: 13982281543095132650 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 10688945422788452939 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 1387390223232454000 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 20476. Hash: 4424021631256816544 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 20476. Hash: 9768152532901400557 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 20476. Hash: 4890305528292926235 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 20476. Hash: 309820643999247955 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 10688945422788452939 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20476. Hash: 1387390223232454000 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 4724. Hash: 10265340188340999982 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4724. Hash: 6393506322209434620 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4724. Hash: 7072046838448900487 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4724. Hash: 14469479861311642848 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4724. Hash: 14586570136206675867 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4724. Hash: 1080930468361041453 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.HubCap.HubCap_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 9998270082112342253 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 715698668253946311 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 19800. Hash: 9689144294054390217 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 19800. Hash: 13129471596255615133 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 19800. Hash: 12915864712175384367 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 19800. Hash: 704744783983559605 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 9998270082112342253 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 19800. Hash: 715698668253946311 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 5846. Hash: 2128276120164603588 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 5846. Hash: 954788723450394678 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 5846. Hash: 14309642535096835998 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 5846. Hash: 2996000735336843208 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 5846. Hash: 6423899825309547347 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 5846. Hash: 6861847030641362531 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InnerPortion.InnerSphere.InnerSphere_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 10645867109602892598 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 7257961874201179082 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 192. Hash: 12720324392877726426 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 192. Hash: 7937958557505694755 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 192. Hash: 15065829696130008213 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 192. Hash: 14378088137727336097 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 10645867109602892598 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 192. Hash: 7257961874201179082 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 65. Hash: 3145658351065228323 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 65. Hash: 13102825703658386866 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 65. Hash: 14651886668877289638 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 65. Hash: 7706988068999921308 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 65. Hash: 1183045102730537867 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 65. Hash: 15200278891890596008 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCone.InnerCone_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 5283498994389857134 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 4759806696539235318 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4864. Hash: 1916980755154570809 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4864. Hash: 3641075817419129841 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4864. Hash: 1597884606887295389 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4864. Hash: 12470368568863176335 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 5283498994389857134 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4864. Hash: 4759806696539235318 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1300. Hash: 519927422840758162 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1300. Hash: 16690819534142395524 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1300. Hash: 13697492049473980098 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1300. Hash: 16069718749273082363 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1300. Hash: 9503213552298852479 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1300. Hash: 14920629477034919393 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerPost.InnerPost_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 12344372623177285558 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 3415525735687273566 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4096. Hash: 1937219152553164558 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4096. Hash: 16718347243549159919 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4096. Hash: 7775661729866538946 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4096. Hash: 16465645522600859391 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 12344372623177285558 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4096. Hash: 3415525735687273566 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1122. Hash: 10794215007683911939 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1122. Hash: 4030215540982392192 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1122. Hash: 14289595630739500233 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1122. Hash: 13010448485976282215 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1122. Hash: 8318401526835048407 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1122. Hash: 100095329364523248 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerBaseCuff.InnerBaseCuff_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 7123339701675171032 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 15827204344457762670 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1536. Hash: 15732245908985477324 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1536. Hash: 865397990859725823 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1536. Hash: 2552281640891423471 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1536. Hash: 17779645716491562667 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 7123339701675171032 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1536. Hash: 15827204344457762670 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 448. Hash: 11533511688039530581 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 448. Hash: 6463578982826635244 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 448. Hash: 10590255987412720146 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 448. Hash: 17744412650281038495 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 448. Hash: 8278337182397882868 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 448. Hash: 8818080862566813062 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.Inset.Inset_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 12059380739436291361 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 17894062399627363441 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 3025207993250150716 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 909667023812047269 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 9807712812840041710 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 15826838159231044203 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 12059380739436291361 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 17894062399627363441 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 257. Hash: 8823641736072761245 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 257. Hash: 17988941723121644388 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 257. Hash: 8180735114915510878 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 257. Hash: 6608315278879931556 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 257. Hash: 15606348252975307518 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 257. Hash: 9909736394462106525 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.BottomCap.BottomCap_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 3444203649101035485 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 937532470362399061 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 54208. Hash: 196567085853229565 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 54208. Hash: 1716600532837684655 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 54208. Hash: 2905235470236164097 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 54208. Hash: 2074363216216487237 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 3444203649101035485 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 54208. Hash: 937532470362399061 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 13860. Hash: 263381874971540959 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 13860. Hash: 15191673020616208011 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 13860. Hash: 7105458185902008309 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 13860. Hash: 16259705089531364237 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 13860. Hash: 16022317673583270139 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 13860. Hash: 7251784463761381809 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.InnerCushion.InnerCushion_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 10464397683020008867 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 11067273583889078226 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 20048. Hash: 15901168792190323178 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 20048. Hash: 552570814640404138 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 20048. Hash: 17726428588184726937 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 20048. Hash: 7508591493894188819 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 10464397683020008867 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 20048. Hash: 11067273583889078226 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 5206. Hash: 1074257132915638034 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 5206. Hash: 2243653809093009497 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 5206. Hash: 11559777087289074275 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 5206. Hash: 15450183130901763011 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 5206. Hash: 15359221084106995089 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 5206. Hash: 8652443944286435468 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MaterialBase.OuterBaseCuff.OuterBaseCuff_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 14931201357194905697 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5600314145323623005 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 11738661055172304644 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 320620113692599118 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 8466587534284734762 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 3318206549561696188 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 14931201357194905697 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5600314145323623005 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 75483450873662317 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 3793407172213641704 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 2508676912793167321 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 2013136453053212946 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 9302779689053257196 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 16922723248982534245 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingLeft.RingLeft_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 9568588494434360329 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5573555818259644549 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 12082144485035076372 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 5616878210949329467 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 2560. Hash: 4057154333260499171 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 2560. Hash: 1620460765416970456 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 9568588494434360329 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 2560. Hash: 5573555818259644549 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 3542867785718437760 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 720. Hash: 5575788853295372734 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 8244955966647049157 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 720. Hash: 17763430282605007327 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 5442657821959315522 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 720. Hash: 12477592739739533067 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.InlayRings.RingRight.RingRight_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 4609122246850169975 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2696528076485355457 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 6640465288865380105 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 133343330720363387 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 790774688340154169 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 3046294637431015510 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 4609122246850169975 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2696528076485355457 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 16761061667647714654 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 14954885971875692232 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 37061345418264916 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 7368059604555723833 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 6244533771017459078 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 6416015160247779547 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.RightHub.RightHub_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2524875548439506384 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 89810986084680009 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 1044733215619569246 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 15252409165383740719 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 6304. Hash: 3184716392697283856 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 6304. Hash: 4969210758291089995 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 2524875548439506384 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 6304. Hash: 89810986084680009 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 8467946356718878053 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 1617. Hash: 2373603727160338558 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 1521191693628786862 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 1617. Hash: 7175852234718691900 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 12283058591680528758 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 1617. Hash: 14516337485055158228 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.LeftHub.LeftHub_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 7734180808251274182 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 13560118186140352568 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 3520. Hash: 5538036360908204376 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 3520. Hash: 470358662493460341 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 3520. Hash: 13801426056203770982 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 3520. Hash: 8463107387658894201 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 7734180808251274182 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 3520. Hash: 13560118186140352568 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 2691029117997309291 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 960. Hash: 10093724573967674240 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 1221219959437752888 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 960. Hash: 1294720383009806722 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 10294793677923893113 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 960. Hash: 6108415656799664788 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.Inside.Inside_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.Tiled +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 4120783891454032649 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 9011003754405408275 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 18912. Hash: 12406712159692783345 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 18912. Hash: 7868083933985729169 + GenerationMethod: 1 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 18912. Hash: 1990603474898794477 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 18912. Hash: 4812378464029296668 + GenerationMethod: 1 + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.Tiled +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 4120783891454032649 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 18912. Hash: 9011003754405408275 + UVCustomName: Unwrapped + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_2.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + +Node Name: Tiled +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.Tiled +Node Type: MeshVertexUVData + UVs: Count 4891. Hash: 15498889522919365505 + UVCustomName: Tiled + +Node Name: Unwrapped +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.Unwrapped +Node Type: MeshVertexUVData + UVs: Count 4891. Hash: 15832520573612498718 + UVCustomName: Unwrapped + +Node Name: TangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.TangentSet_0 +Node Type: MeshVertexTangentData + Tangents: Count 4891. Hash: 6697133368486369688 + GenerationMethod: 1 + SetIndex: 0 + +Node Name: TangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.TangentSet_1 +Node Type: MeshVertexTangentData + Tangents: Count 4891. Hash: 18420832496569008358 + GenerationMethod: 1 + SetIndex: 1 + +Node Name: BitangentSet_0 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.BitangentSet_0 +Node Type: MeshVertexBitangentData + Bitangents: Count 4891. Hash: 2267250201584370787 + GenerationMethod: 1 + +Node Name: BitangentSet_1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.BitangentSet_1 +Node Type: MeshVertexBitangentData + Bitangents: Count 4891. Hash: 7889928104085840247 + GenerationMethod: 1 + +Node Name: blinn1 +Node Path: RootNode.ShaderBall_1m.MainSphere.MainOuterSphere.MainOuterSphere_1_optimized.blinn1 +Node Type: MaterialData + MaterialName: blinn1 + UniqueId: 2076548245838624187 + IsNoDraw: false + DiffuseColor: < 0.800000, 0.800000, 0.800000> + SpecularColor: < 0.500000, 0.500000, 0.500000> + EmissiveColor: < 0.000000, 0.000000, 0.000000> + Opacity: 1.000000 + Shininess: 6.311791 + UseColorMap: Not set + BaseColor: Not set + UseMetallicMap: Not set + MetallicFactor: Not set + UseRoughnessMap: Not set + RoughnessFactor: Not set + UseEmissiveMap: Not set + EmissiveIntensity: Not set + UseAOMap: Not set + DiffuseTexture: ShaderBall/_dev_shaderball_00_basecolor.png + SpecularTexture: + BumpTexture: + NormalTexture: + MetallicTexture: + RoughnessTexture: + AmbientOcclusionTexture: + EmissiveTexture: + BaseColorTexture: ShaderBall/_dev_shaderball_00_basecolor.png + diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx new file mode 100644 index 0000000000..caf6dcbe8f --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/assets/ShaderBall/shaderball.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e63a55a35c749a16a03e10a1f53a48bd426c61db80151de080235b14cf6b70d +size 2479344 diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py index 71f200074c..5965db57b1 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py @@ -21,8 +21,8 @@ from ly_test_tools.o3de.asset_processor import ASSET_PROCESSOR_PLATFORM_MAP from ..ap_fixtures.asset_processor_fixture import asset_processor as asset_processor from ..ap_fixtures.ap_setup_fixture import ap_setup_fixture as ap_setup_fixture from ..ap_fixtures.ap_config_backup_fixture import ap_config_backup_fixture as ap_config_backup_fixture -from ..ap_fixtures.ap_config_default_platform_fixture import ap_config_default_platform_fixture as ap_config_default_platform_fixture - +from ..ap_fixtures.ap_config_default_platform_fixture \ + import ap_config_default_platform_fixture as ap_config_default_platform_fixture # Import LyShared import ly_test_tools.o3de.pipeline_utils as utils @@ -33,6 +33,7 @@ logger = logging.getLogger(__name__) # Helper: variables we will use for parameter values in the test: targetProjects = ["AutomatedTesting"] + @pytest.fixture @pytest.mark.SUITE_sandbox def local_resources(request, workspace, ap_setup_fixture): @@ -54,21 +55,21 @@ class BlackboxAssetTest: blackbox_fbx_tests = [ pytest.param( BlackboxAssetTest( - test_name= "OneMeshOneMaterial_RunAP_SuccessWithMatchingProducts", - asset_folder= "OneMeshOneMaterial", + test_name="OneMeshOneMaterial_RunAP_SuccessWithMatchingProducts", + asset_folder="OneMeshOneMaterial", scene_debug_file="onemeshonematerial.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "OneMeshOneMaterial.fbx", - uuid = b"8a9164adb84859be893e18aa819438e1", - jobs = [ + source_file_name="OneMeshOneMaterial.fbx", + uuid=b"8a9164adb84859be893e18aa819438e1", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", + job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=1, - products = [ + products=[ asset_db_utils.DBProduct( product_name='onemeshonematerial/onemeshonematerial.dbgsg', sub_id=1918494907, @@ -86,21 +87,21 @@ blackbox_fbx_tests = [ BlackboxAssetTest( # Verifies that the soft naming convention feature with level of detail meshes works. # https://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer-soft-naming.html - test_name= "SoftNamingLOD_RunAP_SuccessWithMatchingProducts", - asset_folder= "SoftNamingLOD", + test_name="SoftNamingLOD_RunAP_SuccessWithMatchingProducts", + asset_folder="SoftNamingLOD", scene_debug_file="lodtest.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "lodtest.fbx", - uuid = b"44c8627fe2c25aae91fe3ff9547be3b9", - jobs = [ + source_file_name="lodtest.fbx", + uuid=b"44c8627fe2c25aae91fe3ff9547be3b9", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", + job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=22, - products = [ + products=[ asset_db_utils.DBProduct( product_name='softnaminglod/lodtest.dbgsg', sub_id=-632012261, @@ -118,21 +119,21 @@ blackbox_fbx_tests = [ BlackboxAssetTest( # Verifies that the soft naming convention feature with physics proxies works. # https://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer-soft-naming.html - test_name= "SoftNamingPhysics_RunAP_SuccessWithMatchingProducts", - asset_folder= "SoftNamingPhysics", + test_name="SoftNamingPhysics_RunAP_SuccessWithMatchingProducts", + asset_folder="SoftNamingPhysics", scene_debug_file="physicstest.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "physicstest.fbx", - uuid = b"df957b7918cf5b029806c73f630fa1c8", - jobs = [ + source_file_name="physicstest.fbx", + uuid=b"df957b7918cf5b029806c73f630fa1c8", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", + job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=14, - products = [ + products=[ asset_db_utils.DBProduct( product_name='softnamingphysics/physicstest.dbgsg', sub_id=-740411732, @@ -152,21 +153,21 @@ blackbox_fbx_tests = [ ), pytest.param( BlackboxAssetTest( - test_name= "MultipleMeshOneMaterial_RunAP_SuccessWithMatchingProducts", - asset_folder= "TwoMeshOneMaterial", + test_name="MultipleMeshOneMaterial_RunAP_SuccessWithMatchingProducts", + asset_folder="TwoMeshOneMaterial", scene_debug_file="multiple_mesh_one_material.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "multiple_mesh_one_material.fbx", - uuid = b"597618fd497659a1b197a015fe47aa95", - jobs = [ + source_file_name="multiple_mesh_one_material.fbx", + uuid=b"597618fd497659a1b197a015fe47aa95", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", + job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=2, - products = [ + products=[ asset_db_utils.DBProduct( product_name='twomeshonematerial/multiple_mesh_one_material.dbgsg', sub_id=2077268018, @@ -183,22 +184,22 @@ blackbox_fbx_tests = [ pytest.param( BlackboxAssetTest( # Verifies whether multiple meshes can share linked materials - test_name= "MultipleMeshLinkedMaterials_RunAP_SuccessWithMatchingProducts", - asset_folder= "TwoMeshLinkedMaterials", - scene_debug_file= "multiple_mesh_linked_materials.dbgsg", - assets = [ + test_name="MultipleMeshLinkedMaterials_RunAP_SuccessWithMatchingProducts", + asset_folder="TwoMeshLinkedMaterials", + scene_debug_file="multiple_mesh_linked_materials.dbgsg", + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "multiple_mesh_linked_materials.fbx", - uuid = b"25d8301c2eef5dc7bded310db8ea608d", - jobs = [ + source_file_name="multiple_mesh_linked_materials.fbx", + uuid=b"25d8301c2eef5dc7bded310db8ea608d", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", - platform= "pc", + job_key="Scene compilation", + platform="pc", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=2, - products= [ + products=[ asset_db_utils.DBProduct( product_name='twomeshlinkedmaterials/multiple_mesh_linked_materials.dbgsg', sub_id=-1898461950, @@ -216,22 +217,22 @@ blackbox_fbx_tests = [ pytest.param( BlackboxAssetTest( # Verifies a mesh with multiple materials - test_name= "SingleMeshMultipleMaterials_RunAP_SuccessWithMatchingProducts", - asset_folder= "OneMeshMultipleMaterials", + test_name="SingleMeshMultipleMaterials_RunAP_SuccessWithMatchingProducts", + asset_folder="OneMeshMultipleMaterials", scene_debug_file="single_mesh_multiple_materials.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "single_mesh_multiple_materials.fbx", - uuid = b"f08fd585dfa35881b4bf86637da5e858", - jobs = [ + source_file_name="single_mesh_multiple_materials.fbx", + uuid=b"f08fd585dfa35881b4bf86637da5e858", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", - platform= "pc", + job_key="Scene compilation", + platform="pc", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=1, - products = [ + products=[ asset_db_utils.DBProduct( product_name='onemeshmultiplematerials/single_mesh_multiple_materials.dbgsg', sub_id=-262822238, @@ -277,21 +278,21 @@ blackbox_fbx_tests = [ ), pytest.param( BlackboxAssetTest( - test_name= "MotionTest_RunAP_SuccessWithMatchingProducts", - asset_folder= "Motion", + test_name="MotionTest_RunAP_SuccessWithMatchingProducts", + asset_folder="Motion", scene_debug_file="Jack_Idle_Aim_ZUp.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "Jack_Idle_Aim_ZUp.fbx", - uuid = b"eda904ae0e145f8b973d57fc5809918b", - jobs = [ + source_file_name="Jack_Idle_Aim_ZUp.fbx", + uuid=b"eda904ae0e145f8b973d57fc5809918b", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", + job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=0, - products = [ + products=[ asset_db_utils.DBProduct( product_name='motion/jack_idle_aim_zup.dbgsg', sub_id=-517610290, @@ -307,29 +308,56 @@ blackbox_fbx_tests = [ ] ), ), + pytest.param( + BlackboxAssetTest( + test_name="ShaderBall_RunAP_SuccessWithMatchingProducts", + asset_folder="ShaderBall", + scene_debug_file="shaderball.dbgsg", + assets=[ + asset_db_utils.DBSourceAsset( + source_file_name="shaderball.fbx", + uuid=b"48181ba8038e5193997540fc8dffb06d", + jobs=[ + asset_db_utils.DBJob( + job_key="Scene compilation", + builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", + status=4, + error_count=0, + warning_count=30, + products=[ + asset_db_utils.DBProduct( + product_name='shaderball/shaderball.dbgsg', + sub_id=-1607815784, + asset_type=b'07f289d14dc74c4094b40a53bbcb9f0b'), + ] + ), + ] + ) + ] + ), + ), ] - blackbox_fbx_special_tests = [ pytest.param( BlackboxAssetTest( - test_name= "MultipleMeshMultipleMaterial_MultipleAssetInfo_RunAP_SuccessWithMatchingProducts", - asset_folder= "TwoMeshTwoMaterial", - override_asset_folder = "OverrideAssetInfoForTwoMeshTwoMaterial", + test_name="MultipleMeshMultipleMaterial_MultipleAssetInfo_RunAP_SuccessWithMatchingProducts", + asset_folder="TwoMeshTwoMaterial", + override_asset_folder="OverrideAssetInfoForTwoMeshTwoMaterial", scene_debug_file="multiple_mesh_multiple_material.dbgsg", override_scene_debug_file="multiple_mesh_multiple_material_override.dbgsg", - assets = [ + assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "multiple_mesh_multiple_material.fbx", - uuid = b"b5915fb874af5c8a866ccabbddb57595", - jobs = [ + source_file_name="multiple_mesh_multiple_material.fbx", + uuid=b"b5915fb874af5c8a866ccabbddb57595", + jobs=[ asset_db_utils.DBJob( job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=2, - products = [ + products=[ asset_db_utils.DBProduct( product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg', sub_id=896980093, @@ -341,16 +369,16 @@ blackbox_fbx_special_tests = [ ], override_assets=[ asset_db_utils.DBSourceAsset( - source_file_name = "multiple_mesh_multiple_material.fbx", - uuid = b"b5915fb874af5c8a866ccabbddb57595", - jobs = [ + source_file_name="multiple_mesh_multiple_material.fbx", + uuid=b"b5915fb874af5c8a866ccabbddb57595", + jobs=[ asset_db_utils.DBJob( - job_key= "Scene compilation", + job_key="Scene compilation", builder_guid=b"bd8bf65894854fe3830e8ec3a23c35f3", status=4, error_count=0, warning_count=2, - products = [ + products=[ asset_db_utils.DBProduct( product_name='twomeshtwomaterial/multiple_mesh_multiple_material.dbgsg', sub_id=896980093, @@ -378,29 +406,26 @@ class TestsFBX_AllPlatforms(object): @pytest.mark.BAT @pytest.mark.SUITE_sandbox @pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests) - def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, - ap_setup_fixture, asset_processor, project, - blackbox_param): + def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace, ap_setup_fixture, + asset_processor, project, blackbox_param): """ - Please see run_fbx_test(...) for details + Please see run_fbx_test(...) for details Test Steps: 1. Determine if blackbox is set to none 2. Run FBX Test + """ if blackbox_param == None: return - self.run_fbx_test(workspace, ap_setup_fixture, - asset_processor, project, blackbox_param) + self.run_fbx_test(workspace, ap_setup_fixture, asset_processor, project, blackbox_param) @pytest.mark.BAT @pytest.mark.SUITE_sandbox @pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests) - def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self, - workspace, ap_setup_fixture, - asset_processor, project, - blackbox_param): + def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts( + self, workspace, ap_setup_fixture, asset_processor, project, blackbox_param): """ Please see run_fbx_test(...) for details @@ -430,7 +455,7 @@ class TestsFBX_AllPlatforms(object): + product.product_name def run_fbx_test(self, workspace, ap_setup_fixture, asset_processor, - project, blackbox_params: BlackboxAssetTest, overrideAsset = False): + project, blackbox_params: BlackboxAssetTest, overrideAsset=False): """ These tests work by having the test case ingest the test data and determine the run pattern. Tests will process scene settings files and will additionally do a verification against a provided debug file @@ -469,21 +494,23 @@ class TestsFBX_AllPlatforms(object): expected_product_list.append(expected_product.product_name) missing_assets, _ = utils.compare_assets_with_cache(expected_product_list, - asset_processor.project_test_cache_folder()) + asset_processor.project_test_cache_folder()) - assert not missing_assets, f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}' + assert not missing_assets, \ + f'The following assets were expected to be in, but not found in cache: {str(missing_assets)}' # Load the asset database. db_path = os.path.join(asset_processor.temp_asset_root(), "Cache", "assetdb.sqlite") cache_root = os.path.dirname(os.path.join(asset_processor.temp_asset_root(), "Cache", - ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform])) + ASSET_PROCESSOR_PLATFORM_MAP[workspace.asset_processor_platform])) if blackbox_params.scene_debug_file: - scene_debug_file = blackbox_params.override_scene_debug_file if overrideAsset\ + scene_debug_file = blackbox_params.override_scene_debug_file if overrideAsset \ else blackbox_params.scene_debug_file - debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(), blackbox_params.scene_debug_file) + debug_graph_path = os.path.join(asset_processor.project_test_cache_folder(), + blackbox_params.scene_debug_file) expected_debug_graph_path = os.path.join(asset_processor.project_test_source_folder(), scene_debug_file) logger.info(f"Parsing scene graph: {debug_graph_path}") diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp index 9fe4cfd0d2..bcaaa02b67 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.cpp @@ -96,10 +96,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent) connect( m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, &AzAssetBrowserWindow::SetTableViewVisibleAfterFilter); - - connect( - m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, - &AzAssetBrowserWindow::UpdateTableModelAfterFilter); connect( m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this, &AzAssetBrowserWindow::SelectionChangedSlot); @@ -251,24 +247,6 @@ void AzAssetBrowserWindow::SetExpandedAssetBrowserMode() m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::ExpandedMode; - disconnect( - m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, - &AzAssetBrowserWindow::UpdateTableModelAfterFilter); - disconnect( - m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, - &AzAssetBrowserWindow::SetTableViewVisibleAfterFilter); - - disconnect( - m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this, - &AzAssetBrowserWindow::SelectionChangedSlot); - disconnect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - disconnect( - m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, - &AzAssetBrowser::SearchWidget::ClearStringFilter); - disconnect( - m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, - &AzAssetBrowser::SearchWidget::ClearTypeFilter); - if (m_ui->m_assetBrowserTableViewWidget->isVisible()) { m_ui->m_assetBrowserTableViewWidget->setVisible(false); @@ -281,37 +259,9 @@ void AzAssetBrowserWindow::SetDefaultAssetBrowserMode() namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::DefaultMode; - - connect( - m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, - &AzAssetBrowserWindow::SetTableViewVisibleAfterFilter); - - connect( - m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this, - &AzAssetBrowserWindow::UpdateTableModelAfterFilter); - connect( - m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this, - &AzAssetBrowserWindow::SelectionChangedSlot); - connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem); - connect( - m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget, - &AzAssetBrowser::SearchWidget::ClearStringFilter); - connect( - m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget, - &AzAssetBrowser::SearchWidget::ClearTypeFilter); - - //If the filter is not empty we want to switch views and Update the model - UpdateTableModelAfterFilter(); SetTableViewVisibleAfterFilter(); } -void AzAssetBrowserWindow::UpdateTableModelAfterFilter() -{ - if (!m_ui->m_searchWidget->GetFilterString().isEmpty()) - { - m_tableModel->UpdateTableModelMaps(); - } -} void AzAssetBrowserWindow::SetTableViewVisibleAfterFilter() { @@ -389,8 +339,8 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected UpdatePreview(); } -// while its tempting to use Activated here, we dont actually want it to count as activation -// just becuase on some OS clicking once is activation. +// while its tempting to use Activated here, we don't actually want it to count as activation +// just because on some OS clicking once is activation. void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element) { namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h index 753f000300..34103316ab 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.h @@ -68,7 +68,6 @@ protected slots: void CreateSwitchViewMenu(); void SetExpandedAssetBrowserMode(); void SetDefaultAssetBrowserMode(); - void UpdateTableModelAfterFilter(); void SetTableViewVisibleAfterFilter(); private: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index fbd066ec6e..3f254581dd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -70,6 +70,7 @@ #include #include #include +#include #include AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer' needs to have dll-interface to be used by clients of class 'QFileInfo' @@ -273,7 +274,8 @@ namespace AzToolsFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), - azrtti_typeid() + azrtti_typeid(), + azrtti_typeid(), }); return components; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index ec709aef8a..14c5e719b4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -24,24 +24,13 @@ namespace AzToolsFramework AZ_Assert( m_filterModel, "Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel."); - connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &AssetBrowserTableModel::UpdateTableModelMaps); - connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &AssetBrowserTableModel::UpdateTableModelMaps); - connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &AssetBrowserTableModel::beginResetModel); - connect( - sourceModel, &QAbstractItemModel::modelReset, this, - [this]() - { - { - QSignalBlocker sb(this); - UpdateTableModelMaps(); - } - endResetModel(); - }); - connect(sourceModel, &QAbstractItemModel::layoutChanged, this, &AssetBrowserTableModel::UpdateTableModelMaps); - connect(sourceModel, &QAbstractItemModel::dataChanged, this, &AssetBrowserTableModel::SourceDataChanged); - - QSortFilterProxyModel::setSourceModel(sourceModel); + + connect(m_filterModel, &QAbstractItemModel::rowsInserted, this, &AssetBrowserTableModel::UpdateTableModelMaps); + connect(m_filterModel, &QAbstractItemModel::rowsRemoved, this, &AssetBrowserTableModel::UpdateTableModelMaps); + connect(m_filterModel, &QAbstractItemModel::layoutChanged, this, &AssetBrowserTableModel::UpdateTableModelMaps); + connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, &AssetBrowserTableModel::beginResetModel); + connect(m_filterModel, &QAbstractItemModel::dataChanged, this, &AssetBrowserTableModel::SourceDataChanged); } QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const @@ -112,7 +101,7 @@ namespace AzToolsFramework int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const { - return !parent.isValid() ? m_indexMap.size() : sourceModel()->rowCount(parent); + return !parent.isValid() ? m_indexMap.size() : 0; } int AssetBrowserTableModel::BuildTableModelMap( @@ -162,28 +151,29 @@ namespace AzToolsFramework AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const { - if (index.isValid()) - { - return static_cast(index.internalPointer()); - } - else + if (!index.isValid()) { AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry."); return nullptr; } + return static_cast(index.internalPointer()); } void AssetBrowserTableModel::UpdateTableModelMaps() { + beginResetModel(); emit layoutAboutToBeChanged(); - m_indexMap.clear(); - m_rowMap.clear(); + if (!m_indexMap.isEmpty() || !m_rowMap.isEmpty()) + { + m_indexMap.clear(); + m_rowMap.clear(); + } AzToolsFramework::EditorSettingsAPIBus::BroadcastResult( m_numberOfItemsDisplayed, &AzToolsFramework::EditorSettingsAPIBus::Handler::GetMaxNumberOfItemsShownInSearchView); - BuildTableModelMap(sourceModel()); emit layoutChanged(); + endResetModel(); } } // namespace AssetBrowser } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index f84e6bd81c..4048156b60 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -21,7 +21,9 @@ namespace AzToolsFramework class AssetBrowserFilterModel; class AssetBrowserEntry; - class AssetBrowserTableModel : public QSortFilterProxyModel + class AssetBrowserTableModel + : public QSortFilterProxyModel + , public AssetBrowserComponentNotificationBus::Handler { Q_OBJECT @@ -43,7 +45,7 @@ namespace AzToolsFramework int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// - private: + AssetBrowserEntry* GetAssetEntry(QModelIndex index) const; int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index b463381638..8b58791e68 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -225,29 +225,36 @@ namespace AzToolsFramework const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column()); const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column()); - auto aboveEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); - auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); + auto belowEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole)); + auto aboveEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole)); - auto aboveSourceEntry = azrtti_cast(aboveEntry); auto belowSourceEntry = azrtti_cast(belowEntry); + auto aboveSourceEntry = azrtti_cast(aboveEntry); - // if current index is the last entry in the view - // or the index above it is a Source Entry and - // the index below is invalid or is valid but it is also a source entry - // then the current index is the only child. - if (index.row() == viewModel->rowCount() - 1 || - (indexBelow.isValid() && aboveSourceEntry && - (!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry)))) + // Last item and the above entry is a source entry + // or indexBelow is a source entry and the index above is not + if (viewModel->rowCount() > 0 && index.row() == viewModel->rowCount() - 1) + { + if (aboveSourceEntry) + { + DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); + } + else + { + DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); + } + } + else if (belowSourceEntry && aboveSourceEntry) { DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon } - else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry + else if (belowSourceEntry && !aboveSourceEntry) { - DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon + DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); } - else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry + else if (aboveSourceEntry) // The index above is a source entry { - DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon + DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw First Child Icon } else //the index above and below are also child entries { @@ -286,7 +293,6 @@ namespace AzToolsFramework absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast; break; case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild: - default: absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild; break; } @@ -311,5 +317,4 @@ namespace AzToolsFramework } // namespace AssetBrowser } // namespace AzToolsFramework - #include "AssetBrowser/Views/moc_EntryDelegate.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index d2a88df544..2cdf409125 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -54,6 +54,7 @@ #include #include #include +#include AZ_DEFINE_BUDGET(AzToolsFramework); @@ -106,6 +107,7 @@ namespace AzToolsFramework AzToolsFramework::Components::EditorIntersectorComponent::CreateDescriptor(), AzToolsFramework::AzToolsFrameworkConfigurationSystemComponent::CreateDescriptor(), AzToolsFramework::Components::EditorEntityUiSystemComponent::CreateDescriptor(), + AzToolsFramework::Script::LuaSymbolsReporterSystemComponent::CreateDescriptor(), }); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h new file mode 100644 index 0000000000..f0bfecef38 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterBus.h @@ -0,0 +1,110 @@ +/* + * 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 + * + */ +#pragma once + +#include +#include + +namespace AzToolsFramework +{ + namespace Script + { + struct LuaPropertySymbol + { + AZ_TYPE_INFO(LuaPropertySymbol, "{5AFB147F-50A4-4F00-9F82-D8D5BBC970D6}"); + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_name; + bool m_canRead; + bool m_canWrite; + + AZStd::string ToString() const; + }; + + struct LuaMethodSymbol + { + AZ_TYPE_INFO(LuaMethodSymbol, "{7B074A36-C81D-46A0-8D2F-62E426EBE38A}"); + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_name; + AZStd::string m_debugArgumentInfo; + + AZStd::string ToString() const; + }; + + struct LuaClassSymbol + { + AZ_TYPE_INFO(LuaClassSymbol, "{5FBE5841-A8E1-44B6-BEDA-22302CF8DF5F}"); + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_name; + AZ::Uuid m_typeId; + AZStd::vector m_properties; + AZStd::vector m_methods; + + AZStd::string ToString() const; + }; + + struct LuaEBusSender + { + AZ_TYPE_INFO(LuaEBusSender, "{23EE4188-0924-49DB-BF3F-EB7AAB6D5E5C}"); + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_name; + AZStd::string m_debugArgumentInfo; + AZStd::string m_category; + + AZStd::string ToString() const; + }; + + struct LuaEBusSymbol + { + AZ_TYPE_INFO(LuaEBusSymbol, "{381C5639-A916-4D2E-B825-50A3F2D93137}"); + static void Reflect(AZ::ReflectContext* context); + + AZStd::string m_name; + bool m_canBroadcast; + bool m_canQueue; + bool m_hasHandler; + + AZStd::vector m_senders; + + AZStd::string ToString() const; + }; + + // This is an EBus useful to scrape classes, globals and EBuses exposed to game scripting + // e.g: Lua. + class LuaSymbolsReporterRequests + { + public: + AZ_RTTI(LuaSymbolsReporterRequests, "{3FF9A105-3159-49FF-8DC6-4948AE7B4AB8}"); + virtual ~LuaSymbolsReporterRequests() = default; + // Put your public methods here + + virtual const AZStd::vector& GetListOfClasses() = 0; + virtual const AZStd::vector& GetListOfGlobalProperties() = 0; + virtual const AZStd::vector& GetListOfGlobalFunctions() = 0; + virtual const AZStd::vector& GetListOfEBuses() = 0; + + }; + + class LuaSymbolsReporterBusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using LuaSymbolsReporterRequestBus = AZ::EBus; + + } // namespace Script +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp new file mode 100644 index 0000000000..81ba8ee398 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.cpp @@ -0,0 +1,475 @@ +/* + * 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 + * + */ + +#include +#include +#include +#include + +#include "LuaSymbolsReporterSystemComponent.h" + +namespace AzToolsFramework +{ + namespace Script + { + AZStd::string LuaPropertySymbol::ToString() const + { + return AZStd::string::format("%s [%s/%s]", + m_name.c_str(), + m_canRead ? "R" : "_", + m_canWrite ? "W" : "_"); + } + + void LuaPropertySymbol::Reflect(AZ::ReflectContext* context) + { + auto behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "script") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("name", BehaviorValueProperty(&LuaPropertySymbol::m_name)) + ->Property("canRead", BehaviorValueProperty(&LuaPropertySymbol::m_canRead)) + ->Property("canWrite", BehaviorValueProperty(&LuaPropertySymbol::m_canWrite)) + ->Method("ToString", &LuaPropertySymbol::ToString) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) + ; + } + } + + AZStd::string LuaMethodSymbol::ToString() const + { + return AZStd::string::format("%s(%s)", m_name.c_str(), m_debugArgumentInfo.c_str()); + } + + void LuaMethodSymbol::Reflect(AZ::ReflectContext* context) + { + auto behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "script") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("name", BehaviorValueProperty(&LuaMethodSymbol::m_name)) + ->Property("debugArgumentInfo", BehaviorValueProperty(&LuaMethodSymbol::m_debugArgumentInfo)) + ->Method("ToString", &LuaMethodSymbol::ToString) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) + ; + } + } + + AZStd::string LuaClassSymbol::ToString() const + { + return AZStd::string::format("%s [%s]", m_name.c_str(), m_typeId.ToString().c_str()); + } + + void LuaClassSymbol::Reflect(AZ::ReflectContext* context) + { + auto behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "script") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("name", BehaviorValueProperty(&LuaClassSymbol::m_name)) + ->Property("typeId", BehaviorValueProperty(&LuaClassSymbol::m_typeId)) + ->Property("properties", BehaviorValueProperty(&LuaClassSymbol::m_properties)) + ->Property("methods", BehaviorValueProperty(&LuaClassSymbol::m_methods)) + ->Method("ToString", &LuaClassSymbol::ToString) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) + ; + } + } + + AZStd::string LuaEBusSender::ToString() const + { + return AZStd::string::format("%s(%s) - [%s]", m_name.c_str(), m_debugArgumentInfo.c_str(), m_category.c_str()); + } + + void LuaEBusSender::Reflect(AZ::ReflectContext* context) + { + auto behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "script") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("name", BehaviorValueProperty(&LuaEBusSender::m_name)) + ->Property("debugArgumentInfo", BehaviorValueProperty(&LuaEBusSender::m_debugArgumentInfo)) + ->Property("category", BehaviorValueProperty(&LuaEBusSender::m_category)) + ->Method("ToString", &LuaEBusSender::ToString) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) + ; + } + } + + AZStd::string LuaEBusSymbol::ToString() const + { + auto boolToStr = +[](bool val) { return val ? "true" : "false"; }; + return AZStd::string::format("%s: canBroadcast(%s), canQueue(%s), hasHandler(%s)", + m_name.c_str(), + boolToStr(m_canBroadcast), boolToStr(m_canQueue), boolToStr(m_hasHandler)); + } + + void LuaEBusSymbol::Reflect(AZ::ReflectContext* context) + { + auto behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "script") + ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Property("name", BehaviorValueProperty(&LuaEBusSymbol::m_name)) + ->Property("canBroadcast", BehaviorValueProperty(&LuaEBusSymbol::m_canBroadcast)) + ->Property("canQueue", BehaviorValueProperty(&LuaEBusSymbol::m_canQueue)) + ->Property("hasHandler", BehaviorValueProperty(&LuaEBusSymbol::m_hasHandler)) + ->Property("senders", BehaviorValueProperty(&LuaEBusSymbol::m_senders)) + ->Method("ToString", &LuaEBusSymbol::ToString) + ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString) + ; + } + } + + //! This local class helps us keeping private the sensitive data in LuaSymbolsReporterSystemComponent + //! Used inside the function pointers for several AZ::SciptContextDebug::Enumerate* functions. + class IntrusiveHelper + { + public: + static AZStd::vector& GetClassSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedClassSymbols; } + static AZStd::unordered_map& GetClassUuidToIndexMap(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_classUuidToIndexMap; } + static AZStd::vector& GetGlobalPropertySymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedGlobalPropertySymbols; } + static AZStd::vector& GetGlobalFunctionSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedGlobalFunctionSymbols; } + static AZStd::vector& GetEBusSymbols(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_cachedEbusSymbols; } + static AZStd::unordered_map& GetEBusNameToIndexMap(LuaSymbolsReporterSystemComponent& symbolsReporter) { return symbolsReporter.m_ebusNameToIndexMap; } + }; + + void LuaSymbolsReporterSystemComponent::Reflect(AZ::ReflectContext* context) + { + LuaPropertySymbol::Reflect(context); + LuaMethodSymbol::Reflect(context); + LuaClassSymbol::Reflect(context); + LuaEBusSender::Reflect(context); + LuaEBusSymbol::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0); + + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + serializeContext->RegisterGenericType>(); + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("LuaSymbolsReporterBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "script") + ->Event("GetListOfClasses", &LuaSymbolsReporterRequests::GetListOfClasses) + ->Event("GetListOfGlobalProperties", &LuaSymbolsReporterRequests::GetListOfGlobalProperties) + ->Event("GetListOfGlobalFunctions", &LuaSymbolsReporterRequests::GetListOfGlobalFunctions) + ->Event("GetListOfEBuses", &LuaSymbolsReporterRequests::GetListOfEBuses) + ; + } + } + + void LuaSymbolsReporterSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("LuaSymbolsReporterSystemService")); + } + + void LuaSymbolsReporterSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("LuaSymbolsReporterSystemService")); + } + + void LuaSymbolsReporterSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("ScriptService")); + } + + void LuaSymbolsReporterSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + // No dependent services. + } + + void LuaSymbolsReporterSystemComponent::Activate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + LuaSymbolsReporterRequestBus::Handler::BusConnect(); + } + + void LuaSymbolsReporterSystemComponent::Deactivate() + { + LuaSymbolsReporterRequestBus::Handler::BusDisconnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + } + + AZ::ScriptContext* LuaSymbolsReporterSystemComponent::InitScriptContext() + { + if (m_scriptContext) + { + return m_scriptContext; + } + + AZ::ScriptSystemRequestBus::BroadcastResult(m_scriptContext, &AZ::ScriptSystemRequests::GetContext, AZ::ScriptContextIds::DefaultScriptContextId); + return m_scriptContext; + } + + void LuaSymbolsReporterSystemComponent::LoadGlobalSymbols() + { + auto scriptContext = InitScriptContext(); + if (!scriptContext) + { + AZ_Error(LogName, false, "Invalid scriptContext"); + return; + } + + scriptContext->EnableDebug(); + + auto debugContext = scriptContext->GetDebugContext(); + if (!debugContext) + { + AZ_Error(LogName, false, "Invalid debugContext from scriptContext"); + return; + } + + auto enumMethodFunc = +[]([[maybe_unused]] const AZ::Uuid* classTypeId, const char* methodName, const char* debugArgumentInfo, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + auto& methodSymbols = IntrusiveHelper::GetGlobalFunctionSymbols(mySelf); + methodSymbols.push_back({}); + auto& methodSymbol = methodSymbols.back(); + methodSymbol.m_name = methodName; + if (debugArgumentInfo) + { + methodSymbol.m_debugArgumentInfo = debugArgumentInfo; + } + return true; + }; + + auto enumPropertyFunc = +[]([[maybe_unused]] const AZ::Uuid* classTypeId, const char* propertyName, bool canRead, bool canWrite, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + auto& propertySymbols = IntrusiveHelper::GetGlobalPropertySymbols(mySelf); + propertySymbols.push_back({}); + auto& propertySymbol = propertySymbols.back(); + propertySymbol.m_name = propertyName; + propertySymbol.m_canRead = canRead; + propertySymbol.m_canWrite = canWrite; + + return true; + }; + + debugContext->EnumRegisteredGlobals(enumMethodFunc, enumPropertyFunc, this); + + scriptContext->DisableDebug(); + } + + /////////////////////////////////////////////////////////////////////////// + /// LuaSymbolsReporterRequestBus::Handler + const AZStd::vector& LuaSymbolsReporterSystemComponent::GetListOfClasses() + { + if (!m_cachedClassSymbols.empty()) + { + return m_cachedClassSymbols; + } + + auto scriptContext = InitScriptContext(); + if (!scriptContext) + { + return m_cachedClassSymbols; + } + + scriptContext->EnableDebug(); + + auto debugContext = scriptContext->GetDebugContext(); + if (!debugContext) + { + return m_cachedClassSymbols; + } + + auto enumClassFunc = +[](const char* className, const AZ::Uuid& classTypeId, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + + auto& classSymbols = IntrusiveHelper::GetClassSymbols(mySelf); + classSymbols.push_back({}); + auto& classSymbol = classSymbols.back(); + classSymbol.m_name = className; + classSymbol.m_typeId = classTypeId; + + auto& uuidToClassMap = IntrusiveHelper::GetClassUuidToIndexMap(mySelf); + uuidToClassMap.emplace(classTypeId, classSymbols.size() - 1); + + return true; + }; + + auto enumMethodFunc = +[](const AZ::Uuid* classTypeId, const char* methodName, const char* debugArgumentInfo, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + auto& classUuidToIndexMap = IntrusiveHelper::GetClassUuidToIndexMap(mySelf); + auto itor = classUuidToIndexMap.find(*classTypeId); + if (itor == classUuidToIndexMap.end()) + { + AZ_Error(LogName, false, "Can not add method [%s] because class uuid [%s] is not registered", methodName, classTypeId->ToString().c_str()); + return false; + } + + auto classIndex = itor->second; + auto& classSymbols = IntrusiveHelper::GetClassSymbols(mySelf); + auto& classSymbol = classSymbols[classIndex]; + classSymbol.m_methods.push_back({}); + auto& methodSymbol = classSymbol.m_methods.back(); + methodSymbol.m_name = methodName; + if (debugArgumentInfo) + { + methodSymbol.m_debugArgumentInfo = debugArgumentInfo; + } + return true; + }; + + auto enumPropertyFunc = +[](const AZ::Uuid* classTypeId, const char* propertyName, bool canRead, bool canWrite, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + auto& classUuidToIndexMap = IntrusiveHelper::GetClassUuidToIndexMap(mySelf); + auto itor = classUuidToIndexMap.find(*classTypeId); + if (itor == classUuidToIndexMap.end()) + { + AZ_Error(LogName, false, "Can not add property [%s] because class uuid [%s] is not registered", propertyName, classTypeId->ToString().c_str()); + return false; + } + + auto classIndex = itor->second; + auto& classSymbols = IntrusiveHelper::GetClassSymbols(mySelf); + auto& classSymbol = classSymbols[classIndex]; + classSymbol.m_properties.push_back({}); + auto& propertySymbol = classSymbol.m_properties.back(); + propertySymbol.m_name = propertyName; + propertySymbol.m_canRead = canRead; + propertySymbol.m_canWrite = canWrite; + + return true; + }; + + debugContext->EnumRegisteredClasses(enumClassFunc, enumMethodFunc, enumPropertyFunc, this); + + scriptContext->DisableDebug(); + + return m_cachedClassSymbols; + } + + const AZStd::vector& LuaSymbolsReporterSystemComponent::GetListOfGlobalProperties() + { + if (!m_cachedGlobalPropertySymbols.empty()) + { + return m_cachedGlobalPropertySymbols; + } + + LoadGlobalSymbols(); + + return m_cachedGlobalPropertySymbols; + } + + const AZStd::vector& LuaSymbolsReporterSystemComponent::GetListOfGlobalFunctions() + { + if (!m_cachedGlobalFunctionSymbols.empty()) + { + return m_cachedGlobalFunctionSymbols; + } + + LoadGlobalSymbols(); + + return m_cachedGlobalFunctionSymbols; + } + + const AZStd::vector& LuaSymbolsReporterSystemComponent::GetListOfEBuses() + { + if (!m_cachedEbusSymbols.empty()) + { + return m_cachedEbusSymbols; + } + + auto scriptContext = InitScriptContext(); + if (!scriptContext) + { + return m_cachedEbusSymbols; + } + + scriptContext->EnableDebug(); + + auto debugContext = scriptContext->GetDebugContext(); + if (!debugContext) + { + return m_cachedEbusSymbols; + } + + auto enumEBusFunc = +[](const AZStd::string& ebusName, bool canBroadcast, bool canQueue, bool hasHandler, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + + auto& ebusSymbols = IntrusiveHelper::GetEBusSymbols(mySelf); + ebusSymbols.push_back({}); + auto& ebusSymbol = ebusSymbols.back(); + ebusSymbol.m_name = ebusName; + ebusSymbol.m_canBroadcast = canBroadcast; + ebusSymbol.m_canQueue = canQueue; + ebusSymbol.m_hasHandler = hasHandler; + + auto& nameToIndexMap = IntrusiveHelper::GetEBusNameToIndexMap(mySelf); + nameToIndexMap.emplace(ebusName, ebusSymbols.size() - 1); + + return true; + }; + + auto enumEBusSenderFunc = +[](const AZStd::string& ebusName, const AZStd::string& senderName, const AZStd::string& debugArgumentInfo, const AZStd::string& category, void* userData) -> bool + { + auto& mySelf = *reinterpret_cast(userData); + auto& nameToIndexMap = IntrusiveHelper::GetEBusNameToIndexMap(mySelf); + auto itor = nameToIndexMap.find(ebusName); + if (itor == nameToIndexMap.end()) + { + AZ_Error(LogName, false, "Can not add ebus sender [%s] because ebus [%s] is not registered", senderName.c_str(), ebusName.c_str()); + return false; + } + + auto ebusIndex = itor->second; + auto& ebusSymbols = IntrusiveHelper::GetEBusSymbols(mySelf); + auto& ebusSymbol = ebusSymbols[ebusIndex]; + + ebusSymbol.m_senders.push_back({}); + auto& ebusSender = ebusSymbol.m_senders.back(); + ebusSender.m_name = senderName; + ebusSender.m_debugArgumentInfo = debugArgumentInfo; + ebusSender.m_category = category; + return true; + }; + + debugContext->EnumRegisteredEBuses(enumEBusFunc, enumEBusSenderFunc, this); + + scriptContext->DisableDebug(); + + return m_cachedEbusSymbols; + } + /////////////////////////////////////////////////////////////////////////// + + } //namespace Script +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h new file mode 100644 index 0000000000..2467c82ad7 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h @@ -0,0 +1,73 @@ +/* + * 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 + * + */ +#pragma once + +#include +#include + +#include + +namespace AzToolsFramework +{ + namespace Script + { + /// System component for LuaSymbolsReporterRequestBus + class LuaSymbolsReporterSystemComponent + : public AZ::Component + , public LuaSymbolsReporterRequestBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler + { + public: + AZ_COMPONENT(LuaSymbolsReporterSystemComponent, "{DB8D95BA-FECF-4D81-A45C-8C05E706E2AC}"); + static void Reflect(AZ::ReflectContext* context); + + static constexpr char LogName[] = "LuaSymbolsReporter"; + + LuaSymbolsReporterSystemComponent() = default; + ~LuaSymbolsReporterSystemComponent() = default; + + /////////////////////////////////////////////////////////////////////////// + /// LuaSymbolsReporterRequestBus::Handler + const AZStd::vector& GetListOfClasses() override; + const AZStd::vector& GetListOfGlobalProperties() override; + const AZStd::vector& GetListOfGlobalFunctions() override; + const AZStd::vector& GetListOfEBuses() override; + /////////////////////////////////////////////////////////////////////////// + + private: + friend class IntrusiveHelper; + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate() override; + void Deactivate() override; + + AZ::ScriptContext* InitScriptContext(); + void LoadGlobalSymbols(); + + AZ::ScriptContext* m_scriptContext = nullptr; + + AZStd::vector m_cachedClassSymbols; + // The key is a class uuid, the value is the index in @m_cachedClassSymbols + AZStd::unordered_map m_classUuidToIndexMap; + + AZStd::vector m_cachedGlobalPropertySymbols; + AZStd::vector m_cachedGlobalFunctionSymbols; + + AZStd::vector m_cachedEbusSymbols; + + // The key is the ebus name, the value is the index in @m_cachedEbusSymbols + AZStd::unordered_map m_ebusNameToIndexMap; + + }; + } // namespace Script +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index a5f1e29942..434a1d8303 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -943,13 +943,15 @@ namespace AzToolsFramework { return false; } - + + const int count = rowCount(parent); AZ::EntityId newParentId = GetEntityFromIndex(parent); - AZ::EntityId beforeEntityId = GetEntityFromIndex(index(row, 0, parent)); + AZ::EntityId beforeEntityId = (row >= 0 && row < count) ? GetEntityFromIndex(index(row, 0, parent)) : AZ::EntityId(); EntityIdList topLevelEntityIds; topLevelEntityIds.reserve(entityIdListContainer.m_entityIds.size()); ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::FindTopLevelEntityIdsInactive, entityIdListContainer.m_entityIds, topLevelEntityIds); - if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId)) + const auto appendActionForInvalid = newParentId.IsValid() && (row >= count) ? AppendEnd : AppendBeginning; + if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId, appendActionForInvalid)) { return false; } @@ -1046,7 +1048,7 @@ namespace AzToolsFramework return true; } - bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId) + bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId, ReparentForInvalid forInvalid) { AZ_PROFILE_FUNCTION(AzToolsFramework); if (!CanReparentEntities(newParentId, selectedEntityIds)) @@ -1056,10 +1058,18 @@ namespace AzToolsFramework m_isFilterDirty = true; - ScopedUndoBatch undo("Reparent Entities"); //capture child entity order before re-parent operation, which will automatically add order info if not present EntityOrderArray entityOrderArray = GetEntityChildOrder(newParentId); + //search for the insertion entity in the order array + const auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId); + const bool hasInvalidIndex = beforeEntityItr == entityOrderArray.end(); + if (hasInvalidIndex && forInvalid == None) + { + return false; + } + + ScopedUndoBatch undo("Reparent Entities"); // The new parent is dirty due to sort change(s) undo.MarkEntityDirty(GetEntityIdForSortInfo(newParentId)); @@ -1088,9 +1098,7 @@ namespace AzToolsFramework } } - //search for the insertion entity in the order array - auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId); - + //replace order info matching selection with bad values rather than remove to preserve layout for (auto& id : entityOrderArray) { @@ -1100,17 +1108,25 @@ namespace AzToolsFramework } } - if (newParentId.IsValid()) + //if adding to a valid parent entity, insert at the found entity location or at the head/tail depending on placeAtTail flag + if (hasInvalidIndex) { - //if adding to a valid parent entity, insert at the found entity location or at the head of the container - auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.begin(); - entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end()); - } - else + switch(forInvalid) + { + case AppendEnd: + entityOrderArray.insert(entityOrderArray.end(), processedEntityIds.begin(), processedEntityIds.end()); + break; + case AppendBeginning: + entityOrderArray.insert(entityOrderArray.begin(), processedEntityIds.begin(), processedEntityIds.end()); + break; + default: + AZ_Assert(false, "Unexpected type for ReparentForInvalid"); + break; + } + } + else { - //if adding to an invalid parent entity (the root), insert at the found entity location or at the tail of the container - auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.end(); - entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end()); + entityOrderArray.insert(beforeEntityItr, processedEntityIds.begin(), processedEntityIds.end()); } //remove placeholder entity ids diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 8176867038..0a46ee4850 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -72,6 +72,13 @@ namespace AzToolsFramework ColumnCount //!< Total number of columns }; + enum ReparentForInvalid + { + None, //!< For an invalid location the entity does not change location + AppendEnd, //!< Append Item to end of target parent list + AppendBeginning, //!< Append Item to the beginning of target parent list + }; + // Note: the ColumnSortIndex column isn't shown, hence the -1 and the need for a separate counter. // A wrong column count number causes refresh issues and hover mismatch on model update. static const int VisibleColumnCount = ColumnCount - 1; @@ -162,7 +169,7 @@ namespace AzToolsFramework // Buffer Processing Slots - These are called using single-shot events when the buffers begin to fill. bool CanReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds) const; - bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId()); + bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId(), ReparentForInvalid forInvalid = None); //! Use the current filter setting and re-evaluate the filter. void InvalidateFilter(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index b57eedcd81..286ef97418 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -770,6 +770,9 @@ set(FILES PythonTerminal/ScriptTermDialog.ui Input/QtEventToAzInputManager.h Input/QtEventToAzInputManager.cpp + Script/LuaSymbolsReporterBus.h + Script/LuaSymbolsReporterSystemComponent.h + Script/LuaSymbolsReporterSystemComponent.cpp ) # Prevent the following files from being grouped in UNITY builds diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index d34abcbc6c..a47ccb62c9 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -34,6 +34,7 @@ ly_add_target( INCLUDE_DIRECTORIES PRIVATE Source + Platform/${PAL_PLATFORM_NAME} BUILD_DEPENDENCIES PRIVATE 3rdParty::Qt::Core diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake index 11222602d5..c3acd44f9b 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_linux.cpp ProjectUtils_linux.cpp ProjectManagerDefs_linux.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Linux.h ) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h new file mode 100644 index 0000000000..7c0543361f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Linux.h @@ -0,0 +1,11 @@ +/* + * 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 + * + */ + +#pragma once + +#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..97aee25507 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectManager_Traits_Platform.h @@ -0,0 +1,11 @@ +/* + * 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 + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake index 54b35f0d3c..6d4d453f21 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_mac.cpp ProjectUtils_mac.cpp ProjectManagerDefs_mac.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Mac.h ) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h new file mode 100644 index 0000000000..7c0543361f --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Mac.h @@ -0,0 +1,11 @@ +/* + * 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 + * + */ + +#pragma once + +#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..dc77e77fd0 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectManager_Traits_Platform.h @@ -0,0 +1,11 @@ +/* + * 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 + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake index d95b0d2502..22b4614ddf 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake @@ -11,4 +11,6 @@ set(FILES ProjectBuilderWorker_windows.cpp ProjectUtils_windows.cpp ProjectManagerDefs_windows.cpp + ProjectManager_Traits_Platform.h + ProjectManager_Traits_Windows.h ) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h new file mode 100644 index 0000000000..f5eac50dbc --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Platform.h @@ -0,0 +1,11 @@ +/* + * 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 + * + */ + +#pragma once + +#include diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h new file mode 100644 index 0000000000..e6422b5a77 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManager_Traits_Windows.h @@ -0,0 +1,11 @@ +/* + * 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 + * + */ + +#pragma once + +#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR true diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 6694168f2b..db0ec7e91a 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -9,7 +9,7 @@ QMainWindow { #ScreensCtrl { min-width:1200px; - min-height:800px; + min-height:700px; } QPushButton:focus { diff --git a/Code/Tools/ProjectManager/Source/Application.cpp b/Code/Tools/ProjectManager/Source/Application.cpp index a7e4805ce9..29e0df3c3a 100644 --- a/Code/Tools/ProjectManager/Source/Application.cpp +++ b/Code/Tools/ProjectManager/Source/Application.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -194,8 +195,12 @@ namespace O3DE::ProjectManager // set stylesheet after creating the main window or their styles won't get updated AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss")); - // the decoration wrapper is intended to remember window positioning and sizing + // the decoration wrapper is intended to remember window positioning and sizing +#if AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR auto wrapper = new AzQtComponents::WindowDecorationWrapper(); +#else + auto wrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled); +#endif wrapper->setGuest(m_mainWindow.data()); // show the main window here to apply the stylesheet before restoring geometry or we diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h index a200f30c98..52e272a9c4 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h @@ -50,12 +50,12 @@ namespace AZ return scope; } - //! Sets the active context based on the provided PassHierarchyFilter. If the filter doesn't match exactly one pass, then do nothing. - static ImGuiActiveContextScope FromPass(const RPI::PassHierarchyFilter& passHierarchyFilter) + //! Sets the active context based on the provided pass hierarchy filter. If the filter doesn't match exactly one pass, then do nothing. + static ImGuiActiveContextScope FromPass(const AZStd::vector& passHierarchy) { ImGuiActiveContextScope scope; scope.ConnectToImguiNotificationBus(); - ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchyFilter); + ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchy); return scope; } diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h index 289cb178d4..78f71b39ba 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h @@ -15,11 +15,6 @@ namespace AZ { - namespace RPI - { - class PassHierarchyFilter; - } - namespace Render { class ImGuiPass; @@ -51,7 +46,7 @@ namespace AZ //! Pushes whichever ImGui pass is default on the top of the active context stack. Returns true/false for success/fail. virtual bool PushActiveContextFromDefaultPass() = 0; //! Pushes whichever ImGui pass is provided in passHierarchy on the top of the active context stack. Returns true/false for success/fail. - virtual bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) = 0; + virtual bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) = 0; //! Pops the active context off the top of the active context stack. Returns true if there's a context to pop. virtual bool PopActiveContext() = 0; //! Gets the context at the top of the active context stack. Returns nullptr if the stack is emtpy. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h index 93600ec08f..c80926700e 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h @@ -50,7 +50,7 @@ namespace AZ virtual bool CaptureScreenshotWithPreview(const AZStd::string& outputFilePath) = 0; //! Save a buffer attachment or a image attachment binded to a pass's slot to a data file. - //! @param passHierarchy For finding the pass by using PassHierarchyFilter + //! @param passHierarchy For finding the pass by using a pass hierarchy filter. Check PassFilter::CreateWithPassHierarchy() function for detail //! @param slotName Name of the pass's slot. The attachment bound to this slot will be captured. //! @param option Only valid for an InputOutput attachment. Use PassAttachmentReadbackOption::Input to capture the input state //! and use PassAttachmentReadbackOption::Output to capture the output state diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 6c24b7d35b..b6c6910fd3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -647,54 +647,46 @@ namespace AZ UpdateViewsOfCascadeSegments(); } - void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() { - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("CascadedShadowmapsTemplate")); + void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() + { m_cascadedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline()) + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("CascadedShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { + RPI::RenderPipeline* pipeline = pass->GetRenderPipeline(); const RPI::RenderPipelineId pipelineId = pipeline->GetId(); - // This function can be called when the pipeline is not attached to the scene. - // So we check it is attached to the scene. - if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline) + + CascadedShadowmapsPass* shadowPass = azrtti_cast(pass); + AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); + if (pipeline->GetDefaultView()) { - CascadedShadowmapsPass* shadowPass = azrtti_cast(pass); - AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass."); - if (pipeline->GetDefaultView()) - { - m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass); - } + m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DirectionalLightFeatureProcessor::CacheEsmShadowmapsPass() { - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline()) + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - const RPI::RenderPipelineId pipelineId = pipeline->GetId(); - // checking the render pipeline is just removed from the scene. - if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline) + const RPI::RenderPipelineId pipelineId = pass->GetRenderPipeline()->GetId(); + + if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end()) { - if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end()) + EsmShadowmapsPass* esmPass = azrtti_cast(pass); + AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); + if (esmPass->GetLightTypeName() == m_lightTypeName) { - EsmShadowmapsPass* esmPass = azrtti_cast(pass); - AZ_Assert(esmPass, "It is not an EsmShadowmapPass."); - if (m_cascadedShadowmapsPasses.find(esmPass->GetRenderPipeline()->GetId()) != m_cascadedShadowmapsPasses.end() && - esmPass->GetLightTypeName() == m_lightTypeName) - { - m_esmShadowmapsPasses[pipelineId].push_back(esmPass); - } + m_esmShadowmapsPasses[pipelineId].push_back(esmPass); } } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DirectionalLightFeatureProcessor::PrepareCameraViews() @@ -1063,12 +1055,13 @@ namespace AZ // if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view, // to filter out shadows from objects that are excluded from the cubemap - RPI::PassClassFilter passFilter; - AZStd::vector cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!cubeMapPasses.empty()) - { - usageFlags |= RPI::View::UsageReflectiveCubeMap; - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + passFilter.SetOwenrScene(GetParentScene()); // only handles passes for this scene + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&usageFlags]([[maybe_unused]] RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + usageFlags |= RPI::View::UsageReflectiveCubeMap; + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); segment.m_view = RPI::View::CreateView(viewName, usageFlags); } diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp index c085b26e31..453dbbc0ab 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp @@ -80,35 +80,48 @@ namespace AZ } // update the size multiplier on the DiffuseProbeGridDownsamplePass output - AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; - RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy); - const AZStd::vector& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter); - for (RPI::Pass* pass : downsamplePasses) + // NOTE: The ownerScene wasn't added to both filters. This is because the passes from the non-owner scene may have invalid SRG values which could lead to + // GPU error if the scene doesn't have this feature processor enabled. + // For example, the ASV MultiScene sample may have TDR. { - for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) - { - RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; - RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; + AZStd::vector downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") }; + RPI::PassFilter downsamplePassFilter = RPI::PassFilter::CreateWithPassHierarchy(downsamplePassHierarchy); + RPI::PassSystemInterface::Get()->ForEachPass( + downsamplePassFilter, + [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex) + { + RPI::Ptr outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment; + RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers; - sizeMultipliers.m_widthMultiplier = sizeMultiplier; - sizeMultipliers.m_heightMultiplier = sizeMultiplier; - } + sizeMultipliers.m_widthMultiplier = sizeMultiplier; + sizeMultipliers.m_heightMultiplier = sizeMultiplier; + } - // set the output scale on the PassSrg - RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); - auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale")); - downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + // set the output scale on the PassSrg + RPI::FullscreenTrianglePass* downsamplePass = static_cast(pass); + RHI::ShaderInputNameIndex outputImageScaleShaderInput = "m_outputImageScale"; + downsamplePass->GetShaderResourceGroup()->SetConstant( + outputImageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + // handle all downsample passes + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } // update the image scale on the DiffuseComposite pass - AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; - RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy); - const AZStd::vector& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter); - for (RPI::Pass* pass : compositePasses) { - RPI::FullscreenTrianglePass* compositePass = static_cast(pass); - auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale")); - compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast(1.0f / sizeMultiplier)); + AZStd::vector compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") }; + RPI::PassFilter compositePassFilter = RPI::PassFilter::CreateWithPassHierarchy(compositePassHierarchy); + RPI::PassSystemInterface::Get()->ForEachPass(compositePassFilter, [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + RPI::FullscreenTrianglePass* compositePass = static_cast(pass); + RHI::ShaderInputNameIndex imageScaleShaderInput = "m_imageScale"; + compositePass->GetShaderResourceGroup()->SetConstant(imageScaleShaderInput, aznumeric_cast(1.0f / sizeMultiplier)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } } // namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 4329fd556c..5ea4748fc9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -603,12 +603,12 @@ namespace AZ RHI::Ptr device = RHI::RHISystemInterface::Get()->GetDevice(); if (device->GetFeatures().m_rayTracing == false) { - RPI::PassHierarchyFilter updatePassFilter(AZ::Name("DiffuseProbeGridUpdatePass")); - const AZStd::vector& updatePasses = RPI::PassSystemInterface::Get()->FindPasses(updatePassFilter); - for (RPI::Pass* pass : updatePasses) - { - pass->SetEnabled(false); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("DiffuseProbeGridUpdatePass"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(false); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp index 3ec8840a1c..5b5599383e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp @@ -10,9 +10,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -66,22 +67,14 @@ namespace AZ { // Need to invalidate the CopyToSwapChain pass so that it updates the pipeline state in the event that // the swapchain format changed (for example, moving from LDR to HDR display) - auto* passSystem = RPI::PassSystemInterface::Get(); - const Name fullscreenCopyTemplateName("FullscreenCopyTemplate"); - - if (passSystem->HasPassesForTemplateName(fullscreenCopyTemplateName)) - { - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(fullscreenCopyTemplateName); - for (RPI::Pass* pass : passes) + const Name copyToSwapChainPassName("CopyToSwapChain"); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(copyToSwapChainPassName, GetRenderPipeline()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - RPI::FullscreenTrianglePass* fullscreenTrianglePass = azrtti_cast(pass); - const Name& passName = fullscreenTrianglePass->GetName(); - if (passName.GetStringView() == "CopyToSwapChain") - { - fullscreenTrianglePass->QueueForInitialization(); - } - } - } + pass->QueueForInitialization(); + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); + ConfigureDisplayParameters(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 8c0360dec2..ed2d8a1d9f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -372,29 +372,25 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - RPI::PassClassFilter passFilter; - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - - if (foundPasses.size() == 0) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(RPI::PassSystemInterface::Get()->FindFirstPass(passFilter)); + if (!previewPass) { - AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass pass "); + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass"); return false; } - AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast(foundPasses[0]); bool result = previewPass->ReadbackOutput(m_readback); if (result) { m_state = State::Pending; m_result = FrameCaptureResult::None; SystemTickBus::Handler::BusConnect(); + return true; } - else - { - AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");; - } - return result; + + AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass"); + return false; } bool FrameCaptureSystemComponent::CapturePassAttachment(const AZStd::vector& passHierarchy, const AZStd::string& slot, @@ -405,6 +401,12 @@ namespace AZ return false; } + if (passHierarchy.size() == 0) + { + AZ_Warning("FrameCaptureSystemComponent", false, "Empty data in passHierarchy"); + return false; + } + InitReadback(); if (m_state != State::Idle) @@ -426,17 +428,15 @@ namespace AZ } m_latestCaptureInfo.clear(); - // Find the pass first - AZ::RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchy); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); - if (foundPasses.size() == 0) + if (!pass) { - AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str()); + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passHierarchy[0].c_str()); return false; } - AZ::RPI::Pass* pass = foundPasses[0]; if (pass->ReadbackAttachment(m_readback, Name(slot), option)) { m_state = State::Pending; @@ -444,6 +444,7 @@ namespace AZ SystemTickBus::Handler::BusConnect(); return true; } + AZ_Warning("FrameCaptureSystemComponent", false, "Failed to readback the attachment bound to pass [%s] slot [%s]", pass->GetName().GetCStr(), slot.c_str()); return false; } diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp index afca20c5cb..3783752e67 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp @@ -109,15 +109,15 @@ namespace AZ void ImGuiSystemComponent::ForAllImGuiPasses(PassFunction func) { ImGuiContext* contextToRestore = ImGui::GetCurrentContext(); - RPI::PassClassFilter filter; - auto imguiPasses = RPI::PassSystemInterface::Get()->FindPasses(filter); - - for (RPI::Pass* pass : imguiPasses) - { - ImGuiPass* imguiPass = azrtti_cast(pass); - ImGui::SetCurrentContext(imguiPass->GetContext()); - func(imguiPass); - } + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [func](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + ImGuiPass* imguiPass = azrtti_cast(pass); + ImGui::SetCurrentContext(imguiPass->GetContext()); + func(imguiPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); ImGui::SetCurrentContext(contextToRestore); } @@ -169,29 +169,37 @@ namespace AZ return false; } - bool ImGuiSystemComponent::PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchyFilter) + bool ImGuiSystemComponent::PushActiveContextFromPass(const AZStd::vector& passHierarchyFilter) { - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passHierarchyFilter); + if (passHierarchyFilter.size() == 0) + { + AZ_Warning("ImGuiSystemComponent", false, "passHierarchyFilter is empty"); + return false; + } + AZStd::vector foundImGuiPasses; - for (RPI::Pass* pass : foundPasses) - { - ImGuiPass* imGuiPass = azrtti_cast(pass); - if (imGuiPass) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchyFilter); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&foundImGuiPasses](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - foundImGuiPasses.push_back(imGuiPass); - } - } + ImGuiPass* imGuiPass = azrtti_cast(pass); + if (imGuiPass) + { + foundImGuiPasses.push_back(imGuiPass); + } + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); if (foundImGuiPasses.size() == 0) { - AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter.ToString().c_str()); + AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter[0].c_str()); return false; } if (foundImGuiPasses.size() > 1) { - AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter.ToString().c_str()); + AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter[0].c_str()); } ImGuiContext* context = foundImGuiPasses.at(0)->GetContext(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h index 838cf0b3c2..d59124890f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h @@ -56,7 +56,7 @@ namespace AZ ImGuiPass* GetDefaultImGuiPass() override; bool PushActiveContextFromDefaultPass() override; - bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) override; + bool PushActiveContextFromPass(const AZStd::vector& passHierarchy) override; bool PopActiveContext() override; ImGuiContext* GetActiveContext() override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp index fe1ce8a281..98b527868d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -259,24 +260,19 @@ namespace AZ // [GFX TODO][ATOM-3035]This function is temporary and will change with improvement to the draw list tag system void DepthOfFieldSettings::UpdateAutoFocusDepth(bool enabled) - { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); + { const Name TemplateNameReadBackFocusDepth = Name("DepthOfFieldReadBackFocusDepthTemplate"); - if (passSystem->HasPassesForTemplateName(TemplateNameReadBackFocusDepth)) - { - const AZStd::vector& dofPasses = passSystem->GetPassesForTemplateName(TemplateNameReadBackFocusDepth); - for (RPI::Pass* pass : dofPasses) + // [GFX TODO][ATOM-4908] multiple camera should be distingushed. + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(TemplateNameReadBackFocusDepth, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, enabled](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* dofPass = azrtti_cast(pass); - // Check this pass belongs to a render pipeline of the scene. - // [GFX TODO][ATOM-4908] multiple camera should be distingushed. - const RPI::RenderPipelineId pipelineId = dofPass->GetRenderPipeline()->GetId(); - if (enabled && GetParentScene()->GetRenderPipeline(pipelineId)) + if (enabled) { m_normalizedFocusDistanceForAutoFocus = dofPass->GetNormalizedFocusDistanceForAutoFocus(); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void DepthOfFieldSettings::SetCameraEntityId(EntityId cameraEntityId) diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index 96ca424307..26c2a61d54 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -188,21 +189,21 @@ namespace AZ void ExposureControlSettings::UpdateLuminanceHeatmap() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - // [GFX-TODO][ATOM-13194] Support multiple views for the luminance heatmap - // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass - const RPI::Ptr luminanceHeatmap = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHeatmapNameId); - if (luminanceHeatmap) - { - luminanceHeatmap->SetEnabled(m_heatmapEnabled); - } + // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass + RPI::PassFilter heatmapPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHeatmapNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(heatmapPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(m_heatmapEnabled); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); - const RPI::Ptr histogramGenerator = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHistogramGeneratorNameId); - if (histogramGenerator) - { - histogramGenerator->SetEnabled(m_heatmapEnabled); - } + RPI::PassFilter histogramPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHistogramGeneratorNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(histogramPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + pass->SetEnabled(m_heatmapEnabled); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ExposureControlSettings::UpdateBuffer() diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp index 85c45de96b..aac3d1d941 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp @@ -31,12 +31,14 @@ namespace AZ 0, [](const uint8_t& value) { - auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter()); - for (auto* pass : passes) - { - LookModificationCompositePass* lookModPass = azrtti_cast(pass); - lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); - } + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass(); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [value](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + LookModificationCompositePass* lookModPass = azrtti_cast(pass); + lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); + + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); }, ConsoleFunctorFlags::Null, "This can be increased to deal with particularly tricky luts. Range (0-2). 0 (default) - Standard linear sampling. 1 - 7 tap b-spline sampling. 2 - 19 tap b-spline sampling." diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index eef0c51e95..ad059fbec0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -71,26 +72,18 @@ namespace AZ void SMAAFeatureProcessor::UpdateConvertToPerceptualPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId)) - { - const AZStd::vector& convertToPerceptualColorPasses = passSystem->GetPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId); - for (RPI::Pass* pass : convertToPerceptualColorPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_convertToPerceptualColorPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { pass->SetEnabled(m_data.m_enable); - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateEdgeDetectionPass() - { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_edgeDetectioPassTemplateNameId)) - { - const AZStd::vector& edgeDetectionPasses = passSystem->GetPassesForTemplateName(m_edgeDetectioPassTemplateNameId); - for (RPI::Pass* pass : edgeDetectionPasses) + { + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_edgeDetectioPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* edgeDetectionPass = azrtti_cast(pass); @@ -106,18 +99,14 @@ namespace AZ edgeDetectionPass->SetPredicationScale(m_data.m_predicationScale); edgeDetectionPass->SetPredicationStrength(m_data.m_predicationStrength); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateBlendingWeightCalculationPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId)) - { - const AZStd::vector& blendingWeightCalculationPasses = passSystem->GetPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId); - for (RPI::Pass* pass : blendingWeightCalculationPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_blendingWeightCalculationPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* blendingWeightCalculationPass = azrtti_cast(pass); @@ -130,18 +119,14 @@ namespace AZ blendingWeightCalculationPass->SetDiagonalDetectionEnable(m_data.m_enableDiagonalDetection); blendingWeightCalculationPass->SetCornerDetectionEnable(m_data.m_enableCornerDetection); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::UpdateNeighborhoodBlendingPass() { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - - if (passSystem->HasPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId)) - { - const AZStd::vector& neighborhoodBlendingPasses = passSystem->GetPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId); - for (RPI::Pass* pass : neighborhoodBlendingPasses) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_neighborhoodBlendingPassTemplateNameId, GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { auto* neighborhoodBlendingPass = azrtti_cast(pass); @@ -153,8 +138,8 @@ namespace AZ { neighborhoodBlendingPass->SetOutputMode(SMAAOutputMode::PassThrough); } - } - } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet) diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 66adfe9985..9bfb2b7e9e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -377,14 +377,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector passes = FindPasses({ "Root" }); - if (passes.empty()) - { - return false; - } - - RPI::Pass* root = passes[0]; + RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get(); // Enable all the Timestamp queries in passes. root->SetTimestampQueryEnabled(true); @@ -465,14 +458,7 @@ namespace AZ bool ProfilingCaptureSystemComponent::CapturePassPipelineStatistics(const AZStd::string& outputFilePath) { - // Find the root pass. - AZStd::vector passes = FindPasses({ "Root" }); - if (passes.empty()) - { - return false; - } - - RPI::Pass* root = passes[0]; + RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get(); // Enable all the PipelineStatistics queries in passes. root->SetPipelineStatisticsQueryEnabled(true); @@ -572,19 +558,6 @@ namespace AZ return passes; } - AZStd::vector ProfilingCaptureSystemComponent::FindPasses(AZStd::vector&& passHierarchy) const - { - // Find the pass first. - RPI::PassHierarchyFilter passFilter(passHierarchy); - AZStd::vector foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (foundPasses.size() == 0) - { - AZ_Warning("ProfilingCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str()); - } - - return foundPasses; - } - void ProfilingCaptureSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) { // Update the delayed captures diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h index a9bb8c585f..af1d2f5643 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h @@ -78,8 +78,6 @@ namespace AZ // Recursively collect all the passes from the root pass. AZStd::vector CollectPassesRecursively(const RPI::Pass* root) const; - AZStd::vector FindPasses(AZStd::vector&& passHierarchy) const; - DelayedQueryCaptureHelper m_timestampCapture; DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture; DelayedQueryCaptureHelper m_pipelineStatisticsCapture; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp index 37b1885ee3..a1858a7e9d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp @@ -28,16 +28,17 @@ namespace AZ void ReflectionCopyFrameBufferPass::BuildInternal() { - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); - Data::Instance& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment(); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(pass); + Data::Instance& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment(); - RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0); - AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); - } + RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0); + AttachImageToSlot(outputBinding.m_name, frameBufferAttachment); + + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); FullscreenTrianglePass::BuildInternal(); } diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp index 394a6fd406..edd7ad1013 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp @@ -150,7 +150,7 @@ namespace AZ auto transientImageDesc = RHI::ImageDescriptor::Create2D(imageBindFlags, mipSize.m_width, mipSize.m_height, RHI::Format::R16G16B16A16_FLOAT); RPI::PassAttachment* transientPassAttachment = aznew RPI::PassAttachment(); - AZStd::string transientAttachmentName = AZStd::string::format("ReflectionScreenSpace_BlurImage%d", mip); + AZStd::string transientAttachmentName = AZStd::string::format("%s.ReflectionScreenSpace_BlurImage%d", GetPathName().GetCStr(), mip); transientPassAttachment->m_name = transientAttachmentName; transientPassAttachment->m_path = transientAttachmentName; transientPassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp index 1cf227650c..1362191691 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp @@ -33,20 +33,22 @@ namespace AZ return; } - RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass")); - const AZStd::vector& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter); - if (!passes.empty()) - { - Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(passes.front()); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline()); - // compute the max mip level based on the available mips in the previous frame image, and capping it - // to stay within a range that has reasonable data - const uint32_t MaxNumRoughnessMips = 8; - uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow + { + Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast(pass); - auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); - m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); - } + // compute the max mip level based on the available mips in the previous frame image, and capping it + // to stay within a range that has reasonable data + const uint32_t MaxNumRoughnessMips = 8; + uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1; + + auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel")); + m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel); + + return RPI::PassFilterExecutionFlow::StopVisitingPasses; + }); FullscreenTrianglePass::CompileResources(context); } diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 7c0b3563c7..70ccaa5702 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -313,52 +313,38 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::CachePasses() { - const AZStd::vector validPipelineIds = CacheProjectedShadowmapsPass(); - CacheEsmShadowmapsPass(validPipelineIds); + CacheProjectedShadowmapsPass(); + CacheEsmShadowmapsPass(); m_shadowmapPassNeedsUpdate = true; } - AZStd::vector ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() + void ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass() { - const AZStd::vector& renderPipelines = GetParentScene()->GetRenderPipelines(); - const auto* passSystem = RPI::PassSystemInterface::Get();; - const AZStd::vector& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate")); - - AZStd::vector validPipelineIds; m_projectedShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - ProjectedShadowmapsPass* shadowPass = static_cast(pass); - for (const RPI::RenderPipelinePtr& pipeline : renderPipelines) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("ProjectedShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - if (pipeline.get() == shadowPass->GetRenderPipeline()) - { - m_projectedShadowmapsPasses.emplace_back(shadowPass); - validPipelineIds.push_back(shadowPass->GetRenderPipeline()->GetId()); - } - } - } - return validPipelineIds; + ProjectedShadowmapsPass* shadowPass = static_cast(pass); + m_projectedShadowmapsPasses.emplace_back(shadowPass); + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } - void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds) + void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass() { const Name LightTypeName = Name("projected"); - - const auto* passSystem = RPI::PassSystemInterface::Get(); - const AZStd::vector passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate")); - + m_esmShadowmapsPasses.clear(); - for (RPI::Pass* pass : passes) - { - EsmShadowmapsPass* esmPass = static_cast(pass); - if (esmPass->GetRenderPipeline() && - AZStd::find(validPipelineIds.begin(), validPipelineIds.end(), esmPass->GetRenderPipeline()->GetId()) != validPipelineIds.end() && - esmPass->GetLightTypeName() == LightTypeName) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene()); + RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, LightTypeName](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow { - m_esmShadowmapsPasses.emplace_back(esmPass); - } - } + EsmShadowmapsPass* esmPass = static_cast(pass); + if (esmPass->GetLightTypeName() == LightTypeName) + { + m_esmShadowmapsPasses.emplace_back(esmPass); + } + return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + }); } void ProjectedShadowFeatureProcessor::UpdateFilterParameters() diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index fafcb25a08..8939f1845d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -97,8 +97,8 @@ namespace AZ::Render // Functions for caching the ProjectedShadowmapsPass and EsmShadowmapsPass. void CachePasses(); - AZStd::vector CacheProjectedShadowmapsPass(); - void CacheEsmShadowmapsPass(const AZStd::vector& validPipelineIds); + void CacheProjectedShadowmapsPass(); + void CacheEsmShadowmapsPass(); //! Functions to update the parameter of Gaussian filter used in ESM. void UpdateFilterParameters(); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index e0209702dc..4c379c4239 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -241,12 +242,12 @@ namespace AZ void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) { - InitSkinningAndMorphPass(pipeline->GetRootPass()); + InitSkinningAndMorphPass(pipeline.get()); } void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { - InitSkinningAndMorphPass(renderPipeline->GetRootPass()); + InitSkinningAndMorphPass(renderPipeline); } void SkinnedMeshFeatureProcessor::OnBeginPrepareRender() @@ -289,9 +290,10 @@ namespace AZ return false; } - void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass) + void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline) { - RPI::Ptr skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" }); + RPI::PassFilter skinPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "SkinningPass" }, renderPipeline); + RPI::Ptr skinningPass = RPI::PassSystemInterface::Get()->FindFirstPass(skinPassFilter); if (skinningPass) { SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast(skinningPass.get()); @@ -310,7 +312,8 @@ namespace AZ } } - RPI::Ptr morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" }); + RPI::PassFilter morphPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "MorphTargetPass" }, renderPipeline); + RPI::Ptr morphTargetPass = RPI::PassSystemInterface::Get()->FindFirstPass(morphPassFilter); if (morphTargetPass) { MorphTargetComputePass* morphTargetComputePass = azdynamic_cast(morphTargetPass.get()); diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h index 2e93acf2cd..5b7ab943e1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h @@ -66,7 +66,7 @@ namespace AZ private: AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor); - void InitSkinningAndMorphPass(const RPI::Ptr pipelineRootPass); + void InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline); SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override; bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override; diff --git a/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.azsl b/Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.azsl similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.azsl rename to Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.azsl diff --git a/Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader b/Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.shader similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/DecomposeMsImage.shader rename to Gems/Atom/RPI/Assets/Shaders/DecomposeMsImage.shader diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.azsl b/Gems/Atom/RPI/Assets/Shaders/ImagePreview.azsl similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/ImagePreview.azsl rename to Gems/Atom/RPI/Assets/Shaders/ImagePreview.azsl diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.shader b/Gems/Atom/RPI/Assets/Shaders/ImagePreview.shader similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/ImagePreview.shader rename to Gems/Atom/RPI/Assets/Shaders/ImagePreview.shader diff --git a/Gems/Atom/RPI/Assets/Shader/ImagePreview.shadervariantlist b/Gems/Atom/RPI/Assets/Shaders/ImagePreview.shadervariantlist similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/ImagePreview.shadervariantlist rename to Gems/Atom/RPI/Assets/Shaders/ImagePreview.shadervariantlist diff --git a/Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.azsl b/Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.azsl similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.azsl rename to Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.azsl diff --git a/Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.shader b/Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.shader similarity index 100% rename from Gems/Atom/RPI/Assets/Shader/SceneAndViewSrgs.shader rename to Gems/Atom/RPI/Assets/Shaders/SceneAndViewSrgs.shader diff --git a/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake b/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake index 9e89427a70..40478da961 100644 --- a/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake +++ b/Gems/Atom/RPI/Assets/atom_rpi_asset_files.cmake @@ -7,10 +7,10 @@ # set(FILES - Shader/DecomposeMsImage.azsl - Shader/DecomposeMsImage.shader - Shader/ImagePreview.azsl - Shader/ImagePreview.shader + Shaders/DecomposeMsImage.azsl + Shaders/DecomposeMsImage.shader + Shaders/ImagePreview.azsl + Shaders/ImagePreview.shader ShaderLib/Atom/RPI/Math.azsli ShaderLib/Atom/RPI/TangentSpace.azsli ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 36994ec03b..6523f0a6d8 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -68,9 +68,6 @@ namespace AZ template Ptr FindChildPass() const; - //! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found. - Ptr FindPassByNameRecursive(const Name& passName) const; - //! Gets the list of children. Useful for validating hierarchies AZStd::array_view> GetChildren() const; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index 34eaae4495..e7b9825ecb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -139,6 +139,10 @@ namespace AZ //! Returns the number of output attachment bindings uint32_t GetOutputCount() const; + //! Returns the pass template which was used for create this pass. + //! It may return nullptr if the pass wasn't create from a template + const PassTemplate* GetPassTemplate() const; + //! Enable/disable this pass //! If the pass is disabled, it (and any children if it's a ParentPass) won't be rendered. void SetEnabled(bool enabled); diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h index c31f353adb..c42991725e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h @@ -16,95 +16,85 @@ namespace AZ { namespace RPI { - // A base class for a filter which can be used to filter passes + class Scene; + class RenderPipeline; + class PassFilter { public: - //! Whether the input pass matches with the filter - virtual bool Matches(const Pass* pass) const = 0; + static PassFilter CreateWithPassName(Name passName, const Scene* scene); + static PassFilter CreateWithPassName(Name passName, const RenderPipeline* renderPipeline); - //! Return the pass' name if a pass name is used for the filter. - //! Return nullptr if the filter doesn't have pass name used for matching - virtual const Name* GetPassName() const = 0; + //! Create a PassFilter with pass hierarchy information + //! Filter for passes which have a matching name and also with ordered parents. + //! For example, if the filter is initialized with + //! pass name: "ShadowPass1" + //! pass parents names: "MainPipeline", "Shadow" + //! Passes with these names match the filter: + //! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" + //! or "Root.MainPipeline.Shadow.ShadowPass1" + //! or "MainPipeline.Shadow.Group1.ShadowPass1" + //! + //! Passes with these names wont match: + //! "MainPipeline.ShadowPass1" + //! or "Shadow.MainPipeline.ShadowPass1" + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithPassHierarchy(const AZStd::vector& passHierarchy); + static PassFilter CreateWithTemplateName(Name templateName, const Scene* scene); + static PassFilter CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline); + template + static PassFilter CreateWithPassClass(); - //! Return this filter's info as a string - virtual AZStd::string ToString() const = 0; - }; + enum FilterOptions : uint32_t + { + Empty = 0, + PassName = AZ_BIT(0), + PassTemplateName = AZ_BIT(1), + PassClass = AZ_BIT(2), + PassHierarchy = AZ_BIT(3), + OwnerScene = AZ_BIT(4), + OwnerRenderPipeline = AZ_BIT(5) + }; - //! Filter for passes which have a matching name and also with ordered parents. - //! For example, if the filter is initialized with - //! pass name: "ShadowPass1" - //! pass parents names: "MainPipeline", "Shadow" - //! Passes with these names match the filter: - //! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" - //! or "Root.MainPipeline.Shadow.ShadowPass1" - //! or "MainPipeline.Shadow.Group1.ShadowPass1" - //! - //! Passes with these names wont match: - //! "MainPipeline.ShadowPass1" - //! or "Shadow.MainPipeline.ShadowPass1" - class PassHierarchyFilter - : public PassFilter - { - public: - AZ_RTTI(PassHierarchyFilter, "{478F169F-BA97-4321-AC34-EDE823997159}", PassFilter); - AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0); + void SetOwenrScene(const Scene* scene); + void SetOwenrRenderPipeline(const RenderPipeline* renderPipeline); + void SetPassName(Name passName); + void SetTemplateName(Name passTemplateName); + void SetPassClass(TypeId passClassTypeId); - //! Construct filter with only pass name. - PassHierarchyFilter(const Name& passName); + const Name& GetPassName() const; + const Name& GetPassTemplateName() const; - virtual ~PassHierarchyFilter() = default; + uint32_t GetEnabledFilterOptions() const; - //! Construct filter with pass name and its parents' names in the order of the hierarchy - //! This means k-th element is always an ancestor of the (k-1)-th element. - //! And the last element is the pass name. - PassHierarchyFilter(const AZStd::vector& passHierarchy); - PassHierarchyFilter(const AZStd::vector& passHierarchy); + //! Return true if the input pass matches the filter + bool Matches(const Pass* pass) const; - // PassFilter overrides... - bool Matches(const Pass* pass) const override; - const Name* GetPassName() const override; - AZStd::string ToString() const override; + //! Return true if the input pass matches the filter with selected filter options + //! The input filter options should be a subset of options returned by GetEnabledFilterOptions() + //! This function is used to avoid extra checks for passes which was already filtered. + //! Check PassLibrary::ForEachPass() function's implementation for more details + bool Matches(const Pass* pass, uint32_t options) const; private: - PassHierarchyFilter() = delete; + void UpdateFilterOptions(); - AZStd::vector m_parentNames; Name m_passName; + Name m_templateName; + TypeId m_passClassTypeId = TypeId::CreateNull(); + AZStd::vector m_parentNames; + const RenderPipeline* m_ownerRenderPipeline = nullptr; + const Scene* m_ownerScene = nullptr; + uint32_t m_filterOptions = 0; }; - //! Filter for passes based on their class. - template - class PassClassFilter - : public PassFilter - { - public: - AZ_RTTI(PassClassFilter, "{AF6E3AD5-433A-462A-997A-F36D8A551D02}", PassFilter); - AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0); - PassClassFilter() = default; - - // PassFilter overrides... - bool Matches(const Pass* pass) const override; - const Name* GetPassName() const override; - AZStd::string ToString() const override; - }; - - template - bool PassClassFilter::Matches(const Pass* pass) const - { - return pass->RTTI_IsTypeOf(PassClass::RTTI_Type()); - } - - template - const Name* PassClassFilter::GetPassName() const - { - return nullptr; - } - - template - AZStd::string PassClassFilter::ToString() const - { - return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName()); + template + PassFilter PassFilter::CreateWithPassClass() + { + PassFilter filter; + filter.m_passClassTypeId = PassClass::RTTI_Type(); + filter.UpdateFilterOptions(); + return filter; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h index 0a4b1c4399..66c3c205ab 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h @@ -84,8 +84,8 @@ namespace AZ bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath); bool LoadPassTemplateMappings(Data::Asset mappingAsset); - //! Returns a list of passes found in the pass name mapping using the provided pass filter - AZStd::vector FindPasses(const PassFilter& passFilter) const; + //! Visit each pass which matches the filter + void ForEachPass(const PassFilter& passFilter, AZStd::function passFunction); private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h index 30fa27e64b..8390b0f7e2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h @@ -92,13 +92,13 @@ namespace AZ // PassSystemInterface library related functions... bool HasPassesForTemplateName(const Name& templateName) const override; - const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const override; bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) override; const AZStd::shared_ptr GetPassTemplate(const Name& name) const override; void RemovePassFromLibrary(Pass* pass) override; void RegisterPass(Pass* pass) override; void UnregisterPass(Pass* pass) override; - AZStd::vector FindPasses(const PassFilter& passFilter) const override; + void ForEachPass(const PassFilter& filter, AZStd::function passFunction) override; + Pass* FindFirstPass(const PassFilter& filter) override; private: // Returns the root of the pass tree hierarchy diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h index 0e9386f3bb..7f944df88f 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h @@ -75,6 +75,13 @@ namespace AZ u32 m_maxDrawItemsRenderedInAPass = 0; }; + + enum PassFilterExecutionFlow : uint8_t + { + StopVisitingPasses, + ContinueVisitingPasses, + }; + class PassSystemInterface { friend class Pass; @@ -186,9 +193,6 @@ namespace AZ //! Returns true if the pass factory contains passes created with the given template name virtual bool HasPassesForTemplateName(const Name& templateName) const = 0; - //! Get the passes created with the given template name. - virtual const AZStd::vector& GetPassesForTemplateName(const Name& templateName) const = 0; - //! Adds a PassTemplate to the library virtual bool AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) = 0; @@ -197,9 +201,16 @@ namespace AZ //! Removes all references to the given pass from the pass library virtual void RemovePassFromLibrary(Pass* pass) = 0; + + //! Visit the matching passes from registered passes with specified filter + //! The return value of the passFunction decides if the search continues or not + //! Note: this function will find all the passes which match the pass filter even they are for render pipelines which are not added to a scene + //! This function is fast if a pass name or a pass template name is specified. + virtual void ForEachPass(const PassFilter& filter, AZStd::function passFunction) = 0; - //! Find matching passes from registered passes with specified filter - virtual AZStd::vector FindPasses(const PassFilter& passFilter) const = 0; + //! Find the first matching pass from registered passes with specified filter + //! Note: this function SHOULD ONLY be used when you are certain you only need to handle the first pass found + virtual Pass* FindFirstPass(const PassFilter& filter) = 0; private: // These functions are only meant to be used by the Pass class diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h index 1b7b32c1e9..7f57b30326 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/RPISystemDescriptor.h @@ -32,7 +32,7 @@ namespace AZ //! The asset cache relative path of the only common shader asset for the RPI system that is used //! as means to load the layout for scene srg and view srg. This is used to create any RPI::Scene. - AZStd::string m_commonSrgsShaderAssetPath = "shader/sceneandviewsrgs.azshader"; + AZStd::string m_commonSrgsShaderAssetPath = "shaders/sceneandviewsrgs.azshader"; ImageSystemDescriptor m_imageSystemDescriptor; GpuQuerySystemDescriptor m_gpuQuerySystemDescriptor; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp index c19ae565d0..890e87a7a5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp @@ -121,7 +121,7 @@ namespace AZ m_fence->Init(*device, RHI::FenceState::Reset); // Load shader and srg - const char* ShaderPath = "shader/decomposemsimage.azshader"; + const char* ShaderPath = "shaders/decomposemsimage.azshader"; m_decomposeShader = LoadCriticalShader(ShaderPath); if (m_decomposeShader == nullptr) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp index 36c28877ea..dccf5dbc2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -149,29 +149,6 @@ namespace AZ return index.IsValid() ? m_children[index.GetIndex()] : Ptr(nullptr); } - Ptr ParentPass::FindPassByNameRecursive(const Name& passName) const - { - for (const Ptr& child : m_children) - { - if (child->GetName() == passName) - { - return child.get(); - } - - ParentPass* asParent = child->AsParent(); - if (asParent) - { - auto pass = asParent->FindPassByNameRecursive(passName); - if (pass) - { - return pass; - } - } - } - - return nullptr; - } - const Pass* ParentPass::FindPass(RHI::DrawListTag drawListTag) const { if (HasDrawListTag() && GetDrawListTag() == drawListTag) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index a8d94e9a91..3c1de28d6a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -238,6 +238,11 @@ namespace AZ return m_attachmentBindings[bindingIndex]; } + const PassTemplate* Pass::GetPassTemplate() const + { + return m_template.get(); + } + void Pass::AddAttachmentBinding(PassAttachmentBinding attachmentBinding) { // Add the index of the binding to the input, output or input/output list based on the slot type diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp index 7bd1abc0a7..d9e458c615 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp @@ -8,101 +8,264 @@ #include #include +#include namespace AZ { namespace RPI { - PassHierarchyFilter::PassHierarchyFilter(const Name& passName) + PassFilter PassFilter::CreateWithPassName(Name passName, const Scene* scene) + { + PassFilter filter; + filter.m_passName = passName; + filter.m_ownerScene = scene; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassName(Name passName, const RenderPipeline* renderPipeline) + { + PassFilter filter; + filter.m_passName = passName; + filter.m_ownerRenderPipeline = renderPipeline; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithTemplateName(Name templateName, const Scene* scene) + { + PassFilter filter; + filter.m_templateName = templateName; + filter.m_ownerScene = scene; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline) + { + PassFilter filter; + filter.m_templateName = templateName; + filter.m_ownerRenderPipeline = renderPipeline; + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector& passHierarchy) + { + PassFilter filter; + if (passHierarchy.size() == 0) + { + AZ_Assert(false, "passHierarchy should have at least one element"); + return filter; + } + + filter.m_passName = passHierarchy.back(); + + filter.m_parentNames.resize(passHierarchy.size() - 1); + for (uint32_t index = 0; index < filter.m_parentNames.size(); index++) + { + filter.m_parentNames[index] = passHierarchy[index]; + } + filter.UpdateFilterOptions(); + return filter; + } + + PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector& passHierarchy) + { + PassFilter filter; + if (passHierarchy.size() == 0) + { + AZ_Assert(false, "passHierarchy should have at least one element"); + return filter; + } + + filter.m_passName = Name(passHierarchy.back()); + + filter.m_parentNames.resize(passHierarchy.size() - 1); + for (uint32_t index = 0; index < filter.m_parentNames.size(); index++) + { + filter.m_parentNames[index] = Name(passHierarchy[index]); + } + filter.UpdateFilterOptions(); + return filter; + } + + void PassFilter::SetOwenrScene(const Scene* scene) + { + m_ownerScene = scene; + UpdateFilterOptions(); + } + + void PassFilter::SetOwenrRenderPipeline(const RenderPipeline* renderPipeline) + { + m_ownerRenderPipeline = renderPipeline; + UpdateFilterOptions(); + } + + void PassFilter::SetPassName(Name passName) { m_passName = passName; + UpdateFilterOptions(); } - PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector& passHierarchy) + void PassFilter::SetTemplateName(Name passTemplateName) { - if (passHierarchy.size() == 0) - { - AZ_Assert(false, "passHierarchy should have at least one element"); - return; - } - - m_passName = Name(passHierarchy.back()); - - m_parentNames.resize(passHierarchy.size() - 1); - for (uint32_t index = 0; index < m_parentNames.size(); index++) - { - m_parentNames[index] = Name(passHierarchy[index]); - } + m_templateName = passTemplateName; + UpdateFilterOptions(); } - PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector& passHierarchy) + void PassFilter::SetPassClass(TypeId passClassTypeId) { - if (passHierarchy.size() == 0) - { - AZ_Assert(false, "passHierarchy should have at least one element"); - return; - } - - m_passName = passHierarchy.back(); - - m_parentNames.resize(passHierarchy.size() - 1); - for (uint32_t index = 0; index < m_parentNames.size(); index++) - { - m_parentNames[index] = passHierarchy[index]; - } + m_passClassTypeId = passClassTypeId; + UpdateFilterOptions(); } - bool PassHierarchyFilter::Matches(const Pass* pass) const + const Name& PassFilter::GetPassName() const { - if (pass->GetName() != m_passName) + return m_passName; + } + + const Name& PassFilter::GetPassTemplateName() const + { + return m_templateName; + } + + uint32_t PassFilter::GetEnabledFilterOptions() const + { + return m_filterOptions; + } + + bool PassFilter::Matches(const Pass* pass) const + { + return Matches(pass, m_filterOptions); + } + + bool PassFilter::Matches(const Pass* pass, uint32_t options) const + { + AZ_Assert( (options&m_filterOptions) == options, "options should be a subset of m_filterOptions"); + + // return false if the pass doesn't have a pass template or the template's name is not matching + if (options & FilterOptions::PassTemplateName && (!pass->GetPassTemplate() || pass->GetPassTemplate()->m_name != m_templateName)) { return false; } - ParentPass* parent = pass->GetParent(); - - // search from the back of the array with the most close parent - for (int32_t index = static_cast(m_parentNames.size() - 1); index >= 0; index--) + if ((options & FilterOptions::PassName) && pass->GetName() != m_passName) { - const Name& parentName = m_parentNames[index]; - while (parent) - { - if (parent->GetName() == parentName) - { - break; - } - parent = parent->GetParent(); - } + return false; + } - // if parent is nullptr the it didn't find a parent has matching current parentName - if (!parent) + if ((options & FilterOptions::PassClass) && pass->RTTI_GetType() != m_passClassTypeId) + { + return false; + } + + if ((options & FilterOptions::OwnerRenderPipeline) && m_ownerRenderPipeline != pass->GetRenderPipeline()) + { + return false; + } + + // If the owner render pipeline was checked, the owner scene check can be skipped + if (options & FilterOptions::OwnerScene) + { + if (pass->GetRenderPipeline()) { + // return false if the owner scene doesn't match + if (m_ownerScene != pass->GetRenderPipeline()->GetScene()) + { + return false; + } + } + else + { + // return false if the pass doesn't have an owner scene return false; } + } - // move to next parent - parent = parent->GetParent(); + if ((options & FilterOptions::PassHierarchy)) + { + // Filter for passes which have a matching name and also with ordered parents. + // For example, if the filter is initialized with + // pass name: "ShadowPass1" + // pass parents names: "MainPipeline", "Shadow" + // Passes with these names match the filter: + // "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1" + // or "Root.MainPipeline.Shadow.ShadowPass1" + // or "MainPipeline.Shadow.Group1.ShadowPass1" + // + // Passes with these names wont match: + // "MainPipeline.ShadowPass1" + // or "Shadow.MainPipeline.ShadowPass1" + + ParentPass* parent = pass->GetParent(); + + // search from the back of the array with the most close parent + for (int32_t index = static_cast(m_parentNames.size() - 1); index >= 0; index--) + { + const Name& parentName = m_parentNames[index]; + while (parent) + { + if (parent->GetName() == parentName) + { + break; + } + parent = parent->GetParent(); + } + + // if parent is nullptr the it didn't find a parent has matching current parentName + if (!parent) + { + return false; + } + + // move to next parent + parent = parent->GetParent(); + } } return true; } - const Name* PassHierarchyFilter::GetPassName() const + void PassFilter::UpdateFilterOptions() { - return &m_passName; - } - - AZStd::string PassHierarchyFilter::ToString() const - { - AZStd::string result = "PassHierarchyFilter"; - for (uint32_t index = 0; index < m_parentNames.size(); index++) + m_filterOptions = FilterOptions::Empty; + if (!m_passName.IsEmpty()) { - result += AZStd::string::format(" [%s]", m_parentNames[index].GetCStr()); + m_filterOptions |= FilterOptions::PassName; + } + if (!m_templateName.IsEmpty()) + { + m_filterOptions |= FilterOptions::PassTemplateName; + } + if (m_parentNames.size() > 0) + { + m_filterOptions |= FilterOptions::PassHierarchy; + } + if (m_ownerRenderPipeline) + { + m_filterOptions |= FilterOptions::OwnerRenderPipeline; + } + if (m_ownerScene) + { + // If the OwnerRenderPipeline exists, we shouldn't need to filter owner scene + // Validate the owner render pipeline belongs to the owner scene + if (m_filterOptions & FilterOptions::OwnerRenderPipeline) + { + if (m_ownerRenderPipeline->GetScene() != m_ownerScene) + { + AZ_Warning("RPI", false, "The owner scene filter doesn't match owner render pipeline. It will be skipped."); + } + } + else + { + m_filterOptions |= FilterOptions::OwnerScene; + } + } + if (!m_passClassTypeId.IsNull()) + { + m_filterOptions |= FilterOptions::PassClass; } - - result += AZStd::string::format(" [%s]", m_passName.GetCStr()); - return result; } - } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp index c43edafd6b..6a6f5f3ff9 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp @@ -85,47 +85,80 @@ namespace AZ return (GetPassesForTemplate(templateName).size() > 0); } - AZStd::vector PassLibrary::FindPasses(const PassFilter& passFilter) const + void PassLibrary::ForEachPass(const PassFilter& passFilter, AZStd::function passFunction) { - const Name* passName = passFilter.GetPassName(); + uint32_t filterOptions = passFilter.GetEnabledFilterOptions(); - AZStd::vector result; - - if (passName) + // A lambda function which visits each pass in a pass list, if the pass matches the pass filter, then call the pass function + auto visitList = [passFilter, passFunction](const AZStd::vector& passList, uint32_t options) -> PassFilterExecutionFlow { - // If the pass' name is known, find passes with matching names first - const auto constItr = m_passNameMapping.find(*passName); - if (constItr == m_passNameMapping.end()) + if (passList.size() == 0) { - return result; + return PassFilterExecutionFlow::ContinueVisitingPasses; } - - const AZStd::vector& passes = constItr->second; - - for (Pass* pass : passes) + // if there is not other filter options enabled, skip the filter and call pass functions directly + if (options == PassFilter::FilterOptions::Empty) { - if (passFilter.Matches(pass)) + for (Pass* pass : passList) { - result.push_back(pass); - } - } - } - else - { - // If the filter doesn't know matching pass' name, need to go through all registered passes - for (auto& namePasses : m_passNameMapping) - { - for (Pass* pass : namePasses.second) - { - if (passFilter.Matches(pass)) + // If user want to skip processing, return directly. + if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses) { - result.push_back(pass); + return PassFilterExecutionFlow::StopVisitingPasses; + } + } + return PassFilterExecutionFlow::ContinueVisitingPasses; + } + + // Check with the pass filter and call pass functions + for (Pass* pass : passList) + { + if (passFilter.Matches(pass, options)) + { + if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses) + { + return PassFilterExecutionFlow::StopVisitingPasses; } } } + return PassFilterExecutionFlow::ContinueVisitingPasses; + }; + + // Check pass template name first + if (filterOptions & PassFilter::FilterOptions::PassTemplateName) + { + auto entry = GetEntry(passFilter.GetPassTemplateName()); + if (!entry) + { + return; + } + + filterOptions &= ~(PassFilter::FilterOptions::PassTemplateName); + visitList(entry->m_passes, filterOptions); + return; + } + else if (filterOptions & PassFilter::FilterOptions::PassName) + { + const auto constItr = m_passNameMapping.find(passFilter.GetPassName()); + if (constItr == m_passNameMapping.end()) + { + return; + } + + filterOptions &= ~(PassFilter::FilterOptions::PassName); + visitList(constItr->second, filterOptions); + return; } - return result; + // check againest every passes. This might be slow + AZ_PROFILE_SCOPE(RPI, "PassLibrary::ForEachPass"); + for (auto& namePasses : m_passNameMapping) + { + if (visitList(namePasses.second, filterOptions) == PassFilterExecutionFlow::StopVisitingPasses) + { + return; + } + } } // Add Functions... @@ -419,3 +452,4 @@ namespace AZ } // namespace RPI } // namespace AZ + diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index f4f51f97b7..7f2948c13a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -456,11 +456,6 @@ namespace AZ return m_passLibrary.HasPassesForTemplate(templateName); } - const AZStd::vector& PassSystem::GetPassesForTemplateName(const Name& templateName) const - { - return m_passLibrary.GetPassesForTemplate(templateName); - } - bool PassSystem::AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate) { return m_passLibrary.AddPassTemplate(name, passTemplate); @@ -487,10 +482,21 @@ namespace AZ RemovePassFromLibrary(pass); --m_passCounter; } - - AZStd::vector PassSystem::FindPasses(const PassFilter& passFilter) const + + void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function passFunction) { - return m_passLibrary.FindPasses(passFilter); + return m_passLibrary.ForEachPass(filter, passFunction); + } + + Pass* PassSystem::FindFirstPass(const PassFilter& filter) + { + Pass* foundPass = nullptr; + m_passLibrary.ForEachPass(filter, [&foundPass](RPI::Pass* pass) ->PassFilterExecutionFlow + { + foundPass = pass; + return PassFilterExecutionFlow::StopVisitingPasses; + }); + return foundPass; } SwapChainPass* PassSystem::FindSwapChainPass(AzFramework::NativeWindowHandle windowHandle) const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp index 105936f64d..74d52fd64d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp @@ -244,7 +244,7 @@ namespace AZ m_needsShaderLoad = false; // Load Shader - const char* ShaderPath = "shader/imagepreview.azshader"; + const char* ShaderPath = "shaders/imagepreview.azshader"; Data::Asset shaderAsset = RPI::FindShaderAsset(ShaderPath); m_shader = Shader::FindOrCreate(shaderAsset); if (m_shader == nullptr) diff --git a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp index 420ab1798a..690f212ec7 100644 --- a/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -573,7 +575,7 @@ namespace UnitTest EXPECT_TRUE(pass != nullptr); } - TEST_F(PassTests, PassHierarchyFilter) + TEST_F(PassTests, PassFilter_PassHierarchy) { m_data->AddPassTemplatesToLibrary(); @@ -587,62 +589,55 @@ namespace UnitTest parent2->AsParent()->AddChild(parent1); parent1->AsParent()->AddChild(pass); - { - // Filter with only pass name - PassHierarchyFilter filter(Name("pass1")); - EXPECT_TRUE(filter.Matches(pass.get())); - } - { // Filter with pass hierarchy which has only one element - PassHierarchyFilter filter({ Name("pass1") }); + PassFilter filter = PassFilter::CreateWithPassHierarchy({Name("pass1")}); EXPECT_TRUE(filter.Matches(pass.get())); } { - // Filter with empty pass hierarchy. Result one assert + // Filter with empty pass hierarchy, triggers one assert AZ_TEST_START_TRACE_SUPPRESSION; - PassHierarchyFilter filter(AZStd::vector{}); + PassFilter filter = PassFilter::CreateWithPassHierarchy(AZStd::vector{}); AZ_TEST_STOP_TRACE_SUPPRESSION(1); - EXPECT_FALSE(filter.Matches(pass.get())); } { // Filters with partial hierarchy by using string vector AZStd::vector passHierarchy1 = { "parent1", "pass1" }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { "parent2", "pass1" }; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector passHierarchy3 = { "parent3", "parent2", "pass1" }; - PassHierarchyFilter filter3(passHierarchy3); + PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3); EXPECT_TRUE(filter3.Matches(pass.get())); } { // Filters with partial hierarchy by using Name vector AZStd::vector passHierarchy1 = { Name("parent1"), Name("pass1") }; - PassHierarchyFilter filter1(passHierarchy1); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1); EXPECT_TRUE(filter1.Matches(pass.get())); AZStd::vector passHierarchy2 = { Name("parent2"), Name("pass1")}; - PassHierarchyFilter filter2(passHierarchy2); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2); EXPECT_TRUE(filter2.Matches(pass.get())); AZStd::vector passHierarchy3 = { Name("parent3"), Name("parent2"), Name("pass1") }; - PassHierarchyFilter filter3(passHierarchy3); + PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3); EXPECT_TRUE(filter3.Matches(pass.get())); } { // Find non-leaf pass - PassHierarchyFilter filter1(AZStd::vector{"parent3", "parent1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"parent3", "parent1"}); EXPECT_TRUE(filter1.Matches(parent1.get())); - - PassHierarchyFilter filter2(Name("parent1")); + + PassFilter filter2 = PassFilter::CreateWithPassHierarchy({ Name("parent1") }); EXPECT_TRUE(filter2.Matches(parent1.get())); EXPECT_FALSE(filter2.Matches(pass.get())); } @@ -650,11 +645,131 @@ namespace UnitTest { // Failed to find pass // Mis-matching hierarchy - PassHierarchyFilter filter1(AZStd::vector{"Parent1", "Parent3", "pass1"}); + PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "Parent3", "pass1"}); EXPECT_FALSE(filter1.Matches(pass.get())); // Mis-matching name - PassHierarchyFilter filter2(AZStd::vector{"Parent1", "pass1"}); + PassFilter filter2 = PassFilter::CreateWithPassHierarchy(AZStd::vector{"Parent1", "pass1"}); EXPECT_FALSE(filter2.Matches(parent1.get())); } } + + TEST_F(PassTests, PassFilter_Empty_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3")); + + parent3->AsParent()->AddChild(parent2); + parent2->AsParent()->AddChild(parent1); + parent1->AsParent()->AddChild(pass); + + PassFilter filter; + + // Any pass can match an empty filter + EXPECT_TRUE(filter.Matches(pass.get())); + EXPECT_TRUE(filter.Matches(parent1.get())); + EXPECT_TRUE(filter.Matches(parent2.get())); + EXPECT_TRUE(filter.Matches(parent3.get())); + } + + TEST_F(PassTests, PassFilter_PassClass_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr depthPass = m_passSystem->CreatePassFromTemplate(Name("DepthPrePass"), Name("depthPass")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + parent1->AsParent()->AddChild(pass); + parent1->AsParent()->AddChild(depthPass); + + PassFilter filter1 = PassFilter::CreateWithPassClass(); + + EXPECT_TRUE(filter1.Matches(pass.get())); + EXPECT_FALSE(filter1.Matches(parent1.get())); + + PassFilter filter2 = PassFilter::CreateWithPassClass(); + EXPECT_FALSE(filter2.Matches(pass.get())); + EXPECT_TRUE(filter2.Matches(parent1.get())); + } + + TEST_F(PassTests, PassFilter_PassTemplate_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr childPass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + + PassFilter filter1 = PassFilter::CreateWithTemplateName(Name("Pass"), (Scene*) nullptr); + // childPass doesn't have a template + EXPECT_FALSE(filter1.Matches(childPass.get())); + + PassFilter filter2 = PassFilter::CreateWithTemplateName(Name("ParentPass"), (Scene*) nullptr); + EXPECT_TRUE(filter2.Matches(parent1.get())); + } + + TEST_F(PassTests, ForEachPass_PassTemplateFilter_Success) + { + m_data->AddPassTemplatesToLibrary(); + + // create a pass tree + Ptr pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1")); + Ptr parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1")); + Ptr parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2")); + Ptr parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3")); + + parent3->AsParent()->AddChild(parent2); + parent2->AsParent()->AddChild(parent1); + parent1->AsParent()->AddChild(pass); + + // Create render pipeline + const RPI::PipelineViewTag viewTag{ "viewTag1" }; + RPI::RenderPipelineDescriptor desc; + desc.m_mainViewTagName = viewTag.GetStringView(); + desc.m_name = "TestPipeline"; + RPI::RenderPipelinePtr pipeline = RPI::RenderPipeline::CreateRenderPipeline(desc); + Ptr parent4 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent4")); + pipeline->GetRootPass()->AddChild(parent4); + + Name templateName = Name("ParentPass"); + PassFilter filter1 = PassFilter::CreateWithTemplateName(templateName, (RenderPipeline*)nullptr); + + int count = 0; + m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow + { + EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName); + count++; + return PassFilterExecutionFlow::ContinueVisitingPasses; + }); + + // three from CreatePassFromTemplate() calls and one from Render Pipeline. + EXPECT_TRUE(count == 4); + + count = 0; + m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow + { + EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName); + count++; + return PassFilterExecutionFlow::StopVisitingPasses; + }); + EXPECT_TRUE(count == 1); + + PassFilter filter2 = PassFilter::CreateWithTemplateName(templateName, pipeline.get()); + count = 0; + m_passSystem->ForEachPass(filter2, [&count]([[maybe_unused]] RPI::Pass* pass) -> PassFilterExecutionFlow + { + count++; + return PassFilterExecutionFlow::ContinueVisitingPasses; + }); + + // only the ParentPass in the render pipeline was found + EXPECT_TRUE(count == 1); + + } } diff --git a/Gems/Atom/RPI/Registry/atom_rpi.setreg b/Gems/Atom/RPI/Registry/atom_rpi.setreg index bcbade5d38..16ce8b3bd7 100644 --- a/Gems/Atom/RPI/Registry/atom_rpi.setreg +++ b/Gems/Atom/RPI/Registry/atom_rpi.setreg @@ -3,7 +3,7 @@ "Atom": { "RPI": { "Initialization": { - "CommonSrgsShaderAssetPath": "shader/sceneandviewsrgs.azshader", + "CommonSrgsShaderAssetPath": "shaders/sceneandviewsrgs.azshader", "ImageSystemDescriptor": { "AssetStreamingImagePoolSize": 2147483648, // 2 * 1024 * 1024 * 1024 "SystemStreamingImagePoolSize": 134217728, // 128 * 1024 * 1024 diff --git a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab index b5aed9d14b..92874574e4 100644 --- a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab +++ b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab @@ -581,7 +581,7 @@ { "id": { "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -593,7 +593,7 @@ "id": { "lodIndex": 0, "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -608,10 +608,10 @@ "Configuration": { "ModelAsset": { "assetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", - "subId": 277333723 + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", + "subId": 277889906 }, - "assetHint": "objects/groudplane/groundplane_521x521m.azmodel" + "assetHint": "objects/groudplane/groundplane_512x512m.azmodel" } } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice index b4c9eac10f..e0c7c9e456 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice @@ -836,7 +836,7 @@ - + diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp index a0be18f0e2..161160e16f 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -142,12 +143,13 @@ namespace AZ EnablePasses(true); } - void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable) + void HairFeatureProcessor::EnablePasses(bool enable) { - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName); - if (desiredPass) + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); + if (pass) { - desiredPass->SetEnabled(enable); + pass->SetEnabled(enable); } } @@ -309,10 +311,17 @@ namespace AZ m_forceClearRenderData = true; } + bool HairFeatureProcessor::HasHairParentPass() + { + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene()); + RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); + return pass; + } + void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline.get()->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -323,10 +332,10 @@ namespace AZ m_forceRebuildRenderData = true; } - void HairFeatureProcessor::OnRenderPipelineRemoved(RPI::RenderPipeline* renderPipeline) + void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -338,7 +347,7 @@ namespace AZ void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) { // Proceed only if this is the main pipeline that contains the parent pass - if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName)) + if (!HasHairParentPass()) { return; } @@ -457,7 +466,8 @@ namespace AZ { m_computePasses[passName] = nullptr; - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(passName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_computePasses[passName] = static_cast(desiredPass.get()); @@ -478,8 +488,9 @@ namespace AZ bool HairFeatureProcessor::InitPPLLFillPass() { m_hairPPLLRasterPass = nullptr; // reset it to null, just in case it fails to load the assets properly - - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName); + + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLRasterPassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLRasterPass = static_cast(desiredPass.get()); @@ -497,7 +508,8 @@ namespace AZ { m_hairPPLLResolvePass = nullptr; // reset it to null, just in case it fails to load the assets properly - RPI::Ptr desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName); + RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLResolvePassName, m_renderPipeline); + RPI::Ptr desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter); if (desiredPass) { m_hairPPLLResolvePass = static_cast(desiredPass.get()); @@ -518,8 +530,8 @@ namespace AZ m_hairShortCutGeometryDepthAlphaPass = nullptr; m_hairShortCutGeometryShadingPass = nullptr; - m_hairShortCutGeometryDepthAlphaPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get()); + RPI::PassFilter depthAlphaPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryDepthAlphaPassName, m_renderPipeline); + m_hairShortCutGeometryDepthAlphaPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(depthAlphaPassFilter)); if (m_hairShortCutGeometryDepthAlphaPass) { m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this); @@ -530,8 +542,8 @@ namespace AZ return false; } - m_hairShortCutGeometryShadingPass = static_cast( - m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get()); + RPI::PassFilter shaderingPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryShadingPassName, m_renderPipeline); + m_hairShortCutGeometryShadingPass = static_cast(RPI::PassSystemInterface::Get()->FindFirstPass(shaderingPassFilter)); if (m_hairShortCutGeometryShadingPass) { m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this); diff --git a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h index 46660a6623..f810967824 100644 --- a/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h +++ b/Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h @@ -165,6 +165,8 @@ namespace AZ void EnablePasses(bool enable); + bool HasHairParentPass(); + //! The following will serve to register the FP in the Thumbnail system AZStd::vector m_hairFeatureProcessorRegistryName; diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material deleted file mode 100644 index afaf7e1947..0000000000 --- a/Gems/Terrain/Assets/Materials/Terrain/DefaultTerrainMacroMaterial.material +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "", - "materialType": "TerrainMacroMaterial.materialtype", - "parentMaterial": "", - "propertyLayoutVersion": 1, - "properties": { - } -} diff --git a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype b/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype deleted file mode 100644 index 17769ffb92..0000000000 --- a/Gems/Terrain/Assets/Materials/Terrain/TerrainMacroMaterial.materialtype +++ /dev/null @@ -1,63 +0,0 @@ -{ - "description": "A material for providing terrain with low-fidelity color and normals. This material will get blended with surface detail materials.", - "version": 1, - "propertyLayout": { - "groups": [ - { - "name": "baseColor", - "displayName": "Base Color", - "description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals." - }, - { - "name": "normal", - "displayName": "Normal", - "description": "Properties related to configuring surface normal." - } - ], - "properties": { - "baseColor": [ - { - "name": "textureMap", - "displayName": "Texture", - "description": "Base color of the macro material", - "type": "Image" - } - ], - "normal": [ - { - "name": "textureMap", - "displayName": "Texture", - "description": "Texture for defining surface normal direction. These will override normals generated from the geometry.", - "type": "Image" - }, - { - "name": "flipX", - "displayName": "Flip X Channel", - "description": "Flip tangent direction for this normal map.", - "type": "Bool", - "defaultValue": false - }, - { - "name": "flipY", - "displayName": "Flip Y Channel", - "description": "Flip bitangent direction for this normal map.", - "type": "Bool", - "defaultValue": false - }, - { - "name": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the values", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "softMax": 2.0 - } - ] - } - }, - "shaders": [ - ], - "functors": [ - ] -} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp index 55653c64fa..0d161b6b2b 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp @@ -16,87 +16,65 @@ #include #include +#include + namespace Terrain { - AZ::Data::AssetId TerrainMacroMaterialConfig::s_macroMaterialTypeAssetId{}; + bool TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly() const + { + return !m_macroNormalAsset.GetId().IsValid(); + } void TerrainMacroMaterialConfig::Reflect(AZ::ReflectContext* context) { - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) + if (auto* serialize = azrtti_cast(context); serialize) { serialize->Class() ->Version(1) - ->Field("MacroMaterial", &TerrainMacroMaterialConfig::m_materialAsset) - ; + ->Field("MacroColor", &TerrainMacroMaterialConfig::m_macroColorAsset) + ->Field("MacroNormal", &TerrainMacroMaterialConfig::m_macroNormalAsset) + ->Field("NormalFlipX", &TerrainMacroMaterialConfig::m_normalFlipX) + ->Field("NormalFlipY", &TerrainMacroMaterialConfig::m_normalFlipY) + ->Field("NormalFactor", &TerrainMacroMaterialConfig::m_normalFactor) + ; - // The edit context for this appears in EditorTerrainMacroMaterialComponent.cpp. - } - } - - AZ::Data::AssetId TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId() - { - // Get the Asset ID for the TerrainMacroMaterial material type and store it in a class static so that we don't have to look it - // up again. - if (!s_macroMaterialTypeAssetId.IsValid()) - { - AZ::Data::AssetCatalogRequestBus::BroadcastResult( - s_macroMaterialTypeAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, TerrainMacroMaterialTypeAsset, - azrtti_typeid(), false); - AZ_Assert(s_macroMaterialTypeAssetId.IsValid(), "The asset '%s' couldn't be found.", TerrainMacroMaterialTypeAsset); - } - - return s_macroMaterialTypeAssetId; - } - - bool TerrainMacroMaterialConfig::IsMaterialTypeCorrect(const AZ::Data::AssetId& assetId) - { - // We'll verify that whatever material we try to load has this material type as a dependency, as a way to implicitly detect - // that we're only trying to use terrain macro materials even before we load the asset. - auto macroMaterialTypeAssetId = GetTerrainMacroMaterialTypeAssetId(); - - // Get the dependencies for the requested asset. - AZ::Outcome, AZStd::string> result; - AZ::Data::AssetCatalogRequestBus::BroadcastResult( - result, &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, assetId); - - // If any of the dependencies match the TerrainMacroMaterial materialtype asset, then this should be the correct type of material. - if (result) - { - for (auto& dependency : result.GetValue()) + if (auto* editContext = serialize->GetEditContext(); editContext) { - if (dependency.m_assetId == macroMaterialTypeAssetId) - { - return true; - } + editContext + ->Class( + "Terrain Macro Material Component", "Provide a terrain macro material for a region of the world") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_macroColorAsset, "Color Texture", + "Terrain macro color texture for use by any terrain inside the bounding box on this entity.") + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_macroNormalAsset, "Normal Texture", + "Texture for defining surface normal direction. These will override normals generated from the geometry.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_normalFlipX, "Normal Flip X", + "Flip tangent direction for this normal map.") + ->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly) + ->DataElement( + AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_normalFlipY, "Normal Flip Y", + "Flip bitangent direction for this normal map.") + ->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly) + ->DataElement( + AZ::Edit::UIHandlers::Slider, &TerrainMacroMaterialConfig::m_normalFactor, "Normal Factor", + "Strength factor for scaling the normal map values.") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 10.0f) + ->Attribute(AZ::Edit::Attributes::SoftMin, 0.0f) + ->Attribute(AZ::Edit::Attributes::SoftMax, 2.0f) + ->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly) + ; } } - - // Didn't have the expected dependency, so it must not be the right material type. - return false; } - AZ::Outcome TerrainMacroMaterialConfig::ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType) - { - if (azrtti_typeid>() != valueType) - { - AZ_Assert(false, "Unexpected value type"); - return AZ::Failure(AZStd::string("Unexpectedly received something other than a material asset for the MacroMaterial!")); - } - - auto newMaterialAsset = *static_cast*>(newValue); - - if (!IsMaterialTypeCorrect(newMaterialAsset.GetId())) - { - return AZ::Failure(AZStd::string::format( - "The selected MacroMaterial ('%s') needs to use the TerrainMacroMaterial material type.", - newMaterialAsset.GetHint().c_str())); - } - - return AZ::Success(); - } - - void TerrainMacroMaterialComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) { services.push_back(AZ_CRC_CE("TerrainMacroMaterialProviderService")); @@ -133,25 +111,29 @@ namespace Terrain void TerrainMacroMaterialComponent::Activate() { - // Clear out our shape bounds and make sure the material is queued to load. + // Clear out our shape bounds and make sure the texture assets are queued to load. m_cachedShapeBounds = AZ::Aabb::CreateNull(); - m_configuration.m_materialAsset.QueueLoad(); + m_configuration.m_macroColorAsset.QueueLoad(); + m_configuration.m_macroNormalAsset.QueueLoad(); // Don't mark our material as active until it's finished loading and is valid. m_macroMaterialActive = false; - // Listen for the material asset to complete loading. - AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_materialAsset.GetId()); + // Listen for the texture assets to complete loading. + AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_macroColorAsset.GetId()); + AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_macroNormalAsset.GetId()); } void TerrainMacroMaterialComponent::Deactivate() { TerrainMacroMaterialRequestBus::Handler::BusDisconnect(); - AZ::Data::AssetBus::Handler::BusDisconnect(); - m_configuration.m_materialAsset.Release(); + AZ::Data::AssetBus::MultiHandler::BusDisconnect(); + m_configuration.m_macroColorAsset.Release(); + m_configuration.m_macroNormalAsset.Release(); - m_macroMaterialInstance.reset(); + m_colorImage.reset(); + m_normalImage.reset(); // Send out any notifications as appropriate based on the macro material destruction. HandleMaterialStateChange(); @@ -195,12 +177,18 @@ namespace Terrain void TerrainMacroMaterialComponent::HandleMaterialStateChange() { - // We only want our component to appear active during the time that the macro material is loaded and valid. The logic below + // We only want our component to appear active during the time that the macro material is fully loaded and valid. The logic below // will handle all transition possibilities to notify if we've become active, inactive, or just changed. We'll also only // keep a valid up-to-date copy of the shape bounds while the material is valid, since we don't need it any other time. + // Color and normal data is considered ready if it's finished loading or if we don't have a texture specified + bool colorReady = m_colorImage || (!m_configuration.m_macroColorAsset.GetId().IsValid()); + bool normalReady = m_normalImage || (!m_configuration.m_macroNormalAsset.GetId().IsValid()); + // If we don't have color or normal data, then we don't have *any* useful data, so don't activate the macro material. + bool hasAnyData = m_configuration.m_macroColorAsset.GetId().IsValid() || m_configuration.m_macroNormalAsset.GetId().IsValid(); + bool wasPreviouslyActive = m_macroMaterialActive; - bool isNowActive = (m_macroMaterialInstance != nullptr); + bool isNowActive = colorReady && normalReady && hasAnyData; // Set our state to active or inactive, based on whether or not the macro material instance is now valid. m_macroMaterialActive = isNowActive; @@ -226,9 +214,10 @@ namespace Terrain // Start listening for shape changes. LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); + MacroMaterialData material = GetTerrainMacroMaterialData(); + TerrainMacroMaterialNotificationBus::Broadcast( - &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), m_macroMaterialInstance, - m_cachedShapeBounds); + &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), material); } else if (wasPreviouslyActive && !isNowActive) { @@ -246,30 +235,35 @@ namespace Terrain else { // We were active both before and after, so just send out a material changed event. + MacroMaterialData material = GetTerrainMacroMaterialData(); TerrainMacroMaterialNotificationBus::Broadcast( - &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), m_macroMaterialInstance); + &TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), material); } } void TerrainMacroMaterialComponent::OnAssetReady(AZ::Data::Asset asset) { - m_configuration.m_materialAsset = asset; - - if (m_configuration.m_materialAsset.Get()->GetMaterialTypeAsset().GetId() == - TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId()) + if (asset.GetId() == m_configuration.m_macroColorAsset.GetId()) { - m_macroMaterialInstance = AZ::RPI::Material::FindOrCreate(m_configuration.m_materialAsset); + m_configuration.m_macroColorAsset = asset; + m_colorImage = AZ::RPI::StreamingImage::FindOrCreate(m_configuration.m_macroColorAsset); + + // Clear the texture asset reference to make sure we don't prevent hot-reloading. + m_configuration.m_macroColorAsset.Release(); + } + else if (asset.GetId() == m_configuration.m_macroNormalAsset.GetId()) + { + m_configuration.m_macroNormalAsset = asset; + m_normalImage = AZ::RPI::StreamingImage::FindOrCreate(m_configuration.m_macroNormalAsset); + + // Clear the texture asset reference to make sure we don't prevent hot-reloading. + m_configuration.m_macroColorAsset.Release(); } else { - AZ_Error("Terrain", false, "Material '%s' has the wrong material type.", m_configuration.m_materialAsset.GetHint().c_str()); - m_macroMaterialInstance.reset(); } - // Clear the material asset reference to make sure we don't prevent hot-reloading. - m_configuration.m_materialAsset.Release(); - HandleMaterialStateChange(); } @@ -278,10 +272,18 @@ namespace Terrain OnAssetReady(asset); } - void TerrainMacroMaterialComponent::GetTerrainMacroMaterialData( - AZ::Data::Instance& macroMaterial, AZ::Aabb& macroMaterialRegion) + MacroMaterialData TerrainMacroMaterialComponent::GetTerrainMacroMaterialData() { - macroMaterial = m_macroMaterialInstance; - macroMaterialRegion = m_cachedShapeBounds; + MacroMaterialData macroMaterial; + + macroMaterial.m_entityId = GetEntityId(); + macroMaterial.m_bounds = m_cachedShapeBounds; + macroMaterial.m_colorImage = m_colorImage; + macroMaterial.m_normalImage = m_normalImage; + macroMaterial.m_normalFactor = m_configuration.m_normalFactor; + macroMaterial.m_normalFlipX = m_configuration.m_normalFlipX; + macroMaterial.m_normalFlipY = m_configuration.m_normalFlipY; + + return macroMaterial; } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h index b60f6b2a94..cd82316ff6 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h @@ -11,10 +11,9 @@ #include #include #include -#include #include #include - +#include namespace LmbrCentral { @@ -32,23 +31,20 @@ namespace Terrain AZ_RTTI(TerrainMacroMaterialConfig, "{9DBAFFF0-FD20-4594-8884-E3266D8CCAC8}", AZ::ComponentConfig); static void Reflect(AZ::ReflectContext* context); - AZ::Data::Asset m_materialAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; - - static AZ::Data::AssetId GetTerrainMacroMaterialTypeAssetId(); - static bool IsMaterialTypeCorrect(const AZ::Data::AssetId&); - AZ::Outcome ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType); - - private: - static inline constexpr const char* TerrainMacroMaterialTypeAsset = "materials/terrain/terrainmacromaterial.azmaterialtype"; - static AZ::Data::AssetId s_macroMaterialTypeAssetId; + bool NormalMapAttributesAreReadOnly() const; + AZ::Data::Asset m_macroColorAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; + AZ::Data::Asset m_macroNormalAsset = { AZ::Data::AssetLoadBehavior::QueueLoad }; + bool m_normalFlipX = false; + bool m_normalFlipY = false; + float m_normalFactor = 1.0f; }; class TerrainMacroMaterialComponent : public AZ::Component , public TerrainMacroMaterialRequestBus::Handler , private LmbrCentral::ShapeComponentNotificationsBus::Handler - , private AZ::Data::AssetBus::Handler + , private AZ::Data::AssetBus::MultiHandler { public: template @@ -70,7 +66,7 @@ namespace Terrain bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; - void GetTerrainMacroMaterialData(AZ::Data::Instance& macroMaterial, AZ::Aabb& macroMaterialRegion) override; + MacroMaterialData GetTerrainMacroMaterialData() override; private: //////////////////////////////////////////////////////////////////////// @@ -86,7 +82,8 @@ namespace Terrain TerrainMacroMaterialConfig m_configuration; AZ::Aabb m_cachedShapeBounds; - AZ::Data::Instance m_macroMaterialInstance; bool m_macroMaterialActive{ false }; + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; }; } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp index 07472d1b85..65500af42d 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/EditorComponents/EditorTerrainMacroMaterialComponent.cpp @@ -17,36 +17,7 @@ namespace Terrain { BaseClassType::ReflectSubClass( context, 1, - &LmbrCentral::EditorWrappedComponentBaseVersionConverter - ); - - AZ::SerializeContext* serializeContext = azrtti_cast(context); - - if (serializeContext) - { - AZ::EditContext* editContext = serializeContext->GetEditContext(); - - // The edit context for TerrainMacroMaterialConfig is specified here to make it easier to add custom filtering to the - // asset picker for the material asset so that we can eventually only display materials that inherit from the proper - // material type. - if (editContext) - { - editContext - ->Class( - "Terrain Macro Material Component", "Provide a terrain macro material for a region of the world") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - - ->DataElement( - AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_materialAsset, "Macro Material", - "Terrain macro material for use by any terrain inside the bounding box on this entity.") - // This is disabled until ChangeValidate can support the Asset type. :( - //->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainMacroMaterialConfig::ValidateMaterialAsset) - ; - } - } - + &LmbrCentral::EditorWrappedComponentBaseVersionConverter< + typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType, 1>); } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 8c85e21490..e6aed28897 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -51,13 +51,6 @@ namespace Terrain { // Terrain material static const char* const HeightmapImage("settings.heightmapImage"); - - // Macro material - static const char* const MacroColorTextureMap("baseColor.textureMap"); - static const char* const MacroNormalTextureMap("normal.textureMap"); - static const char* const MacroNormalFlipX("normal.flipX"); - static const char* const MacroNormalFlipY("normal.flipY"); - static const char* const MacroNormalFactor("normal.factor"); } namespace ShaderInputs @@ -185,12 +178,11 @@ namespace Terrain m_areaData.m_heightmapUpdated = true; } - void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) + void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) { MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); - materialData.m_bounds = region; - UpdateMacroMaterialData(materialData, material); + UpdateMacroMaterialData(materialData, newMaterialData); // Update all sectors in region. ForOverlappingSectors(materialData.m_bounds, @@ -203,20 +195,14 @@ namespace Terrain ); } - void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance macroMaterial) + void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) { - if (macroMaterial) - { - MacroMaterialData& data = FindOrCreateMacroMaterial(entityId); - UpdateMacroMaterialData(data, macroMaterial); - } - else - { - RemoveMacroMaterial(entityId); - } + MacroMaterialData& data = FindOrCreateMacroMaterial(entityId); + UpdateMacroMaterialData(data, newMaterialData); } - void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) + void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged( + AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) { MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId); for (SectorData& sectorData : m_sectorData) @@ -269,6 +255,7 @@ namespace Terrain } m_areaData.m_macroMaterialsUpdated = true; + RemoveMacroMaterial(entityId); } void TerrainFeatureProcessor::UpdateTerrainData() @@ -382,42 +369,18 @@ namespace Terrain TerrainMacroMaterialRequestBus::EnumerateHandlers( [&](TerrainMacroMaterialRequests* handler) { - MaterialInstance macroMaterial; - AZ::Aabb bounds; - handler->GetTerrainMacroMaterialData(macroMaterial, bounds); + MacroMaterialData macroMaterial = handler->GetTerrainMacroMaterialData(); AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId()); - OnTerrainMacroMaterialCreated(entityId, macroMaterial, bounds); + OnTerrainMacroMaterialCreated(entityId, macroMaterial); return true; } ); TerrainMacroMaterialNotificationBus::Handler::BusConnect(); } - void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material) + void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData) { - // Since we're using an actual macro material instance for now, get the values from it that we care about. - const auto materialLayout = material->GetMaterialPropertiesLayout(); - - const AZ::RPI::MaterialPropertyIndex macroColorTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroColorTextureMap)); - AZ_Error(TerrainFPName, macroColorTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroColorTextureMap); - - const AZ::RPI::MaterialPropertyIndex macroNormalTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalTextureMap)); - AZ_Error(TerrainFPName, macroNormalTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalTextureMap); - - const AZ::RPI::MaterialPropertyIndex macroNormalFlipXIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipX)); - AZ_Error(TerrainFPName, macroNormalFlipXIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipX); - - const AZ::RPI::MaterialPropertyIndex macroNormalFlipYIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipY)); - AZ_Error(TerrainFPName, macroNormalFlipYIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipY); - - const AZ::RPI::MaterialPropertyIndex macroNormalFactorIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFactor)); - AZ_Error(TerrainFPName, macroNormalFactorIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFactor); - - macroMaterialData.m_colorImage = material->GetPropertyValue(macroColorTextureMapIndex).GetValue>(); - macroMaterialData.m_normalImage = material->GetPropertyValue(macroNormalTextureMapIndex).GetValue>(); - macroMaterialData.m_normalFlipX = material->GetPropertyValue(macroNormalFlipXIndex).GetValue(); - macroMaterialData.m_normalFlipY = material->GetPropertyValue(macroNormalFlipYIndex).GetValue(); - macroMaterialData.m_normalFactor = material->GetPropertyValue(macroNormalFactorIndex).GetValue(); + macroMaterialData = newMaterialData; if (macroMaterialData.m_bounds.IsValid()) { @@ -783,7 +746,7 @@ namespace Terrain // larger but this will limit how much is rendered. } - TerrainFeatureProcessor::MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId) + MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId) { for (MacroMaterialData& data : m_macroMaterials.GetDataVector()) { @@ -795,7 +758,7 @@ namespace Terrain return nullptr; } - TerrainFeatureProcessor::MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId) + MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId) { MacroMaterialData* dataPtr = FindMacroMaterial(entityId); if (dataPtr != nullptr) diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index d9f15f2d47..f82fd8ecb0 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -112,18 +112,6 @@ namespace Terrain AZStd::fixed_vector m_macroMaterials; }; - struct MacroMaterialData - { - AZ::EntityId m_entityId; - AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); - - AZ::Data::Instance m_colorImage; - AZ::Data::Instance m_normalImage; - bool m_normalFlipX{ false }; - bool m_normalFlipY{ false }; - float m_normalFactor{ 0.0f }; - }; - // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... void OnMaterialReinitialized(const MaterialInstance& material) override; @@ -132,8 +120,8 @@ namespace Terrain void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; // TerrainMacroMaterialNotificationBus overrides... - void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) override; - void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance material) override; + void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& material) override; + void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& material) override; void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override; @@ -143,7 +131,7 @@ namespace Terrain void UpdateTerrainData(); void PrepareMaterialData(); - void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material); + void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData); void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h index c0618a7f66..af1e755b32 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h @@ -12,11 +12,22 @@ #include #include #include - -#include +#include namespace Terrain { + struct MacroMaterialData + { + AZ::EntityId m_entityId; + AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); + + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; + bool m_normalFlipX{ false }; + bool m_normalFlipY{ false }; + float m_normalFactor{ 0.0f }; + }; + /** * Request terrain macro material data. */ @@ -32,7 +43,7 @@ namespace Terrain virtual ~TerrainMacroMaterialRequests() = default; // Get the terrain macro material and the region that it covers. - virtual void GetTerrainMacroMaterialData(AZ::Data::Instance& macroMaterial, AZ::Aabb& macroMaterialRegion) = 0; + virtual MacroMaterialData GetTerrainMacroMaterialData() = 0; }; using TerrainMacroMaterialRequestBus = AZ::EBus; @@ -51,14 +62,12 @@ namespace Terrain virtual void OnTerrainMacroMaterialCreated( [[maybe_unused]] AZ::EntityId macroMaterialEntity, - [[maybe_unused]] AZ::Data::Instance macroMaterial, - [[maybe_unused]] const AZ::Aabb& macroMaterialRegion) + [[maybe_unused]] const MacroMaterialData& macroMaterial) { } virtual void OnTerrainMacroMaterialChanged( - [[maybe_unused]] AZ::EntityId macroMaterialEntity, - [[maybe_unused]] AZ::Data::Instance macroMaterial) + [[maybe_unused]] AZ::EntityId macroMaterialEntity, [[maybe_unused]] const MacroMaterialData& macroMaterial) { } diff --git a/Templates/CMakeLists.txt b/Templates/CMakeLists.txt index 1a3a45b5ec..84a708989a 100644 --- a/Templates/CMakeLists.txt +++ b/Templates/CMakeLists.txt @@ -9,6 +9,8 @@ ly_install_directory( DIRECTORIES AssetGem + CustomTool + PythonGem DefaultGem DefaultProject MinimalProject diff --git a/Templates/PythonGem/Template/CMakeLists.txt b/Templates/PythonGem/Template/CMakeLists.txt new file mode 100644 index 0000000000..d61bbd9e7d --- /dev/null +++ b/Templates/PythonGem/Template/CMakeLists.txt @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +add_subdirectory(Code) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake new file mode 100644 index 0000000000..8362d37f52 --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_files.cmake @@ -0,0 +1,14 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Include/${Name}/${Name}Bus.h + Source/${Name}ModuleInterface.h + Source/${Name}EditorSystemComponent.cpp + Source/${Name}EditorSystemComponent.h +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake new file mode 100644 index 0000000000..2d4ceae97d --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_shared_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Source/${Name}EditorModule.cpp +) diff --git a/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake new file mode 100644 index 0000000000..ff45c2fc1c --- /dev/null +++ b/Templates/PythonGem/Template/Code/${NameLower}_editor_tests_files.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(FILES + Tests/${Name}EditorTest.cpp +) diff --git a/Templates/PythonGem/Template/Code/CMakeLists.txt b/Templates/PythonGem/Template/Code/CMakeLists.txt new file mode 100644 index 0000000000..b7a5ac89a9 --- /dev/null +++ b/Templates/PythonGem/Template/Code/CMakeLists.txt @@ -0,0 +1,76 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/${Name}/Code/Platform/ or +# //Gems/${Name}/Code +ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + + +# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which +# will also depend on ${Name}.Static +if(PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_target( + NAME ${Name}.Editor.Static STATIC + NAMESPACE Gem + FILES_CMAKE + ${NameLower}_editor_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + ) + + ly_add_target( + NAME ${Name}.Editor GEM_MODULE + NAMESPACE Gem + AUTOMOC + FILES_CMAKE + ${NameLower}_editor_shared_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PUBLIC + Gem::${Name}.Editor.Static + ) + + # By default, we will specify that the above target ${Name} would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor) + ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor) + + +endif() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for ${Name}.Static + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + endif() +endif() diff --git a/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h new file mode 100644 index 0000000000..d09bb2b009 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Include/${Name}/${Name}Bus.h @@ -0,0 +1,40 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#pragma once + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}Requests + { + public: + AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}"); + virtual ~${SanitizedCppName}Requests() = default; + // Put your public methods here + }; + + class ${SanitizedCppName}BusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>; + using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>; + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake new file mode 100644 index 0000000000..2f58a2e6f5 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/${NameLower}_shared_linux_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Linux +# i.e. ../Source/Linux/${Name}Linux.cpp +# ../Source/Linux/${Name}Linux.h +# ../Include/Linux/${Name}Linux.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake new file mode 100644 index 0000000000..1cf737a2f1 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/${NameLower}_shared_mac_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Mac +# i.e. ../Source/Mac/${Name}Mac.cpp +# ../Source/Mac/${Name}Mac.h +# ../Include/Mac/${Name}Mac.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_shared_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake new file mode 100644 index 0000000000..712aad1207 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/${NameLower}_windows_files.cmake @@ -0,0 +1,15 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +# Platform specific files for Windows +# i.e. ../Source/Windows/${Name}Windows.cpp +# ../Source/Windows/${Name}Windows.h +# ../Include/Windows/${Name}Windows.h + +set(FILES +) diff --git a/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..0abcd887e8 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,11 @@ +# {BEGIN_LICENSE} +# 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 +# +# {END_LICENSE} + +set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp new file mode 100644 index 0000000000..644c513747 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorModule.cpp @@ -0,0 +1,47 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include <${Name}ModuleInterface.h> +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}EditorModule + : public ${SanitizedCppName}ModuleInterface + { + public: + AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0); + + ${SanitizedCppName}EditorModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + * Non-SystemComponents should not be added here + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList { + azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(), + }; + } + }; +}// namespace ${SanitizedCppName} + +AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule) diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp new file mode 100644 index 0000000000..1493c98e68 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.cpp @@ -0,0 +1,70 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ + // {END_LICENSE} + +#include +#include <${Name}EditorSystemComponent.h> + +namespace ${SanitizedCppName} +{ + void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class<${SanitizedCppName}EditorSystemComponent, AZ::Component>(); + } + } + + ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == nullptr) + { + ${SanitizedCppName}Interface::Register(this); + } + } + + ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() + { + if (${SanitizedCppName}Interface::Get() == this) + { + ${SanitizedCppName}Interface::Unregister(this); + } + } + + void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService")); + } + + void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void ${SanitizedCppName}EditorSystemComponent::Activate() + { + ${SanitizedCppName}RequestBus::Handler::BusConnect(); + AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + } + + void ${SanitizedCppName}EditorSystemComponent::Deactivate() + { + AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + ${SanitizedCppName}RequestBus::Handler::BusDisconnect(); + } + +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h new file mode 100644 index 0000000000..1db8725a9e --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}EditorSystemComponent.h @@ -0,0 +1,42 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ + // {END_LICENSE} + +#pragma once +#include +#include <${Name}/${Name}Bus.h> + +#include + +namespace ${SanitizedCppName} +{ + /// System component for ${SanitizedCppName} editor + class ${SanitizedCppName}EditorSystemComponent + : public ${SanitizedCppName}RequestBus::Handler + , private AzToolsFramework::EditorEvents::Bus::Handler + , public AZ::Component + { + public: + AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}"); + static void Reflect(AZ::ReflectContext* context); + + ${SanitizedCppName}EditorSystemComponent(); + ~${SanitizedCppName}EditorSystemComponent(); + + private: + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + // AZ::Component + void Activate(); + void Deactivate(); + }; +} // namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h new file mode 100644 index 0000000000..4ddfc9c007 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Source/${Name}ModuleInterface.h @@ -0,0 +1,36 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include +#include + +namespace ${SanitizedCppName} +{ + class ${SanitizedCppName}ModuleInterface + : public AZ::Module + { + public: + AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module); + AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0); + + ${SanitizedCppName}ModuleInterface() + { + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + }; + } + }; +}// namespace ${SanitizedCppName} diff --git a/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp new file mode 100644 index 0000000000..9b84575fa0 --- /dev/null +++ b/Templates/PythonGem/Template/Code/Tests/${Name}EditorTest.cpp @@ -0,0 +1,13 @@ +// {BEGIN_LICENSE} +/* + * 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 + * + */ +// {END_LICENSE} + +#include + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py new file mode 100644 index 0000000000..39515711ae --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/${NameLower}_dialog.py @@ -0,0 +1,46 @@ +""" +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 +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\${SanitizedCppName}_dialog.py +Generated from O3DE PythonGem Template""" + +import azlmbr +from shiboken2 import wrapInstance, getCppPointer +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QVBoxLayout, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton + +# Once PySide2 has been bootstrapped, register our ${SanitizedCppName}Dialog with the Editor + +class ${SanitizedCppName}Dialog(QDialog): + def __init__(self, parent=None): + super(${SanitizedCppName}Dialog, self).__init__(parent) + + self.setObjectName("${SanitizedCppName}Dialog") + + self.setWindowTitle("HelloWorld, ${SanitizedCppName} Dialog") + + self.mainLayout = QVBoxLayout(self) + + self.introLabel = QLabel("Put your cool stuff here!") + + self.mainLayout.addWidget(self.introLabel, 0, Qt.AlignCenter) + + self.helpText = str("For help getting started," + "visit the UI Development documentation
" + "or come ask a question in the sig-ui-ux channel on Discord") + + self.helpLabel = QLabel() + self.helpLabel.setTextFormat(Qt.RichText) + self.helpLabel.setText(self.helpText) + self.helpLabel.setOpenExternalLinks(True) + + self.mainLayout.addWidget(self.helpLabel, 0, Qt.AlignCenter) + + self.setLayout(self.mainLayout) + + return \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/__init__.py b/Templates/PythonGem/Template/Editor/Scripts/__init__.py new file mode 100644 index 0000000000..b5da0c7ff0 --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/__init__.py @@ -0,0 +1,9 @@ +""" +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 +""" +# ------------------------------------------------------------------------- + +__ALL__ = ['bootstrap','${NameLower}_dialog'] \ No newline at end of file diff --git a/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py new file mode 100644 index 0000000000..060116d36c --- /dev/null +++ b/Templates/PythonGem/Template/Editor/Scripts/bootstrap.py @@ -0,0 +1,117 @@ +""" +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 +""" +# ------------------------------------------------------------------------- +"""${SanitizedCppName}\\editor\\scripts\\boostrap.py +Generated from O3DE PythonGem Template""" + +import azlmbr +import az_qt_helpers +from PySide2 import QtCore, QtWidgets, QtGui +from PySide2.QtCore import QEvent, Qt +from PySide2.QtWidgets import QMainWindow, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton +# ------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------- +class SampleUI(QtWidgets.QDialog): + """Lightweight UI Test Class created a button""" + def __init__(self, parent, title='Not Set'): + super(SampleUI, self).__init__(parent) + self.setWindowTitle(title) + self.initUI() + + def initUI(self): + mainLayout = QtWidgets.QHBoxLayout() + testBtn = QtWidgets.QPushButton("I am just a Button man!") + mainLayout.addWidget(testBtn) + self.setLayout(mainLayout) +# ------------------------------------------------------------------------- + +if __name__ == "__main__": + print("${SanitizedCppName}.boostrap, Generated from O3DE PythonGem Template") + + # --------------------------------------------------------------------- + # validate pyside before continuing + try: + azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'IsActive') + params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'GetQtBootstrapParameters') + params is not None and params.mainWindowId is not 0 + from PySide2 import QtWidgets + except Exception as e: + _LOGGER.error(f'Pyside not available, exception: {e}') + raise e + + # keep going, import the other PySide2 bits we will use + from PySide2 import QtGui + from PySide2.QtCore import Slot + from shiboken2 import wrapInstance, getCppPointer + + # Get our Editor main window + _widget_main_window = None + try: + _widget_main_window = az_qt_helpers.get_editor_main_window() + except: + pass # may be booting in the AP? + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # creat a custom menu + _tag_str = '${SanitizedCppName}' + + # create our own menuBar + ${SanitizedCppName}_menu = _widget_main_window.menuBar().addMenu(f"&{_tag_str}") + + # nest a menu for util/tool launching + ${SanitizedCppName}_launch_menu = ${SanitizedCppName}_menu.addMenu("examples") + else: + print('No O3DE MainWindow') + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) add the first SampleUI + action_launch_sample_ui = ${SanitizedCppName}_launch_menu.addAction("O3DE:SampleUI") + + @Slot() + def clicked_sample_ui(): + while 1: # simple PySide2 test, set to 0 to disable + ui = SampleUI(parent=_widget_main_window, title='O3DE:SampleUI') + ui.show() + break + return + # Add click event to menu bar + action_launch_sample_ui.triggered.connect(clicked_sample_ui) + # --------------------------------------------------------------------- + + + # --------------------------------------------------------------------- + if _widget_main_window: + # (1) and custom external module Qwidget + action_launch_${SanitizedCppName}_dialog = ${SanitizedCppName}_launch_menu.addAction("O3DE:${SanitizedCppName}_dialog") + + @Slot() + def clicked_${SanitizedCppName}_dialog(): + while 1: # simple PySide2 test, set to 0 to disable + try: + import az_qt_helpers + from ${NameLower}_dialog import ${SanitizedCppName}Dialog + az_qt_helpers.register_view_pane('${SanitizedCppName} Popup', ${SanitizedCppName}Dialog) + except Exception as e: + print(f'Error: {e}') + print('Skipping register our ${SanitizedCppName}Dialog with the Editor.') + ${SanitizedCppName}_dialog = ${SanitizedCppName}Dialog(parent=_widget_main_window) + ${SanitizedCppName}_dialog.show() + break + return + # Add click event to menu bar + action_launch_${SanitizedCppName}_dialog.triggered.connect(clicked_${SanitizedCppName}_dialog) + # --------------------------------------------------------------------- + + # end \ No newline at end of file diff --git a/Templates/PythonGem/Template/gem.json b/Templates/PythonGem/Template/gem.json new file mode 100644 index 0000000000..353ad6bf8d --- /dev/null +++ b/Templates/PythonGem/Template/gem.json @@ -0,0 +1,16 @@ +{ + "gem_name": "${Name}", + "display_name": "${Name}", + "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", + "type": "Code", + "summary": "A short description of ${Name}.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "${Name}" + ], + "icon_path": "preview.png", + "requirements": "" +} diff --git a/Templates/PythonGem/Template/preview.png b/Templates/PythonGem/Template/preview.png new file mode 100644 index 0000000000..0f393ac886 --- /dev/null +++ b/Templates/PythonGem/Template/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d +size 2217 diff --git a/Templates/PythonGem/template.json b/Templates/PythonGem/template.json new file mode 100644 index 0000000000..75be757abb --- /dev/null +++ b/Templates/PythonGem/template.json @@ -0,0 +1,216 @@ +{ + "template_name": "PythonGem", + "restricted_name": "o3de", + "restricted_platform_relative_path": "Templates", + "origin": "The primary repo for PythonGem goes here: i.e. http://www.mydomain.com", + "license": "What license PythonGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "PythonGem", + "summary": "A short description of PythonGem.", + "canonical_tags": [], + "user_tags": [ + "PythonGem" + ], + "icon_path": "preview.png", + "copyFiles": [ + { + "file": "CMakeLists.txt", + "origin": "CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_files.cmake", + "origin": "Code/${NameLower}_editor_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_shared_files.cmake", + "origin": "Code/${NameLower}_editor_shared_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/${NameLower}_editor_tests_files.cmake", + "origin": "Code/${NameLower}_editor_tests_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/CMakeLists.txt", + "origin": "Code/CMakeLists.txt", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Include/${Name}/${Name}Bus.h", + "origin": "Code/Include/${Name}/${Name}Bus.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Linux/PAL_linux.cmake", + "origin": "Code/Platform/Linux/PAL_linux.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Mac/PAL_mac.cmake", + "origin": "Code/Platform/Mac/PAL_mac.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Platform/Windows/PAL_windows.cmake", + "origin": "Code/Platform/Windows/PAL_windows.cmake", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorModule.cpp", + "origin": "Code/Source/${Name}EditorModule.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.cpp", + "origin": "Code/Source/${Name}EditorSystemComponent.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}EditorSystemComponent.h", + "origin": "Code/Source/${Name}EditorSystemComponent.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Source/${Name}ModuleInterface.h", + "origin": "Code/Source/${Name}ModuleInterface.h", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Code/Tests/${Name}EditorTest.cpp", + "origin": "Code/Tests/${Name}EditorTest.cpp", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/__init__.py", + "origin": "Editor/Scripts/__init__.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/bootstrap.py", + "origin": "Editor/Scripts/bootstrap.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "Editor/Scripts/${NameLower}_dialog.py", + "origin": "Editor/Scripts/${NameLower}_dialog.py", + "isTemplated": true, + "isOptional": false + }, + { + "file": "gem.json", + "origin": "gem.json", + "isTemplated": true, + "isOptional": false + }, + { + "file": "preview.png", + "origin": "preview.png", + "isTemplated": false, + "isOptional": false + } + ], + "createDirectories": [ + { + "dir": "Assets", + "origin": "Assets" + }, + { + "dir": "Code", + "origin": "Code" + }, + { + "dir": "Editor", + "origin": "Editor" + }, + { + "dir": "Editor/Scripts", + "origin": "Editor/Scripts" + }, + { + "dir": "Code/Include", + "origin": "Code/Include" + }, + { + "dir": "Code/Include/${Name}", + "origin": "Code/Include/${Name}" + }, + { + "dir": "Code/Platform", + "origin": "Code/Platform" + }, + { + "dir": "Code/Platform/Linux", + "origin": "Code/Platform/Linux" + }, + { + "dir": "Code/Platform/Mac", + "origin": "Code/Platform/Mac" + }, + { + "dir": "Code/Platform/Windows", + "origin": "Code/Platform/Windows" + }, + { + "dir": "Code/Source", + "origin": "Code/Source" + }, + { + "dir": "Code/Tests", + "origin": "Code/Tests" + } + ] +} diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 1388366661..f5e5edcf66 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -102,6 +102,10 @@ def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) { } } +def IsAPLogUpload(branchName, jobName) { + return !IsPullRequest(branchName) && jobName.toLowerCase().contains('asset') && env.AP_LOGS_S3_BUCKET +} + def GetRunningPipelineName(JENKINS_JOB_NAME) { // If the job name has an underscore def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_') @@ -431,6 +435,27 @@ def ExportTestScreenshots(Map options, String branchName, String platformName, S } } +def UploadAPLogs(Map options, String branchName, String jobName, String workspace, Map params) { + dir("${workspace}/${ENGINE_REPOSITORY_NAME}") { + projects = params.CMAKE_LY_PROJECTS.split(",") + projects.each{ project -> + def apLogsPath = "${project}/user/log" + def s3UploadScriptPath = "scripts/build/tools/upload_to_s3.py" + if(env.IS_UNIX) { + pythonPath = "${options.PYTHON_DIR}/python.sh" + } + else { + pythonPath = "${options.PYTHON_DIR}/python.cmd" + } + def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " + + "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " + + "--search_subdirectories True --key_prefix ${env.JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName}" + + "--extra-args {\"ACL\": \"bucket-owner-full-control\"}" + palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false) + } + } + } + def PostBuildCommonSteps(String workspace, boolean mount = true) { echo 'Starting post-build common steps...' @@ -494,6 +519,14 @@ def CreateExportTestScreenshotsStage(Map pipelineConfig, String branchName, Stri } } +def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String jobName, String workspace, Map params) { + return { + stage("${jobName}_upload_ap_logs") { + UploadAPLogs(pipelineConfig, branchName, jobName, workspace, params) + } + } +} + def CreateTeardownStage(Map environmentVars) { return { stage('Teardown') { @@ -543,6 +576,9 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node } } + if (IsAPLogUpload(branchName, build_job_name)) { + CreateUploadAPLogsStage(pipelineConfig, branchName, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call() + } // All other errors will be raised outside the retry block currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' currentException = e.toString() diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt index 625909e214..a09d823950 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt @@ -12,7 +12,12 @@ libxcb-xinerama0 # For Qt plugins at runtime libxcb-xinput0 # For Qt plugins at runtime libfontconfig1-dev # For Qt plugins at runtime libcurl4-openssl-dev # For HttpRequestor -libsdl2-dev # for WWise/Audio +libsdl2-dev # For WWise/Audio +libxcb-xkb-dev # For xcb keyboard input +libxkbcommon-x11-dev # For xcb keyboard input +libxkbcommon-dev # For xcb keyboard input +libxcb-xfixes0-dev # For mouse input +libxcb-xinput-dev # For mouse input zlib1g-dev mesa-common-dev diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt index 2cbbe58b38..e0e03cda90 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt @@ -16,5 +16,7 @@ libsdl2-dev # for WWise/Audio libxcb-xkb-dev # For xcb keyboard input libxkbcommon-x11-dev # For xcb keyboard input libxkbcommon-dev # For xcb keyboard input +libxcb-xfixes0-dev # For mouse input +libxcb-xinput-dev # For mouse input zlib1g-dev mesa-common-dev