"""
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
Lumberyard Legacy Mesh Component to Atom Mesh Component Conversion Script
"""
from LegacyConversionHelpers import *
class Material_Assignment_Info(object):
def __init__(self, slotAssetId, assignmentAssetId):
self.slotAssetId = slotAssetId
self.assignmentAssetId = assignmentAssetId
class Material_Component_Converter(object):
"""
Some material related functions. Since there is no material component in legacy, this doesn't inherit from Component_Converter like other similar classes
"""
def __init__(self, assetCatalogHelper):
self.assetCatalogHelper = assetCatalogHelper
def create_material_map_entry(self, slotAssetId, materialAssetId):
#
#
#
#
#
#
#
#
#
#
#
#
#
pair = create_xml_element_from_string("")
isMapAssignment = True
pair.append(self.create_material_assignment_id(slotAssetId, isMapAssignment))
pair.append(self.create_material_assignment(materialAssetId))
return pair
def create_material_asset_string_from_assetid(self, assetId):
hint_path = ""
if assetId != "{00000000-0000-0000-0000-000000000000}:0":
hint_path = self.assetCatalogHelper.assetIdToRelativePathDict[assetId]
return "".join(("id=", assetId, ",type={522C7BE0-501D-463E-92C6-15184A2B7AD8},hint={", hint_path, "},loadBehavior=1"))
def create_material_assignment_id(self, slotAssetId, isMapAssignment):
# Material assignment ids are serialized differently for the map vs the EditorMaterialComponentSlot
field = ""
if isMapAssignment:
field = "value1"
else:
field = "id"
#
#
#
#
#
#
#
materialAssignmentId = create_xml_element_from_string("".join(("")))
# TODO - for now, always using the default lod index 18446744073709551615, which applies to all lods that don't have a specific override
materialAssignmentId_lodIndex = create_xml_element_from_string("")
materialAssignmentId_AssetId = create_xml_element_from_string("")
uuid = get_uuid_from_assetId(slotAssetId)
materialAssignmentId_AssetId_Uuid = create_xml_element_from_string("".join(("")))
subId = get_subid_from_assetId(slotAssetId)
materialAssignmentId_AssetId_subid = xml.etree.ElementTree.Element("Class", {'name' : "unsigned int", 'field' : "subId", 'value' : subId, 'type' : "{43DA906B-7DEF-4CA8-9790-854106D3F983}"})
materialAssignmentId_AssetId.append(materialAssignmentId_AssetId_Uuid)
materialAssignmentId_AssetId.append(materialAssignmentId_AssetId_subid)
materialAssignmentId.append(materialAssignmentId_lodIndex)
materialAssignmentId.append(materialAssignmentId_AssetId)
return materialAssignmentId
def create_editor_material_assignment_slot(self, slotAssetId, materialAssetId, isDefaultSlot):
field = ""
if isDefaultSlot:
field = "defaultMaterialSlot"
else:
field = "element"
#
#
#
#
materialComponentSlot = create_xml_element_from_string("".join(("")))
isMapAssignment = False
materialAssignmentId = self.create_material_assignment_id(slotAssetId, isMapAssignment)
materialAsset = self.create_material_asset(materialAssetId)
defaultPropertyOverrides = self.create_material_property_overrides()
materialComponentSlot.append(materialAssignmentId)
materialComponentSlot.append(materialAsset)
materialComponentSlot.append(defaultPropertyOverrides)
return materialComponentSlot
def create_material_assignment(self, materialAssetId):
#
#
#
#
materialAssignment = create_xml_element_from_string("")
materialAssignment.append(self.create_material_asset(materialAssetId))
materialAssignment.append(self.create_material_property_overrides())
return materialAssignment
def create_material_asset(self, materialAssetId):
materialAssetString = self.create_material_asset_string_from_assetid(materialAssetId)
materialAsset = xml.etree.ElementTree.Element("Class", {'name' : "Asset", 'field' : "materialAsset", 'value' : materialAssetString, 'version' : "2", 'type' : "{77A19D40-8731-4D3C-9041-1B43047366A4}"})
return materialAsset
def create_material_property_overrides(self):
return create_xml_element_from_string("")
def create_material_component_with_material_assignments(self, atomMaterialInDefaultSlotAssetId, materialAssignmentList):
# TODO - the relative path might not be in the same project/gem folder as the .slice
#
#
#
#
#
#
#
#
#
#
#
#
# ... (map entries)
#
#
#
editorMaterialComponent = create_xml_element_from_string("")
# can't use create_xml_element_from_string here because of the spaces in the class name
editorRenderComponentAdapter = xml.etree.ElementTree.Element("Class", {'name' : "EditorRenderComponentAdapter", 'field' : "BaseClass1", 'type' : "{DF046B40-536D-5D59-96EF-7A40DA6191B2}"})
editorComponentAdapter = xml.etree.ElementTree.Element("Class", {'name' : "EditorComponentAdapter", 'field' : "BaseClass1", 'version' : "1", 'type' : "{6C4D4557-3728-56F8-A0FF-A309C3AAB853}"})
editorComponentBase = create_xml_element_from_string("")
component = create_xml_element_from_string("")
u64 = create_xml_element_from_string("")
component.append(u64)
editorComponentBase.append(component)
editorComponentAdapter.append(editorComponentBase)
materialComponentController = create_xml_element_from_string("")
materialComponentConfig = create_xml_element_from_string("")
componentConfig = create_xml_element_from_string("")
materialMap = create_xml_element_from_string("")
# {00000000-0000-0000-0000-000000000000}:0 is the default slot
defaultMaterialAssignmentMapEntry = self.create_material_map_entry("{00000000-0000-0000-0000-000000000000}:0", atomMaterialInDefaultSlotAssetId)
materialMap.append(defaultMaterialAssignmentMapEntry)
for atomMaterial in materialAssignmentList:
if atomMaterial.assignmentAssetId:
materialMapElement = self.create_material_map_entry(atomMaterial.slotAssetId, atomMaterial.assignmentAssetId)
materialMap.append(materialMapElement)
materialComponentConfig.append(componentConfig)
materialComponentConfig.append(materialMap)
materialComponentController.append(materialComponentConfig)
editorComponentAdapter.append(materialComponentController)
editorRenderComponentAdapter.append(editorComponentAdapter)
isDefaultSlot = True
defaultMaterialComponentSlot = self.create_editor_material_assignment_slot("{00000000-0000-0000-0000-000000000000}:0", atomMaterialInDefaultSlotAssetId, isDefaultSlot)
#
# ... (slots)
isDefaultSlot = False
materialsSlots = create_xml_element_from_string("Class name=\"AZStd::vector\" field=\"materialSlots\" type=\"{7FDDDE36-46C8-5DBC-8566-E792AA358BD9}\"")
for atomMaterial in materialAssignmentList:
if atomMaterial.assignmentAssetId:
materialSlotElement = self.create_editor_material_assignment_slot(atomMaterial.slotAssetId, atomMaterial.assignmentAssetId, isDefaultSlot)
materialsSlots.append(materialSlotElement)
else:
# Use the default material if none was specified
materialSlotElement = self.create_editor_material_assignment_slot(atomMaterial.slotAssetId, self.get_default_material_assetid(), isDefaultSlot)
materialsSlots.append(materialSlotElement)
#
#
editorMaterialComponent.append(editorRenderComponentAdapter)
editorMaterialComponent.append(defaultMaterialComponentSlot)
return editorMaterialComponent
def convert_legacy_mtl_relative_path_to_atom_material_assetid(self, normalizedProjectDir, oldMaterialRelativePath, oldFbxRelativePathWithoutExtension):
if len(oldMaterialRelativePath) == 0:
# if no material was used, try to find one that matches the name of the fbx (in the event the fbx only has a single material)
cacheMaterialRelativePath = "".join((oldFbxRelativePathWithoutExtension, ".azmaterial"))
if cacheMaterialRelativePath in self.assetCatalogHelper.relativePathToAssetIdDict:
assetId = self.assetCatalogHelper.relativePathToAssetIdDict[cacheMaterialRelativePath]
return assetId
else:
return self.get_default_material_assetid()
# TODO - doesn't work if .mtl is in the path
atomRelativePath = oldMaterialRelativePath.replace('.mtl', '.azmaterial')
assetId = self.assetCatalogHelper.get_asset_id_from_relative_path(atomRelativePath)
if assetId:
return assetId
return self.get_default_material_assetid()
def get_default_material_assetid(self):
return "{00000000-0000-0000-0000-000000000000}:0"
#return "{2A83451E-0FE6-508E-BAA2-6142AAA53C42}:0" # AtomStarterGame\Materials\Magenta.material" - makes it obvious we couldn't find a material
def convert_legacy_mtl_relative_path_to_atom_material_list(self, normalizedProjectDir, oldMaterialRelativePath, oldFbxRelativePathWithoutExtension, isActor):
materialList = []
# Find all the materials produced by the fbx
cacheFbxPath = ""
if isActor:
cacheFbxPath = "".join((oldFbxRelativePathWithoutExtension, ".actor"))
else:
cacheFbxPath = "".join((oldFbxRelativePathWithoutExtension, ".azmodel"))
if cacheFbxPath in self.assetCatalogHelper.relativePathToAssetIdDict:
fbxAssetId = self.assetCatalogHelper.relativePathToAssetIdDict[cacheFbxPath]
# get the guid portion of the id
subIdSeparatorIndex = fbxAssetId.find(":")
fbxGuid = fbxAssetId[:subIdSeparatorIndex]
fbxProductList = self.assetCatalogHelper.assetUuidToAssetIdsDict[fbxGuid]
for productAssetId in fbxProductList:
relativePath = self.assetCatalogHelper.assetIdToRelativePathDict[productAssetId]
if relativePath.endswith(".azmaterial"):
# we found a product material.
slot = productAssetId
assignment = ""
# strip the _#### from it
extraCharactersIndex = relativePath.rfind("_")
convertedRelativePath = "".join((relativePath[:extraCharactersIndex], ".azmaterial"))
if convertedRelativePath in self.assetCatalogHelper.relativePathToAssetIdDict:
# try to find an atom material with the same name
assignment = self.assetCatalogHelper.relativePathToAssetIdDict[convertedRelativePath]
elif cacheFbxPath.replace(".azmodel", ".azmaterial") in self.assetCatalogHelper.relativePathToAssetIdDict:
# An fbx with only 1 submesh is going to produce an azmaterial that is fbxname_materialname
# even though this is often redundant like rivervista_01_RiverVista01MAT.azmaterial.
# Legacy .mtl files like this would end up with a rivervista_01.mtl that was a multi-material
# but only had a single sub-material called RiverVista01MAT.
# The legacy material converter treats this case as a single material, and instead of
# naming the file rivervista_01_RiverVista01MAT.material, it just calls it rivervista_01.material.
# However, the atom model builder still follows the fbxname_materialname convention, so in this case
# we should look and see if the material converter created an rivervist_01.material
assignment = self.assetCatalogHelper.relativePathToAssetIdDict[cacheFbxPath.replace(".azmodel", ".azmaterial")]
else:
assignment = self.get_default_material_assetid()
print("Could not match {0} to a corresponding source atom material".format(convertedRelativePath))
materialList.append(Material_Assignment_Info(slot, assignment))
return materialList