""" 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