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