Skip to content

Commit b4be436

Browse files
new: Allow to use the LegacyComponentConverter between a lumberyard and o3de project (o3de#17397)
* fix: use the correct cache location and fix for python 3.10 * new: Allow to use specific asset catalog as new arg * fix: Arguments parsing more than one arg * clean: handle review comments * clean: handle review comments 2 * fix: Remove default and unused material slot from entry * new: Support non uniform scale conversion --------- Signed-off-by: guillaume-haerinck <[email protected]>
1 parent 1efe11a commit b4be436

File tree

5 files changed

+207
-33
lines changed

5 files changed

+207
-33
lines changed

Gems/AtomLyIntegration/CommonFeatures/Assets/Editor/Scripts/LegacyContentConversion/LegacyActorComponentConverter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def is_this_the_component_im_looking_for(self, xmlElement, parent):
3737

3838
def gather_info_for_conversion(self, xmlElement, parent):
3939
# We don't need to modify the actor component, but we do need to add a material component
40-
for possibleActorAssetComponent in xmlElement.getchildren():
40+
for possibleActorAssetComponent in xmlElement.iter():
4141
if "field" in possibleActorAssetComponent.keys() and possibleActorAssetComponent.get("field") == "ActorAsset" and "value" in possibleActorAssetComponent.keys():
4242
assetId = possibleActorAssetComponent.get("value")
4343

Gems/AtomLyIntegration/CommonFeatures/Assets/Editor/Scripts/LegacyContentConversion/LegacyComponentConverter.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515
project and attempts to convert the following components into something
1616
reasonably similar that renders in Atom:
1717
Mesh
18+
Material
19+
PointLight
1820
Actor
1921
2022
2123
Do materials get carried over?
2224
================================================
2325
For the mesh component, this script will attempt to create an atom mesh component
2426
that uses the same material, pre-supposing that you have already run the
25-
LegacyMaterialConverter.py script sto generate Atom .material files out of legacy .mtl files
27+
LegacyAssetConverter script to generate Atom .material files out of legacy .mtl files.
28+
(Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\SDK\Maya\Scripts\Python\legacy_asset_converter\main.py)
2629
For meshes that only have one sub-mesh, this is straightforward as the mesh will only
2730
have one material to apply, and this script will look for a material with the same
2831
name but a .material extension.
@@ -35,14 +38,26 @@
3538
re-named the submaterials, it should find a match. This applies to both the
3639
ActorComponent and the MeshComponent
3740
41+
42+
What does this script needs?
43+
================================================
44+
This script will parse the assetcatalog.xml from the cache folder to get asset ids and path.
45+
This file is in binary by default, you need to rebuild the engine with a modification to make it useable by this script.
46+
- In lumberyard, modify dev\Code\Tools\AssetProcessor\native\AssetManager\AssetCatalog.cpp line 343 to use AZ::ObjectStream::ST_XML instead of ST_BINARY
47+
- In o3de (if you use assetCatalogOverridePath), modify Code\Framework\AzFramework\AzFramework\Asset\AssetCatalog.cpp
48+
and Code\Tools\AssetProcessor\native\AssetManager\AssetCatalog.cpp in the same fashion
49+
50+
Note that after the modification and rebuild, you will need to delete the cache folder and launch the engine for the catalog to be rebuilt
51+
3852
How do I run this script from a command line?
3953
================================================
4054
1) Check out any .slice, .layer, .ly, and .cry files you want to convert from source control
4155
- This script will remove the legacy components entirely, so make sure you have your files
4256
backed up before you run this script in case you want to run it again
43-
2) From the Lumberyard root folder, run LegacyComponentConverter.py project=<ProjectName> --include_gems
44-
- --include_gems is optional
45-
- if you include Gems, it will run all all Gems, not just the ones enabled by your project
57+
2) From the Lumberyard root folder, run LegacyComponentConverter.py -project=<ProjectName> -include_gems -assetCatalogOverridePath=<ExternalProjectPath\Cache\pc\assetcatalog.xml>
58+
- -include_gems is optional. If you include Gems, it will run all all Gems, not just the ones enabled by your project
59+
- -assetCatalogOverridePath is optional. It will replace mesh/material asset id from the source project to this target project
60+
based on relative asset path from the project root (in general, Objects/Models/yourasset.fbx)
4661
4762
4863
What are the artifacts of this script?
@@ -59,12 +74,10 @@
5974
CONVERTED_LOG_NAME = "ComponentConversion_ConvertedLegacyFiles.log"
6075
UNCONVERTED_LOG_NAME = "ComponentConversion_UnsupportedLegacyFiles.log"
6176
STATS_LOG_NAME = "ComponentConversion_LegacyComponentStats.log"
62-
BUILD_PATH = None
63-
GEMS_PATH = None
6477

6578
# Normal imports
6679
import sys
67-
import xml.etree.ElementTree
80+
import xml.etree.ElementTree as ET
6881
import time
6982
from zipfile import ZipFile
7083
import tempfile
@@ -76,8 +89,9 @@
7689
from LegacyMaterialComponentConverter import *
7790
from LegacyActorComponentConverter import *
7891
from LegacyPointLightComponentConverter import *
92+
from LegacyTransformComponentConverter import *
7993

80-
BUILD_PATH = "./"
94+
BUILD_PATH = "./" # Use current working directory, we expect to be in lumberyard dev folder
8195
GEMS_PATH = os.path.join(BUILD_PATH, "Gems")
8296

8397
class Component_File(object):
@@ -169,6 +183,7 @@ def gather_elements(self):
169183
componentConverters.append(Mesh_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
170184
componentConverters.append(Actor_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
171185
componentConverters.append(Point_Light_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
186+
componentConverters.append(Transform_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
172187

173188
if self.is_valid_xml():
174189
root = self.xml.getroot()
@@ -190,7 +205,10 @@ def gather_elements(self):
190205
# TODO - we're about to change the tree structure while iterating, which is apparently undefined but appears to work. Might be better to just build up a list of things that need to be modified, then do a second pass to replace the legacy component
191206
# Seems to be okay since we only change or add elements, never remove entirely
192207
componentConverter.convert(child, parent)
193-
self.xml._setroot(root)
208+
self.xml._setroot(root)
209+
# pretty print
210+
ET.indent(self.xml, space='\t')
211+
194212
print("finished parsing {0}".format(self.filename))
195213

196214
def can_be_converted(self):
@@ -265,12 +283,12 @@ def getUpdatedStatsCollector(self):
265283
def main():
266284
'''sys.__name__ wrapper function'''
267285

268-
msgStr = "This tool will scan all of your project's level/layer/slice files\n\
286+
msgStr = "This tool will scan all of your lumberyard project's level/layer/slice files\n\
269287
convert any compatible legacy components into the equivalent Atom components\n\
270288
This script will overwrite the original files, and will remove the legacy components\n\
271289
upon conversion, decimating the previous contents of those components.\n"
272290

273-
commandLineOptions = Common_Command_Line_Options(sys.argv[0], sys.argv[1])
291+
commandLineOptions = Common_Command_Line_Options(sys.argv)
274292
if commandLineOptions.isHelp:
275293
print (commandLineOptions.helpString)
276294
return
@@ -281,8 +299,13 @@ def main():
281299
extensionList = [".slice", ".layer", ".ly", ".cry"]
282300
fileList = get_file_list(commandLineOptions.projectName, commandLineOptions.includeGems, extensionList, BUILD_PATH, GEMS_PATH)
283301

284-
assetCatalogDictionaries = get_asset_catalog_dictionaries(BUILD_PATH, commandLineOptions.projectName)
285-
302+
if commandLineOptions.assetCatalogOverridePath:
303+
assetCatalogPath = commandLineOptions.assetCatalogOverridePath
304+
assetCatalogDictionaries = get_asset_catalog_dictionaries(assetCatalogPath)
305+
else:
306+
assetCatalogPath = os.path.join("Cache", commandLineOptions.projectName, get_default_asset_platform(), commandLineOptions.projectName, "assetcatalog.xml")
307+
assetCatalogDictionaries = get_asset_catalog_dictionaries(assetCatalogPath)
308+
286309
# Create a log file to store converted component file filenames
287310
# and to check to see if the component file has already been converted.
288311
convertedLogFile = Log_File(filename="{0}\\{1}".format(BUILD_PATH, CONVERTED_LOG_NAME))

Gems/AtomLyIntegration/CommonFeatures/Assets/Editor/Scripts/LegacyContentConversion/LegacyConversionHelpers.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"""
99

1010
import os
11+
import platform
1112
import xml.etree.ElementTree
1213

1314
class Stats_Collector(object):
@@ -101,30 +102,36 @@ class Common_Command_Line_Options(object):
101102
"""
102103
Some common options/parsing
103104
"""
104-
def __init__(self, argv0, argv1):
105-
arguments = argv1.split('-')
106-
105+
def __init__(self, args):
107106
self.projectName = ""
107+
self.assetCatalogOverridePath = ""
108108
self.includeGems = False
109109
self.useP4 = False
110110
self.endsWithStr = ""
111111
self.isHelp = False
112-
self.helpString = "usage: {0} -project=<project name> -include_gems -ends_with=<filter> -use_p4\n\
112+
self.helpString = f"usage: {args[0]} -project=<project name> -include_gems -ends_with=<filter> -use_p4 -assetCatalogOverridePath=<path>\n\
113113
E.g.:\n\
114-
{1} -project=StarterGame -include_gems\n\
115-
-project is required.\n\
114+
-project=StarterGame -include_gems\n\
115+
-project is required. Path is relative\n\
116116
-include_gems is optional, and by default gems will not be included.\n\
117117
-ends_with is optional. It could be used to filter for a specific file (--ends_with=default.mtl)\n\
118-
-use_p4 is optional. It will use the p4 command line to check out files that are edited in your default changelist".format(argv0, argv0)
119-
120-
for argument in arguments:
121-
argument = argument.rstrip(" ")
122-
if argument == "h" or argument == "help" or argv1 == "?":
118+
-use_p4 is optional. It will use the p4 command line to check out files that are edited in your default changelist\n\
119+
-assetCatalogOverridePath is optional. It will use asset ids from this file instead of using the current project asset catalog\n\
120+
(match via relative asset path to the project root folder)"
121+
122+
123+
for argument in args:
124+
argument = argument.rstrip(" ").lstrip("-")
125+
if argument == "h" or argument == "help" or argument == "?":
123126
self.isHelp = True
124127
elif argument.startswith("project"):
125128
projectArgs = argument.split("=")
126129
if len(projectArgs) > 1:
127130
self.projectName = projectArgs[1]
131+
elif argument.startswith("assetCatalogOverridePath"):
132+
projectArgs = argument.split("=")
133+
if len(projectArgs) > 1:
134+
self.assetCatalogOverridePath = projectArgs[1]
128135
elif argument == "include_gems":
129136
self.includeGems = True
130137
elif argument == "use_p4":
@@ -204,7 +211,7 @@ def get_asset_id_from_relative_path(self, relativePath):
204211
return self.relativePathToAssetIdDict[relativePath]
205212
return ""
206213

207-
def get_asset_catalog_dictionaries(buildPath, projectName):
214+
def get_asset_catalog_dictionaries(assetCatalogPath):
208215
"""
209216
This function pre-supposes that you have modified AssetCatalog::SaveRegistry_Impl
210217
in Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp
@@ -218,7 +225,6 @@ def get_asset_catalog_dictionaries(buildPath, projectName):
218225
relativePathToAssetIdDict[""] = "{00000000-0000-0000-0000-000000000000}:0"
219226
assetIdToRelativePathDict = {}
220227
assetUuidToAssetIdsDict = {}
221-
assetCatalogPath = os.path.join(projectName, "Cache", "pc", "assetcatalog.xml")
222228
assetCatalogXml = xml.etree.ElementTree.parse(assetCatalogPath)
223229
for possibleAssetInfo in assetCatalogXml.getroot().iter('Class'):
224230
if "name" in possibleAssetInfo.keys() and possibleAssetInfo.get("name") == "AssetInfo":
@@ -278,6 +284,12 @@ def get_subid_from_assetId(assetId):
278284
separatorIndex = assetId.find(":") + 1
279285
return assetId[separatorIndex:]
280286

287+
def get_default_asset_platform():
288+
host_platform_to_asset_platform_map = { 'windows': 'pc',
289+
'linux': 'linux',
290+
'darwin': 'mac' }
291+
return host_platform_to_asset_platform_map.get(platform.system().lower(), "")
292+
281293
class Component_Converter(object):
282294
"""
283295
Converter base class

Gems/AtomLyIntegration/CommonFeatures/Assets/Editor/Scripts/LegacyContentConversion/LegacyMaterialComponentConverter.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,9 @@ def create_material_component_with_material_assignments(self, atomMaterialInDefa
153153
componentConfig = create_xml_element_from_string("<Class name=\"ComponentConfig\" field=\"BaseClass1\" version=\"1\" type=\"{0A7929DF-2932-40EA-B2B3-79BC1C3490D0}\"/>")
154154
materialMap = create_xml_element_from_string("<Class name=\"AZStd::unordered_map\" field=\"materials\" type=\"{50F6716F-698B-5A6C-AACD-940597FDEC24}\">")
155155

156-
# {00000000-0000-0000-0000-000000000000}:0 is the default slot
157-
defaultMaterialAssignmentMapEntry = self.create_material_map_entry("{00000000-0000-0000-0000-000000000000}:0", atomMaterialInDefaultSlotAssetId)
158-
materialMap.append(defaultMaterialAssignmentMapEntry)
159-
156+
defaultSlot = "{00000000-0000-0000-0000-000000000000}:0"
160157
for atomMaterial in materialAssignmentList:
161-
if atomMaterial.assignmentAssetId:
158+
if atomMaterial.assignmentAssetId and atomMaterial.assignmentAssetId != defaultSlot:
162159
materialMapElement = self.create_material_map_entry(atomMaterial.slotAssetId, atomMaterial.assignmentAssetId)
163160
materialMap.append(materialMapElement)
164161

@@ -169,7 +166,7 @@ def create_material_component_with_material_assignments(self, atomMaterialInDefa
169166
editorRenderComponentAdapter.append(editorComponentAdapter)
170167

171168
isDefaultSlot = True
172-
defaultMaterialComponentSlot = self.create_editor_material_assignment_slot("{00000000-0000-0000-0000-000000000000}:0", atomMaterialInDefaultSlotAssetId, isDefaultSlot)
169+
defaultMaterialComponentSlot = self.create_editor_material_assignment_slot(defaultSlot, atomMaterialInDefaultSlotAssetId, isDefaultSlot)
173170

174171
# <Class name="AZStd::vector" field="materialSlots" type="{7FDDDE36-46C8-5DBC-8566-E792AA358BD9}">
175172
# ... (slots)

0 commit comments

Comments
 (0)