LegacyMeshComponentConverter.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. Lumberyard Legacy Mesh Component to Atom Mesh Component Conversion Script
  6. """
  7. from LegacyConversionHelpers import *
  8. from LegacyMaterialComponentConverter import *
  9. class Mesh_Component_Converter(Component_Converter):
  10. """
  11. Converts point lights
  12. """
  13. def __init__(self, assetCatalogHelper, statsCollector, normalizedProjectDir):
  14. Component_Converter.__init__(self, assetCatalogHelper, statsCollector)
  15. # These are constant for every component in the file
  16. self.materialComponentConverter = Material_Component_Converter(assetCatalogHelper)
  17. self.normalizedProjectDir = normalizedProjectDir
  18. # These need to be reset between each component
  19. self.newAssetId = ""
  20. self.oldMaterialRelativePath = ""
  21. self.oldFbxRelativePathWithoutExtension = ""
  22. def is_this_the_component_im_looking_for(self, xmlElement, parent):
  23. if "name" in xmlElement.keys() and xmlElement.get("name") == "EditorMeshComponent":
  24. return True
  25. return False
  26. def gather_info_for_conversion(self, xmlElement, parent):
  27. # First, get the data that we need
  28. #<Class name="EditorMeshComponent" field="element" version="1" type="{FC315B86-3280-4D03-B4F0-5553D7D08432}">
  29. # <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
  30. # <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
  31. # <Class name="AZ::u64" field="Id" value="17006471516512517700" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  32. # </Class>
  33. # </Class>
  34. # <Class name="MeshComponentRenderNode" field="Static Mesh Render Node" version="1" type="{46FF2BC4-BEF9-4CC4-9456-36C127C310D7}">
  35. # <Class name="bool" field="Visible" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
  36. # <Class name="Asset" field="Static Mesh" value="id={BCD2BB63-338F-53BE-98E1-BF847138B78E}:3250cdc0,type={C2869E3B-DDA0-4E01-8FE3-6770D788866B},hint={objects/airship/airship_pod_outerwalls.cgf}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
  37. # <Class name="AzFramework::SimpleAssetReference&lt;LmbrCentral::MaterialAsset&gt;" field="Material Override" version="1" type="{B7B8ECC7-FF89-4A76-A50E-4C6CA2B6E6B4}">
  38. # <Class name="SimpleAssetReferenceBase" field="BaseClass1" version="1" type="{E16CA6C5-5C78-4AD9-8E9B-F8C1FB4D1DB8}">
  39. # <Class name="AZStd::string" field="AssetPath" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
  40. for editorMeshComponentChild in xmlElement:
  41. if "name" in editorMeshComponentChild.keys() and editorMeshComponentChild.get("name") == "MeshComponentRenderNode":
  42. for meshComponentRenderNodeChild in editorMeshComponentChild:
  43. if "name" in meshComponentRenderNodeChild.keys() and meshComponentRenderNodeChild.get("name") == "Asset":
  44. # We've found the mesh asset, now extract the assetId and hit
  45. assetId = meshComponentRenderNodeChild.get("value")
  46. # Legacy assetId looks like this "id={BCD2BB63-338F-53BE-98E1-BF847138B78E}:3250cdc0,type={C2869E3B-DDA0-4E01-8FE3-6770D788866B},hint={objects/airship/airship_pod_outerwalls.cgf}
  47. # atomAssetId looks like this "{BCD2BB63-338F-53BE-98E1-BF847138B78E}:########,
  48. # newAssetId should look like "id={BCD2BB63-338F-53BE-98E1-BF847138B78E}:########,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/airship/airship_pod_outerwalls.fbx.azmodel}"
  49. #value"id={E01FB8B5-D2B3-52D8-BC36-644FC0E3B5F4}:268435463,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/props/barrel_01.fbx.azmodel}"
  50. # get the relative path to the mesh
  51. meshPathStartIndex = assetId.find("hint={")
  52. meshPathStartIndex += len("hint={")
  53. meshPathEndIndex = assetId.find(".cgf")
  54. self.oldFbxRelativePathWithoutExtension = assetId[meshPathStartIndex: meshPathEndIndex]
  55. # swap the sub-id for the atom model sub-id
  56. atomModelRelativePath = "{0}.fbx.azmodel".format(self.oldFbxRelativePathWithoutExtension)
  57. if atomModelRelativePath in self.assetCatalogHelper.relativePathToAssetIdDict:
  58. atomAssetId = self.assetCatalogHelper.relativePathToAssetIdDict[atomModelRelativePath]
  59. atomSubId = atomAssetId[atomAssetId.find(":") + 1:]
  60. # go from string->int->hex->string to get the hex number as a string
  61. hexSubId = str(hex(int(atomSubId)))
  62. # remove the leading characters to get plain hex
  63. hexString = hexSubId[hexSubId.find("x") + 1:]
  64. subIdStartIndex = assetId.find(":")
  65. subIdStartIndex += 1
  66. subIdEndIndex = assetId.find(",")
  67. subIdReplacement = hexString
  68. # swap the type for the atom model type
  69. typeStartIndex = assetId.find("type={")
  70. typeStartIndex += len("type={")
  71. typeEndIndex = assetId.find("},hint")
  72. typeReplacement = "2C7477B6-69C5-45BE-8163-BCD6A275B6D8"
  73. # swap the hint for the atom model extension
  74. extensionStartIndex = assetId.find(".cgf")
  75. extensionEndIndex = extensionStartIndex + len(".cgf")
  76. extensionReplacement = ".azmodel"
  77. self.newAssetId = "".join((assetId[:subIdStartIndex], subIdReplacement, assetId[subIdEndIndex:typeStartIndex], typeReplacement, assetId[typeEndIndex:extensionStartIndex], extensionReplacement, assetId[extensionEndIndex:]))
  78. else:
  79. # we couldn't find the atom model (it was probably a .cgf instead of a .fbx in source)
  80. self.newAssetId = "id={00000000-0000-0000-0000-000000000000}:00000000,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={}"
  81. print("Could not find {0} in the asset catalog. Make sure the corresponding source file ends in .fbx not .cgf, and that the asset has finished processing with no errors.".format(atomModelRelativePath))
  82. elif "field" in meshComponentRenderNodeChild.keys() and meshComponentRenderNodeChild.get("field") == "Material Override":
  83. for simpleAssetReferenceChild in meshComponentRenderNodeChild:
  84. if "name" in simpleAssetReferenceChild.keys() and simpleAssetReferenceChild.get("name") == "SimpleAssetReferenceBase":
  85. for simpleAssetReferenceBaseChild in simpleAssetReferenceChild:
  86. if "field" in simpleAssetReferenceBaseChild.keys() and simpleAssetReferenceBaseChild.get("field") == "AssetPath" and "value" in simpleAssetReferenceBaseChild.keys():
  87. # We've found the material override
  88. self.oldMaterialRelativePath = simpleAssetReferenceBaseChild.get("value")
  89. def create_adapter_with_new_model_assetId(self, assetIdValue):
  90. # <Class name="EditorComponentAdapter&lt;AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig &gt;" field="BaseClass1" version="1" type="{52DFE044-18C1-5861-BA2A-EDB61107FEE9}">
  91. # <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
  92. # <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
  93. # <Class name="AZ::u64" field="Id" value="4528867579411318958" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  94. # </Class>
  95. # </Class>
  96. # <Class name="AZ::Render::MeshComponentController" field="Controller" type="{D0F35FAC-4194-4C89-9487-D000DDB8B272}">
  97. # <Class name="AZ::Render::MeshComponentConfig" field="Configuration" type="{63737345-51B1-472B-9355-98F99993909B}">
  98. # <Class name="Asset" field="ModelAsset" value="id={509D78D3-2196-50C2-808C-FEDC3C31380D}:10000007,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/suzanne.fbx.azmodel}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
  99. # <Class name="bool" field="ExcludeFromReflectionCubeMaps" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
  100. # </Class>
  101. # </Class>
  102. # </Class>
  103. # </Class>
  104. #<Class name="AZ::Render::EditorMeshComponent" field="element" version="1" type="{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9}">
  105. # <Class name="EditorRenderComponentAdapter&lt;AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig &gt;" field="BaseClass1" type="{3D614286-9164-53B5-833B-4F98D2820BA7}">
  106. # <Class name="EditorComponentAdapter&lt;AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig &gt;" field="BaseClass1" version="1" type="{52DFE044-18C1-5861-BA2A-EDB61107FEE9}">
  107. # <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
  108. # <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
  109. # <Class name="AZ::u64" field="Id" value="4528867579411318958" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  110. # </Class>
  111. # </Class>
  112. # <Class name="AZ::Render::MeshComponentController" field="Controller" type="{D0F35FAC-4194-4C89-9487-D000DDB8B272}">
  113. # <Class name="AZ::Render::MeshComponentConfig" field="Configuration" type="{63737345-51B1-472B-9355-98F99993909B}">
  114. # <Class name="Asset" field="ModelAsset" value="id={509D78D3-2196-50C2-808C-FEDC3C31380D}:10000007,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/suzanne.fbx.azmodel}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
  115. # <Class name="bool" field="ExcludeFromReflectionCubeMaps" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
  116. # </Class>
  117. # </Class>
  118. # </Class>
  119. # </Class>
  120. #</Class>
  121. editorRenderComponentAdapter = xml.etree.ElementTree.Element("Class", {'name': "EditorRenderComponentAdapter<AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig >", 'field': "BaseClass1", 'type': "{3D614286-9164-53B5-833B-4F98D2820BA7}"})
  122. editorComponentAdapter = xml.etree.ElementTree.Element("Class", {'name': "EditorComponentAdapter<AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig >", 'field': "BaseClass1", 'version': "1", 'type': "{52DFE044-18C1-5861-BA2A-EDB61107FEE9}"})
  123. editorComponentBase = xml.etree.ElementTree.Element("Class", {'name': "EditorComponentBase", 'field': "BaseClass1", 'version': "1", 'type': "{D5346BD4-7F20-444E-B370-327ACD03D4A0}"})
  124. component = xml.etree.ElementTree.Element("Class", {'name': "AZ::Component", 'field': "BaseClass1", 'type': "{EDFCB2CF-F75D-43BE-B26B-F35821B29247}"})
  125. u64 = xml.etree.ElementTree.Element("Class", {'name': "AZ::u64", 'field': "Id", 'value': "4528867579411318958", 'type': "{D6597933-47CD-4FC8-B911-63F3E2B0993A}"})
  126. component.append(u64)
  127. editorComponentBase.append(component)
  128. meshComponentController = create_xml_element_from_string("<Class name=\"AZ::Render::MeshComponentController\" field=\"Controller\" type=\"{D0F35FAC-4194-4C89-9487-D000DDB8B272}\">")
  129. meshComponentConfig = xml.etree.ElementTree.Element("Class", {'name': "AZ::Render::MeshComponentConfig", 'field': "Configuration", 'type': "{63737345-51B1-472B-9355-98F99993909B}"})
  130. modelAsset = xml.etree.ElementTree.Element("Class", {'name': "Asset", 'field': "ModelAsset", 'value': assetIdValue, 'version': "1", 'type': "{77A19D40-8731-4D3C-9041-1B43047366A4}"})
  131. excludeFromReflectionCubeMaps = xml.etree.ElementTree.Element("Class", {'name': "bool", 'field': "ExcludeFromReflectionCubeMaps", 'value': "false", 'type': "{A0CA880C-AFE4-43CB-926C-59AC48496112}"})
  132. meshComponentConfig.append(modelAsset)
  133. meshComponentConfig.append(excludeFromReflectionCubeMaps)
  134. meshComponentController.append(meshComponentConfig)
  135. editorComponentAdapter.append(editorComponentBase)
  136. editorComponentAdapter.append(meshComponentController)
  137. editorRenderComponentAdapter.append(editorComponentAdapter)
  138. return editorRenderComponentAdapter
  139. def convert(self, xmlElement, parent):
  140. """
  141. Returns a list of xml elements (siblings)
  142. """
  143. # Now clear the legacy mesh component
  144. xmlElement.clear()
  145. # And replace the content with an Atom mesh component
  146. xmlElement.set("name", "AZ::Render::EditorMeshComponent")
  147. xmlElement.set("field", "element")
  148. xmlElement.set("version", "1")
  149. xmlElement.set("type", "{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9}")
  150. xmlElement.append(self.create_adapter_with_new_model_assetId(self.newAssetId))
  151. if len(self.oldMaterialRelativePath) == 0 or self.oldMaterialRelativePath[0:self.oldMaterialRelativePath.find(".mtl")] == self.oldFbxRelativePathWithoutExtension:
  152. # There was no material override
  153. self.statsCollector.noMaterialOverrideCount += 1
  154. else:
  155. # There was a material override
  156. self.statsCollector.materialOverrideCount += 1
  157. # Now that we have an Atom MeshComponent, we need an Atom MaterialComponent as a neighbor to 'child' (the new mesh component)
  158. atomMaterialInDefaultSlot = self.materialComponentConverter.convert_legacy_mtl_relative_path_to_atom_material_assetid(self.normalizedProjectDir, self.oldMaterialRelativePath, self.oldFbxRelativePathWithoutExtension)
  159. isActor = False
  160. atomMaterialList = self.materialComponentConverter.convert_legacy_mtl_relative_path_to_atom_material_list(self.normalizedProjectDir, self.oldMaterialRelativePath, self.oldFbxRelativePathWithoutExtension, isActor)
  161. parent.append(self.materialComponentConverter.create_material_component_with_material_assignments(atomMaterialInDefaultSlot, atomMaterialList))
  162. def reset(self):
  163. self.newAssetId = ""
  164. self.oldMaterialRelativePath = ""
  165. self.oldFbxRelativePathWithoutExtension = ""