Integrating github/staging through commit ab87ed9

This commit is contained in:
alexpete
2021-04-09 11:27:37 -07:00
parent ae62a97894
commit 1044dc3da1
1582 changed files with 29374 additions and 519051 deletions
@@ -128,7 +128,6 @@ void FlyCameraInputComponent::Reflect(AZ::ReflectContext* reflection)
if (behaviorContext)
{
behaviorContext->EBus<FlyCameraInputBus>("FlyCameraInputBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Event("SetIsEnabled", &FlyCameraInputBus::Events::SetIsEnabled)
->Event("GetIsEnabled", &FlyCameraInputBus::Events::GetIsEnabled);
}
@@ -296,8 +296,6 @@ void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str,
const bool orthoMode = ctx.m_overrideViewProjMatrices;
int baseState = ctx.m_baseState;
const RHI::Viewport& viewport = m_windowContext->GetViewport();
const float viewX = viewport.m_minX;
const float viewY = viewport.m_minY;
@@ -322,7 +320,6 @@ void AZ::FFont::DrawStringUInternal(float x, float y, float z, const char* str,
}
size_t startingVertexCount = m_vertexCount;
size_t startingIndexCount = m_indexCount;
// Local function that is passed into CreateQuadsForText as the AddQuad function
AddFunction AddQuad = [this, startingVertexCount]
@@ -309,7 +309,7 @@ Vec2 AZ::FontRenderer::GetKerning(uint32_t leftGlyph, uint32_t rightGlyph)
const FT_UInt leftGlyphIndex = FT_Get_Char_Index(m_face, leftGlyph);
const FT_UInt rightGlyphIndex = FT_Get_Char_Index(m_face, rightGlyph);
FT_Error ftError = FT_Get_Kerning(m_face, leftGlyphIndex, rightGlyphIndex, FT_KERNING_DEFAULT, &kerningOffsets);
[[maybe_unused]] FT_Error ftError = FT_Get_Kerning(m_face, leftGlyphIndex, rightGlyphIndex, FT_KERNING_DEFAULT, &kerningOffsets);
#if !defined(_RELEASE)
if (0 != ftError)
@@ -261,8 +261,9 @@ int AZ::FontTexture::PreCacheString(const char* string, int* updated, float size
int updateCount = 0;
uint32_t character;
for (Unicode::CIterator<const char*, false> it(string); character = *it; ++it)
for (Unicode::CIterator<const char*, false> it(string); *it; ++it)
{
character = *it;
TextureSlot* slot = GetCharSlot(character, clampedGlyphSize);
if (!slot)
@@ -88,19 +88,46 @@ namespace AtomImGuiTools
{
m_imguiGpuProfiler.Draw(m_showGpuProfiler, AZ::RPI::PassSystemInterface::Get()->GetRootPass().get());
}
if (m_showCpuProfiler)
{
const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics();
if (stats)
{
m_imguiCpuProfiler.Draw(m_showCpuProfiler, *stats);
}
}
if (m_showTransientAttachmentProfiler)
{
auto* transientStats = AZ::RHI::RHISystemInterface::Get()->GetTransientAttachmentStatistics();
if (transientStats)
{
m_showTransientAttachmentProfiler = m_imguiTransientAttachmentProfiler.Draw(*transientStats);
}
}
if (m_showShaderMetrics)
{
m_imguiShaderMetrics.Draw(m_showShaderMetrics, AZ::RPI::ShaderMetricsSystemInterface::Get()->GetMetrics());
}
}
void AtomImGuiToolsSystemComponent::OnImGuiMainMenuUpdate()
{
if (ImGui::BeginMenu("Atom Tools"))
{
if (ImGui::MenuItem("Pass Viewer", "", &m_showPassTree))
ImGui::MenuItem("Pass Viewer", "", &m_showPassTree);
ImGui::MenuItem("Gpu Profiler", "", &m_showGpuProfiler);
if (ImGui::MenuItem("Cpu Profiler", "", &m_showCpuProfiler))
{
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
AZ::RHI::FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics, m_showCpuProfiler);
AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(m_showCpuProfiler);
}
if (ImGui::MenuItem("Gpu Profiler", "", &m_showGpuProfiler))
if (ImGui::MenuItem("Transient Attachment Profiler", "", &m_showTransientAttachmentProfiler))
{
AZ::RHI::RHISystemInterface::Get()->ModifyFrameSchedulerStatisticsFlags(
AZ::RHI::FrameSchedulerStatisticsFlags::GatherTransientAttachmentStatistics, m_showTransientAttachmentProfiler);
}
ImGui::MenuItem("Shader Metrics", "", &m_showShaderMetrics);
ImGui::EndMenu();
}
}
@@ -19,8 +19,11 @@
#if defined(IMGUI_ENABLED)
#include <ImGuiBus.h>
#include <imgui/imgui.h>
#include <Atom/Utils/ImGuiCpuProfiler.h>
#include <Atom/Utils/ImGuiGpuProfiler.h>
#include <Atom/Utils/ImGuiPassTree.h>
#include <Atom/Utils/ImGuiShaderMetrics.h>
#include <Atom/Utils/ImGuiTransientAttachmentProfiler.h>
#endif
namespace AtomImGuiTools
@@ -63,6 +66,15 @@ namespace AtomImGuiTools
AZ::Render::ImGuiGpuProfiler m_imguiGpuProfiler;
bool m_showGpuProfiler = false;
AZ::Render::ImGuiCpuProfiler m_imguiCpuProfiler;
bool m_showCpuProfiler = false;
AZ::Render::ImGuiTransientAttachmentProfiler m_imguiTransientAttachmentProfiler;
bool m_showTransientAttachmentProfiler = false;
AZ::Render::ImGuiShaderMetrics m_imguiShaderMetrics;
bool m_showShaderMetrics = false;
#endif
};
@@ -0,0 +1,84 @@
"""
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 Actor Component to Actor+Material Component Conversion Script
"""
from LegacyConversionHelpers import *
from LegacyMaterialComponentConverter import *
class Actor_Component_Converter(Component_Converter):
"""
Converts point lights
"""
def __init__(self, assetCatalogHelper, statsCollector, normalizedProjectDir):
Component_Converter.__init__(self, assetCatalogHelper, statsCollector)
# These are constant for every component in the file
self.materialComponentConverter = Material_Component_Converter(assetCatalogHelper)
self.normalizedProjectDir = normalizedProjectDir
# These need to be reset between each component
self.oldMaterialRelativePath = ""
self.oldFbxRelativePathWithoutExtension = ""
def is_this_the_component_im_looking_for(self, xmlElement, parent):
if "name" in xmlElement.keys() and xmlElement.get("name") == "EditorActorComponent":
for sibling in list(parent):
if "name" in sibling.keys() and sibling.get("name") == "EditorMaterialComponent":
# There is already a material component on this actor, so we don't need to convert it again
return False
# Found an actor that doesn't have a material component
return True
# Not an actor
return False
def gather_info_for_conversion(self, xmlElement, parent):
# We don't need to modify the actor component, but we do need to add a material component
for possibleActorAssetComponent in xmlElement.getchildren():
if "field" in possibleActorAssetComponent.keys() and possibleActorAssetComponent.get("field") == "ActorAsset" and "value" in possibleActorAssetComponent.keys():
assetId = possibleActorAssetComponent.get("value")
actorPathStartIndex = assetId.find("hint={")
actorPathStartIndex += len("hint={")
actorPathEndIndex = assetId.find(".actor")
self.oldFbxRelativePathWithoutExtension = assetId[actorPathStartIndex: actorPathEndIndex]
elif "field" in possibleActorAssetComponent.keys() and possibleActorAssetComponent.get("field") == "MaterialPerActor":
# TODO - support the actor component's "MaterialPerLOD"
for simpleAssetReferenceChild in possibleActorAssetComponent:
if "name" in simpleAssetReferenceChild.keys() and simpleAssetReferenceChild.get("name") == "SimpleAssetReferenceBase":
for simpleAssetReferenceBaseChild in simpleAssetReferenceChild:
if "field" in simpleAssetReferenceBaseChild.keys() and simpleAssetReferenceBaseChild.get("field") == "AssetPath" and "value" in simpleAssetReferenceBaseChild.keys():
# We've found the material override
self.oldMaterialRelativePath = simpleAssetReferenceBaseChild.get("value")
def convert(self, xmlElement, parent):
"""
Adds a sibling material component
"""
if len(self.oldMaterialRelativePath) == 0 or self.oldMaterialRelativePath[0:self.oldMaterialRelativePath.find(".mtl")] == self.oldFbxRelativePathWithoutExtension:
# There was no material override
self.statsCollector.noMaterialOverrideCount += 1
else:
# There was a material override
print("Material Override: fbx {0} override {1}".format(self.oldFbxRelativePathWithoutExtension, self.oldMaterialRelativePath))
self.statsCollector.materialOverrideCount += 1
# We don't modify the xmlElement here because we do not want to replace the old ActorComponet, we just want to add a material component
atomMaterialInDefaultSlot = self.materialComponentConverter.convert_legacy_mtl_relative_path_to_atom_material_assetid(self.normalizedProjectDir, self.oldMaterialRelativePath, self.oldFbxRelativePathWithoutExtension)
isActor = True
atomMaterialList = self.materialComponentConverter.convert_legacy_mtl_relative_path_to_atom_material_list(self.normalizedProjectDir, self.oldMaterialRelativePath, self.oldFbxRelativePathWithoutExtension, isActor)
parent.append(self.materialComponentConverter.create_material_component_with_material_assignments(atomMaterialInDefaultSlot, atomMaterialList))
# TODO - protect against running this more than once on an actor? Kind of handled by the 'already converted' log file. Not a problem with meshes because the legacy mesh component gets stripped, but it is an issue with actors since they don't get removed
def reset(self):
self.oldMaterialRelativePath = ""
self.oldFbxRelativePathWithoutExtension = ""
@@ -0,0 +1,365 @@
"""
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 Renderer to Atom Component Conversion Script
What does this script do?
================================================
This script walks through all the .slice, .layer, .ly, and .cry files in a
project and attempts to convert the following components into something
reasonably similar that renders in Atom:
Mesh
Actor
Do materials get carried over?
================================================
For the mesh component, this script will attempt to create an atom mesh component
that uses the same material, pre-supposing that you have already run the
LegacyMaterialConverter.py script sto generate Atom .material files out of legacy .mtl files
For meshes that only have one sub-mesh, this is straightforward as the mesh will only
have one material to apply, and this script will look for a material with the same
name but a .material extension.
For mult-materials, this is a little tricky since Atom does not follow the same
ordered sub-material convention used by the legacy renderer. However, legacy .mtl
files that were generated when adding a .fbx to a project use a naming convention
that can be used by this script to match with the default materials that come from Atom.
So as long as you were using the initial .mtl generated by Lumberyard and have not
re-named the submaterials, it should find a match. This applies to both the
ActorComponent and the MeshComponent
How do I run this script from a command line?
================================================
1) Check out any .slice, .layer, .ly, and .cry files you want to convert from source control
- This script will remove the legacy components entirely, so make sure you have your files
backed up before you run this script in case you want to run it again
<<<<<<< HEAD
2) From the dev folder, run LegacyComponentConverter.py project=<ProjectName> --include_gems
=======
2) From the Lumberyard root folder, run LegacyComponentConverter.py project=<ProjectName> --include_gems
>>>>>>> main
- --include_gems is optional
- if you include Gems, it will run all all Gems, not just the ones enabled by your project
What are the artifacts of this script?
================================================
The .slice, .layer, .ly, and .cry files will be converted in-place. No new files will be created
Is this script destructive?
================================================
Yes! This is a one-way conversion that will clear the old data once converted. You should back up
your files before running this conversion in case you want to modify the script and re-run it on
your original level.
"""
CONVERTED_LOG_NAME = "ComponentConversion_ConvertedLegacyFiles.log"
UNCONVERTED_LOG_NAME = "ComponentConversion_UnsupportedLegacyFiles.log"
STATS_LOG_NAME = "ComponentConversion_LegacyComponentStats.log"
BUILD_PATH = None
GEMS_PATH = None
# Normal imports
import sys
import xml.etree.ElementTree
import time
from zipfile import ZipFile
import tempfile
import subprocess
# Local python files
from LegacyConversionHelpers import *
from LegacyMeshComponentConverter import *
from LegacyMaterialComponentConverter import *
from LegacyActorComponentConverter import *
from LegacyPointLightComponentConverter import *
BUILD_PATH = "./"
GEMS_PATH = os.path.join(BUILD_PATH, "Gems")
class Component_File(object):
"""
Class to perform any read, write or conversion operations on material (*.mtl) files.
"""
def __init__(self, filename, projectDir, assetCatalogHelper, statsCollector):
self.filename = filename
self.normalizedProjectDir = os.path.normpath(projectDir)
self.needsConversion = False
self.hadException = False
self.assetCatalogHelper = assetCatalogHelper
self.materialComponentConverter = Material_Component_Converter(assetCatalogHelper)#TODO - I'm pretty sure this is dead code
self.xml = None
self.statsCollector = statsCollector
self.parse_xml()
def is_valid_xml(self):
"""
Performs a simple check to determine if the XML of the mtl is valid.
This is to prevent an assert on a material conversion operation and
preventing the script from finishing.
It's possible for a material's xml to be malformed, which is why this check is needed.
"""
return True
try:
if isinstance(self.xml.getroot(), xml.etree.ElementTree.Element):
return True
else:
return False
except:
return False
def parse_xml(self):
"""
Open and parse the file's xml, storing it for access later.
For .ly and .cry files, it will get the xml out of the .zip
"""
if self.filename.endswith(".cry") or self.filename.endswith(".ly"):
zipRead = ZipFile(self.filename, 'r')
contents = zipRead.read("levelentities.editor_xml")
zipRead.close()
# write the contents to a temporary file so we can parse it with ElementTree
tmpFile = tempfile.NamedTemporaryFile(delete=False)
tmpFile.write(contents)
tmpFile.close()
self.xml = xml.etree.ElementTree.parse(tmpFile.name)
self.gather_elements()
os.unlink(tmpFile.name)
os.path.exists(tmpFile.name)
elif os.path.exists(self.filename):
#try:
# TODO try-except is supposed to make it so one bad xml doesn't crash the lot
# need to clean up stuff so the logging/conversion later doesn't crash
# for now, better to crash here so we see where the exception is being thrown
self.xml = xml.etree.ElementTree.parse(self.filename)
self.gather_elements()
#except OSError as err:
# print("OS error: {0}".format(err))
# self.xml = None
# self.needsConversion = False
# self.hadException = True
#except ValueError:
# print("Could not convert data to an integer.")
# self.xml = None
# self.needsConversion = False
# self.hadException = True
#except:
# print("Unexpected error:", sys.exc_info()[0])
# self.xml = None
# self.needsConversion = False
# self.hadException = True
def gather_elements(self):
"""
Once the xml has been parsed, mine through it to find all of the
neccessary elements that need to be modified.
"""
print("starting to parse {0}".format(self.filename))
componentConverters = []
componentConverters.append(Mesh_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
componentConverters.append(Actor_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
componentConverters.append(Point_Light_Component_Converter(self.assetCatalogHelper, self.statsCollector, self.normalizedProjectDir))
if self.is_valid_xml():
root = self.xml.getroot()
if root.tag == "ObjectStream" or True:
# First, get a dictionary of child->parent mapping for later use inserting sibling elements
self.parent_map = {c:p for p in root.iter('Class') for c in p}
# Now go through and look for mesh components
for child in root.iter('Class'):
# If we run into one of the components we just added, skip it. It doesn't need to be converted,
# and it doesn't exist in the pre-built parent_map so it would throw an exception if we tried to access it
if child in self.parent_map:
parent = self.parent_map[child]
for componentConverter in componentConverters:
componentConverter.reset()
if componentConverter.is_this_the_component_im_looking_for(child, parent):
self.needsConversion = True
componentConverter.gather_info_for_conversion(child, parent)
# 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
# Seems to be okay since we only change or add elements, never remove entirely
componentConverter.convert(child, parent)
self.xml._setroot(root)
print("finished parsing {0}".format(self.filename))
def can_be_converted(self):
"""
Determines if this material file can be converted by checking if
it is using the Illum Shader
"""
return self.needsConversion
def can_write(self):
"""
Checks to make sure the mtl file is writable.
This is to prevent the script from asserting during a
save attempt and preventing the script from finishing.
"""
fullFilePath = self.get_atom_file_path()
if os.path.exists(fullFilePath):
if os.access(fullFilePath, os.W_OK):
return True
else:
with open(fullFilePath,"a+") as f:
f.close()
return True
return False
def get_atom_file_path(self):
# This is just a way to optionally create a new file for comparing with the original
# TODO: control this via command line
atomFileName = self.filename
return atomFileName#.replace('.slice', '_atom.slice')
def convert(self):
"""
Creates the new level/slice file
"""
# TODO - will not work if .slice is part of the path instead of the extension
if self.needsConversion:
if self.filename.endswith(".cry") or self.filename.endswith(".ly"):
# We can't just update the .cry file, we need to rebuild all the contents
#Make a temporary file
tmpFile, tmpFileName = tempfile.mkstemp(dir=os.path.dirname(self.filename))
os.close(tmpFile)
#Create a temporary copy of the .cry file
with ZipFile(self.filename, 'r') as zin:
with ZipFile(tmpFileName, 'w') as zout:
#Loop through the file list and write out every file but level.editor_xml with no modifications
#when we hit the level data we want to edit, write it out with the new contents
for item in zin.infolist():
if item.filename == "levelentities.editor_xml":
xmlString = xml.etree.ElementTree.tostring(self.xml.getroot())
zout.writestr(item, xmlString)
else:
zout.writestr(item, zin.read(item.filename))
#Remove old cry file and rename the temp file
os.remove(self.filename)
os.rename(tmpFileName, self.filename)
else:
self.xml.write(self.get_atom_file_path())
return False
def getUpdatedStatsCollector(self):
"""
Returns the stats collector that was passed in intially, with any modifications that were made
"""
return self.statsCollector
###############################################################################
def main():
'''sys.__name__ wrapper function'''
msgStr = "This tool will scan all of your project's level/layer/slice files\n\
convert any compatible legacy components into the equivalent Atom components\n\
This script will overwrite the original files, and will remove the legacy components\n\
upon conversion, decimating the previous contents of those components.\n"
commandLineOptions = Common_Command_Line_Options(sys.argv[0], sys.argv[1])
if commandLineOptions.isHelp:
print (commandLineOptions.helpString)
return
start_time = time.time()
total_converted = 0
extensionList = [".slice", ".layer", ".ly", ".cry"]
fileList = get_file_list(commandLineOptions.projectName, commandLineOptions.includeGems, extensionList, BUILD_PATH, GEMS_PATH)
assetCatalogDictionaries = get_asset_catalog_dictionaries(BUILD_PATH, commandLineOptions.projectName)
# Create a log file to store converted component file filenames
# and to check to see if the component file has already been converted.
convertedLogFile = Log_File(filename="{0}\\{1}".format(BUILD_PATH, CONVERTED_LOG_NAME))
# Create a log file to store component file filenames that need conversion
# but cannot becuase they are read only.
unconvertedLogFile = Log_File(filename="{0}\\{1}".format(BUILD_PATH, UNCONVERTED_LOG_NAME), include_previous = False)
statsLogFile = Log_File(filename="{0}\\{1}".format(BUILD_PATH, STATS_LOG_NAME), include_previous = False)
statsCollector = Stats_Collector()
# Go through each component file to perform the conversion on it
print("==============================")
componentFileIndex = -1
for componentFileInfo in fileList:
componentFileIndex += 1
componentFileName = componentFileInfo.filename
copmonentFileProjectDir = componentFileInfo.normalizedProjectDir
print(componentFileName)
#if convertedLogFile.has_line(componentFileName.lstrip(BUILD_PATH)): # Use this to only convert files that haven't already been converted
# print("--> Previously converted, not doing")
# continue
if commandLineOptions.endsWithStr == "" or componentFileName.lower().endswith(commandLineOptions.endsWithStr.lower()):
componentFile = Component_File(componentFileName, copmonentFileProjectDir, assetCatalogDictionaries, statsCollector)
if componentFile.can_be_converted():
if commandLineOptions.useP4:
subprocess.check_call(['p4', 'edit', componentFileName])
if componentFile.can_write():
componentFile.convert()
convertedLogFile.add_line_no_duplicates(componentFile.get_atom_file_path())
print("--> Converted")
total_converted += 1
else:
unconvertedLogFile.add_line_no_duplicates("{0} - cannot access file (read-only)".format(componentFile.get_atom_file_path()))
print("--> Could not write to destination component file (read-only). Not converted.")
else:
print("--> did not need conversion.")
statsCollector = componentFile.getUpdatedStatsCollector()
print("\n")
# Fill out the stats log
statsLogFile.add_line("Mesh/Actor Components without a material overrride: {0}".format(statsCollector.noMaterialOverrideCount))
statsLogFile.add_line("Mesh/Actor Components with a material overrride: {0}".format(statsCollector.materialOverrideCount))
statsLogFile.add_line("Total Mesh/Actor Components: {0}".format(statsCollector.noMaterialOverrideCount + statsCollector.materialOverrideCount))
# Finally, save the log files to disk
convertedLogFile.save()
unconvertedLogFile.save()
statsLogFile.save()
total_time = time.time() - start_time
log_str = "You can view a list of converted component files in this log file:\n{0}\\{1}".format(BUILD_PATH, CONVERTED_LOG_NAME)
unconverted_log_str = "You can view a list of component files that were not converted in this log file:\n{0}\\{1}".format(BUILD_PATH, UNCONVERTED_LOG_NAME)
stats_log_str = "You can view a list of component files stats, such as feature and shader usage, in this log file:\n{0}\\{1}".format(BUILD_PATH, STATS_LOG_NAME)
print("==============================\n")
print("Conversion completed in {0} seconds.\n".format(total_time))
print("Converted {0} component file(s)".format(total_converted))
# Inform the user about the log files
print("{0}".format(log_str))
print("{0}".format(unconverted_log_str))
print("{0}".format(stats_log_str))
if __name__ == '__main__':
# GLOBAL NOTE:
# - All python scripts should execute through a main() function.
main()
@@ -0,0 +1,303 @@
"""
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.
Helper classes for legacy conversion scripts
"""
import os
import xml.etree.ElementTree
class Stats_Collector(object):
"""
Stuff any extra info you want to keep track of during conversion into this object
"""
def __init__(self):
self.materialOverrideCount = 0
self.noMaterialOverrideCount = 0
class File_Info(object):
"""
Keep track of both the filename and the directory of the project
(not the directory the file is in, but the LY project itself)
"""
def __init__(self, filename, projectDir):
self.filename = filename
self.normalizedProjectDir = os.path.normpath(projectDir)
class Log_File(object):
"""
Simple class to create, open & save a log file.
"""
def __init__(self, filename, include_previous = True):
self.filename = filename
self.logFile = None
self.include_previous = include_previous
self.lines = []
self.create_log_file()
def create_log_file(self):
"""
Will either generate a new log file or open the existing log file and read its contents
"""
if os.path.exists(self.filename):
self.logFile = open(self.filename, 'r+')
if self.include_previous:
for line in self.logFile.readlines():
self.add_line(line)
else:
self.logFile = open(self.filename, 'w')
def save(self):
"""
Saves the log file back to disk.
To ensure that filenames are not added twice, this method first saves an empty
log to disc. Then saves it again with the list of filenames (self.lines)
stored in this class.
"""
# First clear the log file's contents so they can be written back in
self.logFile.close()
open(self.filename, 'w').close()
self.logFile = open(self.filename, 'w')
for line in self.lines:
self.logFile.write("{0}\n".format(line))
self.logFile.close()
def get_lines(self):
return self.lines
def add_line(self, line):
self.lines.append(line)
def add_line_no_duplicates(self, line):
"""
Adds a line to the log, and prevents adding duplicates
Useful for keeping track of files that have already been converted
The line will be all lowercase for simplicity
"""
# rstrip with no arguments will remove trailing whitespace
lowercase = line.lower().rstrip()
if not lowercase in self.lines:
self.lines.append(lowercase)
def has_line(self, line):
"""
Checks to see if a specific line already exists in the log
"""
# rstrip with no arguments will remove trailing whitespace
lowercase = line.lower().rstrip()
if lowercase in self.lines:
return True
return False
class Common_Command_Line_Options(object):
"""
Some common options/parsing
"""
def __init__(self, argv0, argv1):
arguments = argv1.split('-')
self.projectName = ""
self.includeGems = False
self.useP4 = False
self.endsWithStr = ""
self.isHelp = False
self.helpString = "usage: {0} -project=<project name> -include_gems -ends_with=<filter> -use_p4\n\
E.g.:\n\
{1} -project=StarterGame -include_gems\n\
-project is required.\n\
-include_gems is optional, and by default gems will not be included.\n\
-ends_with is optional. It could be used to filter for a specific file (--ends_with=default.mtl)\n\
-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)
for argument in arguments:
argument = argument.rstrip(" ")
if argument == "h" or argument == "help" or argv1 == "?":
self.isHelp = True
elif argument.startswith("project"):
projectArgs = argument.split("=")
if len(projectArgs) > 1:
self.projectName = projectArgs[1]
elif argument == "include_gems":
self.includeGems = True
elif argument == "use_p4":
self.useP4 = True
elif argument.startswith("ends_with"):
endsWithArgs = argument.split("=")
if len(endsWithArgs) > 1:
self.endsWithStr = endsWithArgs[1]
def get_file_list(projectName, includeGems, extensionList, buildPath, gemsPath):
"""
The main difference between this and any other way to walk a directory
looking for files is that it keeps track of the lumberyard project
or gems folder the file is in, which can later be used to figure out
the relative path that is used by the engine for the file
"""
print("Gathering Files...")
# Lower the project name for easier matching
projectName = projectName.lower()
# First, gather a list of all project folders in the dev root.
# This will help to reduce the amount of files that the script
# has to walk through when searching for files.
projectFolders = []
for root, dirs, files in os.walk(buildPath):
if root != buildPath:
break
for d in dirs:
projectFile = "{0}\\{1}\\project.json".format(root, d)
if os.path.exists(projectFile) and d.lower() == projectName:
projectFolders.append(os.path.join(root, d))
# Add all gems to the list of project folders.
if includeGems:
for root, dirs, files in os.walk(gemsPath):
if root != gemsPath:
break
while len(dirs) >= 1:
d = dirs[0]
gemsFile = "{0}\\{1}\\gem.json".format(root, d)
if os.path.exists(gemsFile):
projectFolders.append(os.path.join(os.path.join(root, d), "Assets"))
for subroot, subdirs, subfiles in os.walk(os.path.join(gemsPath, d)):
if subroot != os.path.join(gemsPath, d):
break
for subd in subdirs:
dirs.append(os.path.join(d, subd))
dirs.remove(d)
fileInfoList = []
for projPath in projectFolders:
for root, dirs, files in os.walk(projPath):
if 'Cache' in root.split(os.sep):
continue
for f in files:
for extension in extensionList:
if f.endswith(extension):
fileInfoList.append(File_Info("{0}\\{1}".format(root, f), projPath))
return fileInfoList
class Asset_Catalog_Dictionaries(object):
"""
Some dictionaries from the asset catalog
"""
def __init__(self, relativePathToAssetIdDict, assetIdToRelativePathDict, assetUuidToAssetIdsDict):
self.relativePathToAssetIdDict = relativePathToAssetIdDict
self.assetIdToRelativePathDict = assetIdToRelativePathDict
self.assetUuidToAssetIdsDict = assetUuidToAssetIdsDict
def get_asset_id_from_relative_path(self, relativePath):
# parse the asset catalog and find the assetId
if relativePath in self.relativePathToAssetIdDict:
return self.relativePathToAssetIdDict[relativePath]
return ""
def get_asset_catalog_dictionaries(buildPath, projectName):
"""
This function pre-supposes that you have modified AssetCatalog::SaveRegistry_Impl
in Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp
to use AZ::ObjectStream::ST_XML instead of AZ::ObjectStream::ST_BINARY
and subsequently deleted Cache/<project name>/pc/<project name>/assetcatalog.xml
and let it re-build so that it is a parseable xml file
"""
print("Parsing Asset Catalog...")
relativePathToAssetIdDict = {}
relativePathToAssetIdDict[""] = "{00000000-0000-0000-0000-000000000000}:0"
assetIdToRelativePathDict = {}
assetUuidToAssetIdsDict = {}
assetCatalogPath = os.path.join(projectName, "Cache", "pc", "assetcatalog.xml")
assetCatalogXml = xml.etree.ElementTree.parse(assetCatalogPath)
for possibleAssetInfo in assetCatalogXml.getroot().iter('Class'):
if "name" in possibleAssetInfo.keys() and possibleAssetInfo.get("name") == "AssetInfo":
# We found some AssetInfo
relativePath = ""
assetId = ""
for child in possibleAssetInfo:
if "field" in child.keys() and child.get("field") == "relativePath" and "value" in child.keys():
relativePath = child.get("value")
if "name" in child.keys() and child.get("name") == "AssetId":
guid = ""
subId = ""
for assetIdPart in child:
if "field" in assetIdPart.keys() and assetIdPart.get("field") == "guid" and "value" in assetIdPart.keys():
guid = assetIdPart.get("value")
if "field" in assetIdPart.keys() and assetIdPart.get("field") == "subId" and "value" in assetIdPart.keys():
subId = assetIdPart.get("value")
assetId = "".join((guid, ":", subId))
relativePathToAssetIdDict[relativePath] = assetId
assetIdToRelativePathDict[assetId] = relativePath
if not guid in assetUuidToAssetIdsDict:
assetUuidToAssetIdsDict[guid] = []
assetUuidToAssetIdsDict[guid].append(assetId)
return Asset_Catalog_Dictionaries(relativePathToAssetIdDict, assetIdToRelativePathDict, assetUuidToAssetIdsDict)
def create_xml_element_from_string(xmlString):
# copy paste a line from a .slice or other serialization file from lumberyard, add a \ before the " marks, and this function will turn it into an xml element
# e.g. <Class name=\"EditorMaterialComponent\" field=\"element\" version=\"3\" type=\"{02B60E9D-470B-447D-A6EE-7D635B154183}\">
# does not protect against malformed strings.
# relies heavily on things like starting with <, no space between < and the tag, etc.
# but works in a pinch
rawXmlList = xmlString.split()
# strip the < from the first element to get the tag
element = xml.etree.ElementTree.Element(rawXmlList[0].lstrip("<"))
for itemIndex in range(len(rawXmlList)):
item = rawXmlList[itemIndex]
if itemIndex == 0:
# ignore the tag because we've already handled it
continue
elif itemIndex == (len(rawXmlList) - 1):
# strip the end tag if it exists
item = item.rstrip('>')
item = item.rstrip('/')
#parse the item
itemList = item.split('=')
attribute = itemList[0]
value = itemList[1].lstrip('\"').rstrip('\"')
element.set(attribute, value)
return element
def get_uuid_from_assetId(assetId):
separatorIndex = assetId.find(":")
return assetId[:separatorIndex]
def get_subid_from_assetId(assetId):
separatorIndex = assetId.find(":") + 1
return assetId[separatorIndex:]
class Component_Converter(object):
"""
Converter base class
"""
def __init__(self, assetCatalogHelper, statsCollector):
self.assetCatalogHelper = assetCatalogHelper
self.statsCollector = statsCollector
def is_this_the_component_im_looking_for(self, xmlElement, parent):
pass
def gather_info_for_conversion(self, xmlElement, parent):
pass
def convert(self, xmlElement, parent):
pass
def reset(self):
pass
@@ -0,0 +1,267 @@
"""
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):
# <Class name="AZStd::pair" field="element" type="{F652A87A-0FDF-527C-B0ED-340C074A4874}">
# <Class name="AZ::Render::MaterialAssignmentId" field="value1" version="1" type="{EB603581-4654-4C17-B6DE-AE61E79EDA97}">
# <Class name="AZ::u64" field="lodIndex" value="18446744073709551615" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# <Class name="AssetId" field="materialAssetId" version="1" type="{652ED536-3402-439B-AEBE-4A5DBC554085}">
# <Class name="AZ::Uuid" field="guid" value="{2A4E7DCF-F5D5-55B3-8D41-A4F89398D53C}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
# <Class name="unsigned int" field="subId" value="31953" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
# </Class>
# </Class>
# <Class name="AZ::Render::MaterialAssignment" field="value2" version="1" type="{C66E5214-A24B-4722-B7F0-5991E6F8F163}">
# <Class name="Asset" field="MaterialAsset" value="id={0BFD18DD-3A64-5240-A272-600301CE821C}:0,type={522C7BE0-501D-463E-92C6-15184A2B7AD8},hint={valena/valenaactor_jumpsuitmat.azmaterial}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
# <Class name="AZStd::unordered_map" field="PropertyOverrides" type="{6E6962E1-04C9-56F9-89C4-361031CC1384}"/>
# </Class>
# </Class>
pair = create_xml_element_from_string("<Class name=\"AZStd::pair\" field=\"element\" type=\"{F652A87A-0FDF-527C-B0ED-340C074A4874}\">")
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"
# <Class name="AZ::Render::MaterialAssignmentId" field="id" version="1" type="{EB603581-4654-4C17-B6DE-AE61E79EDA97}">
# <Class name="AZ::u64" field="lodIndex" value="18446744073709551615" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# <Class name="AssetId" field="materialAssetId" version="1" type="{652ED536-3402-439B-AEBE-4A5DBC554085}">
# <Class name="AZ::Uuid" field="guid" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
# <Class name="unsigned int" field="subId" value="0" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
# </Class>
# </Class>
materialAssignmentId = create_xml_element_from_string("".join(("<Class name=\"AZ::Render::MaterialAssignmentId\" field=\"", field, "\" version=\"1\" type=\"{EB603581-4654-4C17-B6DE-AE61E79EDA97}\">")))
# 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("<Class name=\"AZ::u64\" field=\"lodIndex\" value=\"18446744073709551615\" type=\"{D6597933-47CD-4FC8-B911-63F3E2B0993A}\"/>")
materialAssignmentId_AssetId = create_xml_element_from_string("<Class name=\"AssetId\" field=\"materialAssetId\" version=\"1\" type=\"{652ED536-3402-439B-AEBE-4A5DBC554085}\">")
uuid = get_uuid_from_assetId(slotAssetId)
materialAssignmentId_AssetId_Uuid = create_xml_element_from_string("".join(("<Class name=\"AZ::Uuid\" field=\"guid\" value=\"", uuid, "\" type=\"{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}\"/>")))
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"
# <Class name="EditorMaterialComponentSlot" field="defaultMaterialSlot" version="2" type="{344066EB-7C3D-4E92-B53D-3C9EBD546488}">
# <Class name="AZ::Render::MaterialAssignmentId" ...
# <Class name="Asset" field="materialAsset" value="id={00000000-0000-0000-0000-000000000000}:0,type={522C7BE0-501D-463E-92C6-15184A2B7AD8},hint={}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
# <Class name="AZStd::unordered_map" field="propertyOverrides" type="{6E6962E1-04C9-56F9-89C4-361031CC1384}"/>
# </Class>
materialComponentSlot = create_xml_element_from_string("".join(("<Class name=\"EditorMaterialComponentSlot\" field=\"", field, "\" version=\"4\" type=\"{344066EB-7C3D-4E92-B53D-3C9EBD546488}\">")))
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):
# <Class name="AZ::Render::MaterialAssignment" field="value2" version="1" type="{C66E5214-A24B-4722-B7F0-5991E6F8F163}">
# <Class name="Asset" field="MaterialAsset" value="id={B175B5BF-E97C-52BD-9DC8-60A9CE05CCC8}:0,type={522C7BE0-501D-463E-92C6-15184A2B7AD8},hint={valena/valenaactor_glovesbootsmat.azmaterial}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
# <Class name="AZStd::unordered_map" field="PropertyOverrides" type="{6E6962E1-04C9-56F9-89C4-361031CC1384}"/>
# </Class>
materialAssignment = create_xml_element_from_string("<Class name=\"AZ::Render::MaterialAssignment\" field=\"value2\" version=\"1\" type=\"{C66E5214-A24B-4722-B7F0-5991E6F8F163}\">")
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("<Class name=\"AZStd::unordered_map\" field=\"propertyOverrides\" type=\"{6E6962E1-04C9-56F9-89C4-361031CC1384}\"/>")
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
#<Class name="EditorMaterialComponent" field="element" version="3" type="{02B60E9D-470B-447D-A6EE-7D635B154183}">
# <Class name="EditorRenderComponentAdapter&lt;MaterialComponentController MaterialComponent MaterialComponentConfig &gt;" field="BaseClass1" type="{DF046B40-536D-5D59-96EF-7A40DA6191B2}">
# <Class name="EditorComponentAdapter&lt;MaterialComponentController MaterialComponent MaterialComponentConfig &gt;" field="BaseClass1" version="1" type="{6C4D4557-3728-56F8-A0FF-A309C3AAB853}">
# <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
# <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
# <Class name="AZ::u64" field="Id" value="6456931760107146363" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# </Class>
# </Class>
# <Class name="MaterialComponentController" field="Controller" version="1" type="{34AD7ED0-9866-44CD-93B6-E86840214B91}">
# <Class name="MaterialComponentConfig" field="Configuration" version="3" type="{3366C279-32AE-48F6-839B-7700AE117A54}">
# <Class name="ComponentConfig" field="BaseClass1" version="1" type="{0A7929DF-2932-40EA-B2B3-79BC1C3490D0}"/>
# <Class name="AZStd::unordered_map" field="materials" type="{50F6716F-698B-5A6C-AACD-940597FDEC24}">
# ... (map entries)
# </Class>
# </Class>
# </Class>
editorMaterialComponent = create_xml_element_from_string("<Class name=\"EditorMaterialComponent\" field=\"element\" version=\"5\" type=\"{02B60E9D-470B-447D-A6EE-7D635B154183}\">")
# 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<MaterialComponentController MaterialComponent MaterialComponentConfig >", 'field' : "BaseClass1", 'type' : "{DF046B40-536D-5D59-96EF-7A40DA6191B2}"})
editorComponentAdapter = xml.etree.ElementTree.Element("Class", {'name' : "EditorComponentAdapter<MaterialComponentController MaterialComponent MaterialComponentConfig >", 'field' : "BaseClass1", 'version' : "1", 'type' : "{6C4D4557-3728-56F8-A0FF-A309C3AAB853}"})
editorComponentBase = create_xml_element_from_string("<Class name=\"EditorComponentBase\" field=\"BaseClass1\" version=\"1\" type=\"{D5346BD4-7F20-444E-B370-327ACD03D4A0}\">")
component = create_xml_element_from_string("<Class name=\"AZ::Component\" field=\"BaseClass1\" type=\"{EDFCB2CF-F75D-43BE-B26B-F35821B29247}\">")
u64 = create_xml_element_from_string("<Class name=\"AZ::u64\" field=\"Id\" value=\"6456931760107146363\" type=\"{D6597933-47CD-4FC8-B911-63F3E2B0993A}\"/>")
component.append(u64)
editorComponentBase.append(component)
editorComponentAdapter.append(editorComponentBase)
materialComponentController = create_xml_element_from_string("<Class name=\"MaterialComponentController\" field=\"Controller\" version=\"1\" type=\"{34AD7ED0-9866-44CD-93B6-E86840214B91}\">")
materialComponentConfig = create_xml_element_from_string("<Class name=\"MaterialComponentConfig\" field=\"Configuration\" version=\"3\" type=\"{3366C279-32AE-48F6-839B-7700AE117A54}\">")
componentConfig = create_xml_element_from_string("<Class name=\"ComponentConfig\" field=\"BaseClass1\" version=\"1\" type=\"{0A7929DF-2932-40EA-B2B3-79BC1C3490D0}\"/>")
materialMap = create_xml_element_from_string("<Class name=\"AZStd::unordered_map\" field=\"materials\" type=\"{50F6716F-698B-5A6C-AACD-940597FDEC24}\">")
# {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)
# <Class name="AZStd::vector" field="materialSlots" type="{7FDDDE36-46C8-5DBC-8566-E792AA358BD9}">
# ... (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)
# <Class name="AZStd::vector" field="materialSlotsByLod" type="{22E4F3CF-29C1-54AC-89DD-6FD47A657229}">
# <Class name="AZStd::vector" field="element" type="{7FDDDE36-46C8-5DBC-8566-E792AA358BD9}">
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
@@ -0,0 +1,202 @@
"""
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 *
from LegacyMaterialComponentConverter import *
class Mesh_Component_Converter(Component_Converter):
"""
Converts point lights
"""
def __init__(self, assetCatalogHelper, statsCollector, normalizedProjectDir):
Component_Converter.__init__(self, assetCatalogHelper, statsCollector)
# These are constant for every component in the file
self.materialComponentConverter = Material_Component_Converter(assetCatalogHelper)
self.normalizedProjectDir = normalizedProjectDir
# These need to be reset between each component
self.newAssetId = ""
self.oldMaterialRelativePath = ""
self.oldFbxRelativePathWithoutExtension = ""
def is_this_the_component_im_looking_for(self, xmlElement, parent):
if "name" in xmlElement.keys() and xmlElement.get("name") == "EditorMeshComponent":
return True
return False
def gather_info_for_conversion(self, xmlElement, parent):
# First, get the data that we need
#<Class name="EditorMeshComponent" field="element" version="1" type="{FC315B86-3280-4D03-B4F0-5553D7D08432}">
# <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
# <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
# <Class name="AZ::u64" field="Id" value="17006471516512517700" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# </Class>
# </Class>
# <Class name="MeshComponentRenderNode" field="Static Mesh Render Node" version="1" type="{46FF2BC4-BEF9-4CC4-9456-36C127C310D7}">
# <Class name="bool" field="Visible" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
# <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}"/>
# <Class name="AzFramework::SimpleAssetReference&lt;LmbrCentral::MaterialAsset&gt;" field="Material Override" version="1" type="{B7B8ECC7-FF89-4A76-A50E-4C6CA2B6E6B4}">
# <Class name="SimpleAssetReferenceBase" field="BaseClass1" version="1" type="{E16CA6C5-5C78-4AD9-8E9B-F8C1FB4D1DB8}">
# <Class name="AZStd::string" field="AssetPath" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
for editorMeshComponentChild in xmlElement:
if "name" in editorMeshComponentChild.keys() and editorMeshComponentChild.get("name") == "MeshComponentRenderNode":
for meshComponentRenderNodeChild in editorMeshComponentChild:
if "name" in meshComponentRenderNodeChild.keys() and meshComponentRenderNodeChild.get("name") == "Asset":
# We've found the mesh asset, now extract the assetId and hit
assetId = meshComponentRenderNodeChild.get("value")
# 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}
# atomAssetId looks like this "{BCD2BB63-338F-53BE-98E1-BF847138B78E}:########,
# newAssetId should look like "id={BCD2BB63-338F-53BE-98E1-BF847138B78E}:########,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/airship/airship_pod_outerwalls.azmodel}"
#value"id={E01FB8B5-D2B3-52D8-BC36-644FC0E3B5F4}:268435463,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/props/barrel_01.azmodel}"
# get the relative path to the mesh
meshPathStartIndex = assetId.find("hint={")
meshPathStartIndex += len("hint={")
meshPathEndIndex = assetId.find(".cgf")
self.oldFbxRelativePathWithoutExtension = assetId[meshPathStartIndex: meshPathEndIndex]
# swap the sub-id for the atom model sub-id
atomModelRelativePath = "{0}.azmodel".format(self.oldFbxRelativePathWithoutExtension)
if atomModelRelativePath in self.assetCatalogHelper.relativePathToAssetIdDict:
atomAssetId = self.assetCatalogHelper.relativePathToAssetIdDict[atomModelRelativePath]
atomSubId = atomAssetId[atomAssetId.find(":") + 1:]
# go from string->int->hex->string to get the hex number as a string
hexSubId = str(hex(int(atomSubId)))
# remove the leading characters to get plain hex
hexString = hexSubId[hexSubId.find("x") + 1:]
subIdStartIndex = assetId.find(":")
subIdStartIndex += 1
subIdEndIndex = assetId.find(",")
subIdReplacement = hexString
# swap the type for the atom model type
typeStartIndex = assetId.find("type={")
typeStartIndex += len("type={")
typeEndIndex = assetId.find("},hint")
typeReplacement = "2C7477B6-69C5-45BE-8163-BCD6A275B6D8"
# swap the hint for the atom model extension
extensionStartIndex = assetId.find(".cgf")
extensionEndIndex = extensionStartIndex + len(".cgf")
extensionReplacement = ".azmodel"
self.newAssetId = "".join((assetId[:subIdStartIndex], subIdReplacement, assetId[subIdEndIndex:typeStartIndex], typeReplacement, assetId[typeEndIndex:extensionStartIndex], extensionReplacement, assetId[extensionEndIndex:]))
else:
# we couldn't find the atom model (it was probably a .cgf instead of a .fbx in source)
self.newAssetId = "id={00000000-0000-0000-0000-000000000000}:00000000,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={}"
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))
elif "field" in meshComponentRenderNodeChild.keys() and meshComponentRenderNodeChild.get("field") == "Material Override":
for simpleAssetReferenceChild in meshComponentRenderNodeChild:
if "name" in simpleAssetReferenceChild.keys() and simpleAssetReferenceChild.get("name") == "SimpleAssetReferenceBase":
for simpleAssetReferenceBaseChild in simpleAssetReferenceChild:
if "field" in simpleAssetReferenceBaseChild.keys() and simpleAssetReferenceBaseChild.get("field") == "AssetPath" and "value" in simpleAssetReferenceBaseChild.keys():
# We've found the material override
self.oldMaterialRelativePath = simpleAssetReferenceBaseChild.get("value")
def create_adapter_with_new_model_assetId(self, assetIdValue):
# <Class name="EditorComponentAdapter&lt;AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig &gt;" field="BaseClass1" version="1" type="{52DFE044-18C1-5861-BA2A-EDB61107FEE9}">
# <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
# <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
# <Class name="AZ::u64" field="Id" value="4528867579411318958" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# </Class>
# </Class>
# <Class name="AZ::Render::MeshComponentController" field="Controller" type="{D0F35FAC-4194-4C89-9487-D000DDB8B272}">
# <Class name="AZ::Render::MeshComponentConfig" field="Configuration" type="{63737345-51B1-472B-9355-98F99993909B}">
# <Class name="Asset" field="ModelAsset" value="id={509D78D3-2196-50C2-808C-FEDC3C31380D}:10000007,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/suzanne.azmodel}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
# <Class name="bool" field="ExcludeFromReflectionCubeMaps" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
# </Class>
# </Class>
# </Class>
# </Class>
#<Class name="AZ::Render::EditorMeshComponent" field="element" version="1" type="{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9}">
# <Class name="EditorRenderComponentAdapter&lt;AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig &gt;" field="BaseClass1" type="{3D614286-9164-53B5-833B-4F98D2820BA7}">
# <Class name="EditorComponentAdapter&lt;AZ::Render::MeshComponentController AZ::Render::MeshComponent AZ::Render::MeshComponentConfig &gt;" field="BaseClass1" version="1" type="{52DFE044-18C1-5861-BA2A-EDB61107FEE9}">
# <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
# <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
# <Class name="AZ::u64" field="Id" value="4528867579411318958" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# </Class>
# </Class>
# <Class name="AZ::Render::MeshComponentController" field="Controller" type="{D0F35FAC-4194-4C89-9487-D000DDB8B272}">
# <Class name="AZ::Render::MeshComponentConfig" field="Configuration" type="{63737345-51B1-472B-9355-98F99993909B}">
# <Class name="Asset" field="ModelAsset" value="id={509D78D3-2196-50C2-808C-FEDC3C31380D}:10000007,type={2C7477B6-69C5-45BE-8163-BCD6A275B6D8},hint={objects/suzanne.azmodel}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
# <Class name="bool" field="ExcludeFromReflectionCubeMaps" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
# </Class>
# </Class>
# </Class>
# </Class>
#</Class>
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}"})
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}"})
editorComponentBase = xml.etree.ElementTree.Element("Class", {'name': "EditorComponentBase", 'field': "BaseClass1", 'version': "1", 'type': "{D5346BD4-7F20-444E-B370-327ACD03D4A0}"})
component = xml.etree.ElementTree.Element("Class", {'name': "AZ::Component", 'field': "BaseClass1", 'type': "{EDFCB2CF-F75D-43BE-B26B-F35821B29247}"})
u64 = xml.etree.ElementTree.Element("Class", {'name': "AZ::u64", 'field': "Id", 'value': "4528867579411318958", 'type': "{D6597933-47CD-4FC8-B911-63F3E2B0993A}"})
component.append(u64)
editorComponentBase.append(component)
meshComponentController = create_xml_element_from_string("<Class name=\"AZ::Render::MeshComponentController\" field=\"Controller\" type=\"{D0F35FAC-4194-4C89-9487-D000DDB8B272}\">")
meshComponentConfig = xml.etree.ElementTree.Element("Class", {'name': "AZ::Render::MeshComponentConfig", 'field': "Configuration", 'type': "{63737345-51B1-472B-9355-98F99993909B}"})
modelAsset = xml.etree.ElementTree.Element("Class", {'name': "Asset", 'field': "ModelAsset", 'value': assetIdValue, 'version': "1", 'type': "{77A19D40-8731-4D3C-9041-1B43047366A4}"})
excludeFromReflectionCubeMaps = xml.etree.ElementTree.Element("Class", {'name': "bool", 'field': "ExcludeFromReflectionCubeMaps", 'value': "false", 'type': "{A0CA880C-AFE4-43CB-926C-59AC48496112}"})
meshComponentConfig.append(modelAsset)
meshComponentConfig.append(excludeFromReflectionCubeMaps)
meshComponentController.append(meshComponentConfig)
editorComponentAdapter.append(editorComponentBase)
editorComponentAdapter.append(meshComponentController)
editorRenderComponentAdapter.append(editorComponentAdapter)
return editorRenderComponentAdapter
def convert(self, xmlElement, parent):
"""
Returns a list of xml elements (siblings)
"""
# Now clear the legacy mesh component
xmlElement.clear()
# And replace the content with an Atom mesh component
xmlElement.set("name", "AZ::Render::EditorMeshComponent")
xmlElement.set("field", "element")
xmlElement.set("version", "1")
xmlElement.set("type", "{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9}")
xmlElement.append(self.create_adapter_with_new_model_assetId(self.newAssetId))
if len(self.oldMaterialRelativePath) == 0 or self.oldMaterialRelativePath[0:self.oldMaterialRelativePath.find(".mtl")] == self.oldFbxRelativePathWithoutExtension:
# There was no material override
self.statsCollector.noMaterialOverrideCount += 1
else:
# There was a material override
self.statsCollector.materialOverrideCount += 1
# Now that we have an Atom MeshComponent, we need an Atom MaterialComponent as a neighbor to 'child' (the new mesh component)
atomMaterialInDefaultSlot = self.materialComponentConverter.convert_legacy_mtl_relative_path_to_atom_material_assetid(self.normalizedProjectDir, self.oldMaterialRelativePath, self.oldFbxRelativePathWithoutExtension)
isActor = False
atomMaterialList = self.materialComponentConverter.convert_legacy_mtl_relative_path_to_atom_material_list(self.normalizedProjectDir, self.oldMaterialRelativePath, self.oldFbxRelativePathWithoutExtension, isActor)
parent.append(self.materialComponentConverter.create_material_component_with_material_assignments(atomMaterialInDefaultSlot, atomMaterialList))
def reset(self):
self.newAssetId = ""
self.oldMaterialRelativePath = ""
self.oldFbxRelativePathWithoutExtension = ""
@@ -0,0 +1,226 @@
"""
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 Point Light Component to Atom Point Light Component Conversion Script
"""
from LegacyConversionHelpers import *
from LegacyMaterialComponentConverter import *
class Point_Light_Component_Converter(Component_Converter):
"""
Converts point lights
"""
def __init__(self, assetCatalogHelper, statsCollector, _):
Component_Converter.__init__(self, assetCatalogHelper, statsCollector)
self.color = ""
self.diffuse_multiplier = ""
self.point_max_distance = ""
def convert_legacy_light_intensity_to_atom_intensity(self, diffuse_multiplier: str) -> str:
return str(float(diffuse_multiplier) * 100)
def is_this_the_component_im_looking_for(self, xmlElement, parent):
if "name" in xmlElement.keys() and xmlElement.get("name") == "EditorPointLightComponent":
return True
return False
def gather_info_for_conversion(self, xmlElement, _):
# First, get the data that we need
# <Class name="EditorPointLightComponent" field="element" version="1" type="{00818135-138D-42AD-8657-FF3FD38D9E7A}">
# <Class name="EditorLightComponent" field="BaseClass1" version="2" type="{7C18B273-5BA3-4E0F-857D-1F30BD6B0733}">
# <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
# <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
# <Class name="AZ::u64" field="Id" value="14732701159327458740" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# </Class>
# </Class>
# <Class name="EditorLightConfiguration" field="EditorLightConfiguration" version="1" type="{1D3B114F-8FB2-47BD-9C21-E089F4F37861}">
# <Class name="LightConfiguration" field="BaseClass1" version="8" type="{F4CC7BB4-C541-480C-88FC-C5A8F37CC67F}">
# <Class name="unsigned int" field="LightType" value="0" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
# <Class name="bool" field="Visible" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
# <Class name="bool" field="OnInitially" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
# <Class name="Color" field="Color" value="1.0000000 1.0000000 1.0000000 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
# <Class name="float" field="DiffuseMultiplier" value="50000.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="SpecMultiplier" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="bool" field="Ambient" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
# <Class name="float" field="PointMaxDistance" value="500.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="PointAttenuationBulbSize" value="0.0500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="AreaWidth" value="5.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="AreaHeight" value="5.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="AreaMaxDistance" value="2.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="AreaFOV" value="45.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="ProjectorDistance" value="5.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="ProjectorAttenuationBulbSize" value="0.0500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="ProjectorFOV" value="90.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="ProjectorNearPlane" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# ...
for editorPointLightComponentChild in xmlElement:
#print("Editor Point Light Component Config")
for editorLightComponentChild in editorPointLightComponentChild:
#print("Editor Light Component Config")
#print(f"Name: {editorLightComponentChild.get('name')}, Field: {editorLightComponentChild.get('field')}")
if "name" in editorLightComponentChild.keys() and editorLightComponentChild.get("name") == "EditorLightConfiguration":
#print(f"Editor Light Config. {editorLightComponentChild.get('name')}, {editorLightComponentChild.get('field')}")
for editorLightConfigurationChild in editorLightComponentChild:
for lightConfigurationChild in editorLightConfigurationChild:
#print(f"Name: {lightConfigurationChild.get('name')}, Field: {lightConfigurationChild.get('field')}")
if "field" in lightConfigurationChild.keys() and lightConfigurationChild.get("field") == "Color":
self.color = lightConfigurationChild.get("value")
elif "field" in lightConfigurationChild.keys() and lightConfigurationChild.get("field") == "DiffuseMultiplier":
self.diffuse_multiplier = lightConfigurationChild.get("value")
elif "field" in lightConfigurationChild.keys() and lightConfigurationChild.get("field") == "PointMaxDistance":
self.point_max_distance = lightConfigurationChild.get("value")
#print(f"Color: {self.color}")
#print(f"Diffuse: {self.diffuse_multiplier}")
#print(f"Attenuation Dist.: {self.point_max_distance}")
print('Editor Light Component was touched!')
def __create_adapter_xml(self, color, intensity, attenuationRadius):
# <Class name="AZ::Render::EditorPointLightComponent" field="element" version="1" type="{C4D354BE-5247-41FD-9A8D-550C6772EE5B}">
# <Class name="EditorRenderComponentAdapter&lt;AZ::Render::PointLightComponentController AZ::Render::PointLightComponent PointLightComponentConfi" field="BaseClass1" type="{B09B7A31-789F-5996-AD50-EF71942A5271}">
# <Class name="EditorComponentAdapter&lt;AZ::Render::PointLightComponentController AZ::Render::PointLightComponent PointLightComponentConfig &gt;" field="BaseClass1" version="1" type="{DC9066D5-4557-52C7-B901-4B5626C4F35A}">
# <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
# <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
# <Class name="AZ::u64" field="Id" value="12988262800448744331" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
# </Class>
# </Class>
# <Class name="AZ::Render::PointLightComponentController" field="Controller" version="1" type="{23F82E30-2E1F-45FE-A9A7-B15632ED9EBD}">
# <Class name="PointLightComponentConfig" field="Configuration" version="2" type="{B6FC35BA-D22F-4C20-BFFC-3FE7A48858FA}">
# <Class name="ComponentConfig" field="BaseClass1" version="1" type="{0A7929DF-2932-40EA-B2B3-79BC1C3490D0}"/>
# <Class name="Color" field="Color" value="1.0000000 1.0000000 1.0000000 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
# <Class name="char" field="ColorIntensityMode" value="0" type="{3AB0037F-AF8D-48CE-BCA0-A170D18B2C03}"/>
# <Class name="float" field="Intensity" value="800.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="unsigned char" field="AttenuationRadiusMode" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
# <Class name="float" field="AttenuationRadius" value="89.4427185" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# <Class name="float" field="BulbRadius" value="0.0500000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
# </Class>
# </Class>
# </Class>
# </Class>
# </Class>
editorRenderComponentAdapter = xml.etree.ElementTree.Element("Class", {
'name': "EditorRenderComponentAdapter<AZ::Render::PointLightComponentController AZ::Render::PointLightComponent PointLightComponentConfig >",
'field': "BaseClass1", 'type': "{B09B7A31-789F-5996-AD50-EF71942A5271}"
})
editorComponentAdapter = xml.etree.ElementTree.Element("Class", {
'name': "EditorComponentAdapter<AZ::Render::PointLightComponentController AZ::Render::PointLightComponent PointLightComponentConfig >",
'field': "BaseClass1", 'version': "1", 'type': "{DC9066D5-4557-52C7-B901-4B5626C4F35A}"
})
editorComponentBase = xml.etree.ElementTree.Element("Class", {
'name': "EditorComponentBase",
'field': "BaseClass1",
'version': "1",
'type': "{D5346BD4-7F20-444E-B370-327ACD03D4A0}"
})
component = xml.etree.ElementTree.Element("Class", {
'name': "AZ::Component",
'field': "BaseClass1",
'type': "{EDFCB2CF-F75D-43BE-B26B-F35821B29247}"
})
u64 = xml.etree.ElementTree.Element("Class", {
'name': "AZ::u64",
'field': "Id",
'value': "12988262800448744331",
'type': "{D6597933-47CD-4FC8-B911-63F3E2B0993A}"
})
component.append(u64)
editorComponentBase.append(component)
pointLightComponentController = create_xml_element_from_string(
"<Class name=\"AZ::Render::PointLightComponentController\" field=\"Controller\" version=\"1\" type=\"{23F82E30-2E1F-45FE-A9A7-B15632ED9EBD}\">"
)
pointLightComponentConfig = xml.etree.ElementTree.Element("Class", {
'name': "PointLightComponentConfig",
'field': "Configuration",
'version': "2",
'type': "{B6FC35BA-D22F-4C20-BFFC-3FE7A48858FA}"
})
componentConfig = xml.etree.ElementTree.Element("Class", {
'name': "ComponentConfig",
'field': "BaseClass1",
'version': "1",
'type': "{0A7929DF-2932-40EA-B2B3-79BC1C3490D0}"
})
colorConfig = xml.etree.ElementTree.Element("Class", {
'name': "Color",
'field': "Color",
'value': color,
'type': "{7894072A-9050-4F0F-901B-34B1A0D29417}"
})
colorIntesntiyModeConfig = xml.etree.ElementTree.Element("Class", {
'name': "char",
'field': "ColorIntensityMode",
'value': "0",
'type': "{3AB0037F-AF8D-48CE-BCA0-A170D18B2C03}"
})
inensityConfig = xml.etree.ElementTree.Element("Class", {
'name': "float",
'field': "Intensity",
'value': intensity,
'type': "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"
})
attenuationRadiusModeConfig = xml.etree.ElementTree.Element("Class", {
'name': "unsigned char",
'field': "AttenuationRadiusMode",
'value': "1",
'type': "{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"
})
attenuationRadiusConfig = xml.etree.ElementTree.Element("Class", {
'name': "float",
'field': "AttenuationRadius",
'value': attenuationRadius,
'type': "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"
})
bulbRadiusConfig = xml.etree.ElementTree.Element("Class", {
'name': "float",
'field': "BulbRadius",
'value': "0.05",
'type': "{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"
})
pointLightComponentConfig.append(componentConfig)
pointLightComponentConfig.append(colorConfig)
pointLightComponentConfig.append(colorIntesntiyModeConfig)
pointLightComponentConfig.append(inensityConfig)
pointLightComponentConfig.append(attenuationRadiusModeConfig)
pointLightComponentConfig.append(attenuationRadiusConfig)
pointLightComponentConfig.append(bulbRadiusConfig)
pointLightComponentController.append(pointLightComponentConfig)
editorComponentAdapter.append(editorComponentBase)
editorComponentAdapter.append(pointLightComponentController)
editorRenderComponentAdapter.append(editorComponentAdapter)
return editorRenderComponentAdapter
def convert(self, xmlElement, _):
# Now clear the legacy component
xmlElement.clear()
# And replace the content with an Atom point light component
xmlElement.set("name", "AZ::Render::EditorPointLightComponent")
xmlElement.set("field", "element")
xmlElement.set("version", "1")
xmlElement.set("type", "{C4D354BE-5247-41FD-9A8D-550C6772EE5B}")
xmlElement.append(self.__create_adapter_xml(
self.color,
self.convert_legacy_light_intensity_to_atom_intensity(self.diffuse_multiplier),
self.point_max_distance
))
def reset(self):
self.color = ""
self.diffuse_multiplier = ""
self.point_max_distance = ""
@@ -43,6 +43,7 @@ ly_add_target(
Gem::AtomLyIntegration_CommonFeatures.Public
Gem::LmbrCentral.Static
Gem::GradientSignal.Static
Gem::SurfaceData.Static
Gem::Atom_Feature_Common.Static
Gem::Atom_Bootstrap.Headers
)
@@ -60,8 +60,34 @@ namespace AZ
{
public:
virtual void OnModelReady(const Data::Asset<RPI::ModelAsset>& modelAsset, const Data::Instance<RPI::Model>& model) = 0;
virtual void OnModelPreDestroy() {}
/**
* When connecting to this bus if the asset is ready you will immediately get an OnModelReady event
*/
template<class Bus>
struct ConnectionPolicy
: public AZ::EBusConnectionPolicy<Bus>
{
static void Connect(
typename Bus::BusPtr& busPtr,
typename Bus::Context& context,
typename Bus::HandlerNode& handler,
typename Bus::Context::ConnectLockGuard& connectLock,
const typename Bus::BusIdType& id = 0)
{
AZ::EBusConnectionPolicy<Bus>::Connect(busPtr, context, handler, connectLock, id);
Data::Instance<RPI::Model> model;
MeshComponentRequestBus::EventResult(model, id, &MeshComponentRequestBus::Events::GetModel);
if (model &&
model->GetModelAsset().GetStatus() == AZ::Data::AssetData::AssetStatus::Ready)
{
handler->OnModelReady(model->GetModelAsset(), model);
}
}
};
};
using MeshComponentNotificationBus = EBus<MeshComponentNotifications>;
} // namespace Render
} // namespace AZ
@@ -98,10 +98,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::DefaultAsset, &EditorMaterialComponentSlot::GetDefaultAssetId)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &EditorMaterialComponentSlot::GetLabel)
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
->Attribute("ShowThumbnail", true)
->Attribute("EditButton", ":/Cards/img/UI20/Cards/menu_ico.svg")
->Attribute("EditDescription", "")
->Attribute("EditCallback", &EditorMaterialComponentSlot::OpenPopupMenu)
->Attribute("ThumbnailWithDropDown", &EditorMaterialComponentSlot::OpenPopupMenu)
;
}
}
@@ -109,7 +106,6 @@ namespace AZ
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<EditorMaterialComponentSlot>("EditorMaterialComponentSlot")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
@@ -75,7 +75,6 @@ namespace AZ
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<MaterialComponentConfig>("MaterialComponentConfig")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "render")
->Attribute(AZ::Script::Attributes::Module, "render")
@@ -33,7 +33,6 @@ namespace AZ
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<MaterialComponentRequestBus>("MaterialComponentRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "render")
->Attribute(AZ::Script::Attributes::Module, "render")
@@ -137,7 +137,9 @@ namespace AZ
AZ::Transform transform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(transform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
return m_controller.GetModel()->RayIntersection(transform, src, dir, distance);
AZ::Vector3 ignoreNormal;
return m_controller.GetModel()->RayIntersection(transform, src, dir, distance, ignoreNormal);
}
bool EditorMeshComponent::SupportsEditorRayIntersect()
@@ -197,7 +199,6 @@ namespace AZ
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
AzToolsFramework::Refresh_EntireTree);
}
AZ::u32 EditorMeshComponent::OnConfigurationChanged()
{
// temp variable is needed to hold reference to m_modelAsset while it's being loaded.
@@ -87,6 +87,12 @@ namespace AZ
return values;
}
MeshComponentController::~MeshComponentController()
{
// Release memory, disconnect from buses in the right order and broadcast events so that other components are aware.
Deactivate();
}
void MeshComponentController::Reflect(ReflectContext* context)
{
MeshComponentConfig::Reflect(context);
@@ -130,11 +136,13 @@ namespace AZ
void MeshComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("MaterialReceiverService", 0x0d1a6a74));
provided.push_back(AZ_CRC("MeshService", 0x71d8a455));
}
void MeshComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("MaterialReceiverService", 0x0d1a6a74));
incompatible.push_back(AZ_CRC("MeshService", 0x71d8a455));
}
// [GFX TODO] [ATOM-13339] Remove the ModelAsset id fix up function in MeshComponentController
@@ -157,7 +165,7 @@ namespace AZ
}
else
{
AZ_Assert(false, "Failed to find asset id for [%s] ", modelAsset.GetHint().c_str());
AZ_Error("MeshComponentController", false, "Failed to find asset id for [%s] ", modelAsset.GetHint().c_str());
}
}
}
@@ -192,14 +200,15 @@ namespace AZ
void MeshComponentController::Deactivate()
{
// Buses must be disconnected after unregistering the model, otherwise they can't deliver the events during the process.
UnregisterModel();
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
MeshComponentRequestBus::Handler::BusDisconnect();
TransformNotificationBus::Handler::BusDisconnect();
MaterialReceiverRequestBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
UnregisterModel();
m_meshFeatureProcessor = nullptr;
m_transformInterface = nullptr;
m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
@@ -283,6 +292,7 @@ namespace AZ
{
if (m_meshFeatureProcessor)
{
MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelPreDestroy);
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
}
}
@@ -320,7 +330,7 @@ namespace AZ
const Data::Asset<RPI::ModelAsset>& MeshComponentController::GetModelAsset() const
{
return m_configuration.m_modelAsset;
return GetModel() ? GetModel()->GetModelAsset() : m_configuration.m_modelAsset;
}
Data::AssetId MeshComponentController::GetModelAssetId() const
@@ -66,6 +66,8 @@ namespace AZ
AZ_CLASS_ALLOCATOR(MeshComponentController, AZ::SystemAllocator, 0);
AZ_RTTI(AZ::Render::MeshComponentController, "{D0F35FAC-4194-4C89-9487-D000DDB8B272}");
~MeshComponentController();
static void Reflect(AZ::ReflectContext* context);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
@@ -40,6 +40,7 @@
#include <SkyBox/HDRiSkyboxComponent.h>
#include <SkyBox/PhysicalSkyComponent.h>
#include <Scripting/EntityReferenceComponent.h>
#include <SurfaceData/SurfaceDataMeshComponent.h>
#ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
#include <EditorCommonFeaturesSystemComponent.h>
@@ -71,6 +72,7 @@
#include <SkyBox/EditorHDRiSkyboxComponent.h>
#include <SkyBox/EditorPhysicalSkyComponent.h>
#include <Scripting/EditorEntityReferenceComponent.h>
#include <SurfaceData/EditorSurfaceDataMeshComponent.h>
#endif
namespace AZ
@@ -114,6 +116,7 @@ namespace AZ
GradientWeightModifierComponent::CreateDescriptor(),
DiffuseProbeGridComponent::CreateDescriptor(),
DeferredFogComponent::CreateDescriptor(),
SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(),
#ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
EditorAreaLightComponent::CreateDescriptor(),
@@ -145,6 +148,7 @@ namespace AZ
EditorGradientWeightModifierComponent::CreateDescriptor(),
EditorDiffuseProbeGridComponent::CreateDescriptor(),
EditorDeferredFogComponent::CreateDescriptor(),
SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(),
#endif
});
}
@@ -16,7 +16,7 @@
#include <AzFramework/Components/ComponentAdapter.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/GradientWeightModifier/GradientWeightModifierComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/GradientWeightModifier/GradientWeightModifierComponentConfig.h>
#include <PostProcess/GradientWeightModifier/GradientWeightModifierComponentController.h>
#include <PostProcess/GradientWeightModifier/GradientWeightModifierController.h>
namespace AZ
{
@@ -10,7 +10,7 @@
*
*/
#include <PostProcess/GradientWeightModifier/GradientWeightModifierComponentController.h>
#include <PostProcess/GradientWeightModifier/GradientWeightModifierController.h>
namespace AZ
{
@@ -0,0 +1,25 @@
/*
* 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.
*
*/
#include "EditorSurfaceDataMeshComponent.h"
#include <AzCore/Serialization/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <LmbrCentral/Dependency/DependencyNotificationBus.h>
namespace SurfaceData
{
void EditorSurfaceDataMeshComponent::Reflect(AZ::ReflectContext* context)
{
BaseClassType::ReflectSubClass<EditorSurfaceDataMeshComponent, BaseClassType>(context, 2, &LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType,2>);
}
}
@@ -0,0 +1,38 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Module/Module.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
#include <SurfaceData/SurfaceDataMeshComponent.h>
#include <LmbrCentral/Component/EditorWrappedComponentBase.h>
namespace SurfaceData
{
class EditorSurfaceDataMeshComponent
: public LmbrCentral::EditorWrappedComponentBase<SurfaceDataMeshComponent, SurfaceDataMeshConfig>
{
public:
using BaseClassType = LmbrCentral::EditorWrappedComponentBase<SurfaceDataMeshComponent, SurfaceDataMeshConfig>;
AZ_EDITOR_COMPONENT(EditorSurfaceDataMeshComponent, "{4D73E979-5463-4B75-AE46-70B1E52CBF43}", BaseClassType);
static void Reflect(AZ::ReflectContext* context);
static constexpr const char* const s_categoryName = "Surface Data";
static constexpr const char* const s_componentName = "Mesh Surface Tag Emitter";
static constexpr const char* const s_componentDescription = "Enables a static mesh to emit surface tags";
static constexpr const char* const s_icon = "Editor/Icons/Components/SurfaceData.svg";
static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/SurfaceData.png";
static constexpr const char* const s_helpUrl = "https://docs.aws.amazon.com/console/lumberyard/surfacedata/mesh-surface-tag-emitter";
};
}
@@ -0,0 +1,288 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensor's.
*
* 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.
*
*/
#include "SurfaceDataMeshComponent.h"
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <LmbrCentral/Rendering/MeshAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
#include <MathConversion.h>
namespace SurfaceData
{
void SurfaceDataMeshConfig::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceDataMeshConfig, AZ::ComponentConfig>()
->Version(0)
->Field("SurfaceTags", &SurfaceDataMeshConfig::m_tags)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<SurfaceDataMeshConfig>(
"Mesh Surface Tag Emitter", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &SurfaceDataMeshConfig::m_tags, "Generated Tags", "")
;
}
}
}
void SurfaceDataMeshComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
}
void SurfaceDataMeshComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
}
void SurfaceDataMeshComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("MeshService", 0x71d8a455));
}
void SurfaceDataMeshComponent::Reflect(AZ::ReflectContext* context)
{
SurfaceDataMeshConfig::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceDataMeshComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &SurfaceDataMeshComponent::m_configuration)
;
}
}
SurfaceDataMeshComponent::SurfaceDataMeshComponent(const SurfaceDataMeshConfig& configuration)
: m_configuration(configuration)
{
}
void SurfaceDataMeshComponent::Activate()
{
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId());
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_refresh = false;
// Update the cached mesh data and bounds, then register the surface data provider
UpdateMeshData();
}
void SurfaceDataMeshComponent::Deactivate()
{
if (m_providerHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
}
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
AZ::Render::MeshComponentNotificationBus::Handler::BusDisconnect();
m_refresh = false;
// Clear the cached mesh data
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
m_meshAssetData = {};
m_meshBounds = AZ::Aabb::CreateNull();
m_meshWorldTM = AZ::Transform::CreateIdentity();
m_meshWorldTMInverse = AZ::Transform::CreateIdentity();
}
}
bool SurfaceDataMeshComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const SurfaceDataMeshConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool SurfaceDataMeshComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<SurfaceDataMeshConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
bool SurfaceDataMeshComponent::DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
// test AABB as first pass to claim the point
const AZ::Vector3 testPosition = AZ::Vector3(
inPosition.GetX(),
inPosition.GetY(),
(m_meshBounds.GetMax().GetZ() + m_meshBounds.GetMin().GetZ()) * 0.5f);
if (!m_meshBounds.Contains(testPosition))
{
return false;
}
AZ::RPI::ModelAsset* mesh = m_meshAssetData.GetAs<AZ::RPI::ModelAsset>();
if (!mesh)
{
return false;
}
const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding);
const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ();
return GetMeshRayIntersection(*mesh, m_meshWorldTM, m_meshWorldTMInverse, rayOrigin, rayDirection, outPosition, outNormal);
}
void SurfaceDataMeshComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{
AZ::Vector3 hitPosition;
AZ::Vector3 hitNormal;
if (DoRayTrace(inPosition, hitPosition, hitNormal))
{
SurfacePoint point;
point.m_entityId = GetEntityId();
point.m_position = hitPosition;
point.m_normal = hitNormal;
AddMaxValueForMasks(point.m_masks, m_configuration.m_tags, 1.0f);
surfacePointList.push_back(point);
}
}
AZ::Aabb SurfaceDataMeshComponent::GetSurfaceAabb() const
{
return m_meshBounds;
}
SurfaceTagVector SurfaceDataMeshComponent::GetSurfaceTags() const
{
return m_configuration.m_tags;
}
void SurfaceDataMeshComponent::OnCompositionChanged()
{
if (!m_refresh)
{
m_refresh = true;
AZ::TickBus::Handler::BusConnect();
}
}
void SurfaceDataMeshComponent::OnModelReady([[maybe_unused]] const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset, [[maybe_unused]] const AZ::Data::Instance<AZ::RPI::Model>& model)
{
OnCompositionChanged();
}
void SurfaceDataMeshComponent::OnTransformChanged(const AZ::Transform & local, const AZ::Transform & world)
{
(void)local;
(void)world;
OnCompositionChanged();
}
void SurfaceDataMeshComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
if (m_refresh)
{
UpdateMeshData();
m_refresh = false;
}
AZ::TickBus::Handler::BusDisconnect();
}
void SurfaceDataMeshComponent::UpdateMeshData()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
bool meshValidBeforeUpdate = false;
bool meshValidAfterUpdate = false;
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
meshValidBeforeUpdate = (m_meshAssetData.GetAs<AZ::RPI::ModelAsset>() != nullptr) && (m_meshBounds.IsValid());
m_meshAssetData = {};
AZ::Render::MeshComponentRequestBus::EventResult(m_meshAssetData, GetEntityId(), &AZ::Render::MeshComponentRequestBus::Events::GetModelAsset);
m_meshBounds = AZ::Aabb::CreateNull();
AZ::Render::MeshComponentRequestBus::EventResult(m_meshBounds, GetEntityId(), &AZ::Render::MeshComponentRequestBus::Events::GetWorldBounds);
m_meshWorldTM = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(m_meshWorldTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
m_meshWorldTMInverse = m_meshWorldTM.GetInverse();
meshValidAfterUpdate = (m_meshAssetData.GetAs<AZ::RPI::ModelAsset>() != nullptr) && (m_meshBounds.IsValid());
}
SurfaceDataRegistryEntry registryEntry;
registryEntry.m_entityId = GetEntityId();
registryEntry.m_bounds = GetSurfaceAabb();
registryEntry.m_tags = GetSurfaceTags();
if (!meshValidBeforeUpdate && !meshValidAfterUpdate)
{
// We didn't have a valid mesh asset before or after running this, so do nothing.
}
else if (!meshValidBeforeUpdate && meshValidAfterUpdate)
{
// Our mesh has become valid, so register as a provider and save off the provider handle
AZ_Assert((m_providerHandle == InvalidSurfaceDataRegistryHandle), "Surface data handle is initialized before our mesh became active");
AZ_Assert(m_meshBounds.IsValid(), "Mesh Geometry isn't correctly initialized.");
SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, registryEntry);
// Start listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle);
}
else if (meshValidBeforeUpdate && !meshValidAfterUpdate)
{
// Our mesh has stopped being valid, so unregister and stop listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
}
else if (meshValidBeforeUpdate && meshValidAfterUpdate)
{
// Our mesh was valid before and after, it just changed in some way, so update our registry entry.
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataProvider, m_providerHandle, registryEntry);
}
}
}
@@ -0,0 +1,106 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace LmbrCentral
{
template<typename, typename>
class EditorWrappedComponentBase;
}
namespace SurfaceData
{
constexpr float s_rayAABBHeightPadding = 0.1f;
class SurfaceDataMeshConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(SurfaceDataMeshConfig, AZ::SystemAllocator, 0);
AZ_RTTI(SurfaceDataMeshConfig, "{764C602E-7CA8-4BCC-AB2D-3E46623B3A20}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
SurfaceTagVector m_tags;
};
class SurfaceDataMeshComponent
: public AZ::Component
, public AZ::TickBus::Handler
, public AZ::TransformNotificationBus::Handler
, public AZ::Render::MeshComponentNotificationBus::Handler
, public SurfaceDataProviderRequestBus::Handler
{
public:
template<typename, typename> friend class LmbrCentral::EditorWrappedComponentBase;
AZ_COMPONENT(SurfaceDataMeshComponent, "{F8915F34-BE8B-40B4-B7E8-01EBF3DA1C95}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
SurfaceDataMeshComponent(const SurfaceDataMeshConfig& configuration);
SurfaceDataMeshComponent() = default;
~SurfaceDataMeshComponent() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// MeshComponentNotificationBus
void OnModelReady(const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset, const AZ::Data::Instance<AZ::RPI::Model>& model) override;
//////////////////////////////////////////////////////////////////////////
// TransformNotificationBus
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
////////////////////////////////////////////////////////////////////////
// AZ::TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override;
private:
bool DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const;
void UpdateMeshData();
void OnCompositionChanged();
AZ::Aabb GetSurfaceAabb() const;
SurfaceTagVector GetSurfaceTags() const;
SurfaceDataMeshConfig m_configuration;
SurfaceDataRegistryHandle m_providerHandle = InvalidSurfaceDataRegistryHandle;
// cached data
AZStd::atomic_bool m_refresh{ false };
mutable AZStd::recursive_mutex m_cacheMutex;
AZ::Data::Asset<AZ::Data::AssetData> m_meshAssetData;
AZ::Transform m_meshWorldTM = AZ::Transform::CreateIdentity();
AZ::Transform m_meshWorldTMInverse = AZ::Transform::CreateIdentity();
AZ::Aabb m_meshBounds = AZ::Aabb::CreateNull();
};
}
@@ -35,7 +35,7 @@ namespace AZ
//! ThumbnailRendererData encapsulates all data used by thumbnail renderer and caches assets
struct ThumbnailRendererData final
{
static constexpr const char* LightingPresetPath = "lightingpresets/default.lightingpreset.azasset";
static constexpr const char* LightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
static constexpr const char* DefaultModelPath = "materialeditor/viewportmodels/quadsphere.azmodel";
static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial";
@@ -147,7 +147,7 @@ namespace AZ
data->m_renderPipeline->SetDefaultView(data->m_view);
// Create lighting preset
data->m_lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(LightingPresetPath);
data->m_lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(ThumbnailRendererData::LightingPresetPath);
if (data->m_lightingPresetAsset.IsReady())
{
auto preset = data->m_lightingPresetAsset->GetDataAs<Render::LightingPreset>();
@@ -31,7 +31,6 @@ namespace AZ
void Start() override;
private:
static constexpr const char* LightingPresetPath = "lightingpresets/default.lightingpreset.azasset";
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.1f;
static constexpr float FarDist = 100.0f;
@@ -111,5 +111,7 @@ set(FILES
Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h
Source/Scripting/EditorEntityReferenceComponent.cpp
Source/Scripting/EditorEntityReferenceComponent.h
Source/SurfaceData/EditorSurfaceDataMeshComponent.cpp
Source/SurfaceData/EditorSurfaceDataMeshComponent.h
Resources/AtomLyIntegrationResources.qrc
)
@@ -117,8 +117,8 @@ set(FILES
Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponent.h
Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponent.cpp
Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponentConfig.cpp
Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponentController.h
Source/PostProcess/GradientWeightModifier/GradientWeightModifierComponentController.cpp
Source/PostProcess/GradientWeightModifier/GradientWeightModifierController.h
Source/PostProcess/GradientWeightModifier/GradientWeightModifierController.cpp
Source/ScreenSpace/DeferredFogComponentController.h
Source/ScreenSpace/DeferredFogComponentController.cpp
Source/ScreenSpace/DeferredFogComponent.h
@@ -145,4 +145,6 @@ set(FILES
Source/Scripting/EntityReferenceComponentConfig.cpp
Source/Scripting/EntityReferenceComponentController.cpp
Source/Scripting/EntityReferenceComponentController.h
Source/SurfaceData/SurfaceDataMeshComponent.cpp
Source/SurfaceData/SurfaceDataMeshComponent.h
)
@@ -24,8 +24,6 @@
void CAtomShimRenderer::EF_Init()
{
bool nv = 0;
m_RP.m_MaxVerts = 600;
m_RP.m_MaxTris = 300;
@@ -48,7 +46,7 @@ void CAtomShimRenderer::EF_Init()
m_RP.m_ObjectsPool = (CRenderObject*)CryModuleMemalign(sizeof(CRenderObject) * (m_RP.m_nNumObjectsInPool * RT_COMMAND_BUF_COUNT), 16);
for (int j = 0; j < (int)(m_RP.m_nNumObjectsInPool * RT_COMMAND_BUF_COUNT); j++)
{
CRenderObject* pRendObj = new(&m_RP.m_ObjectsPool[j])CRenderObject();
new(&m_RP.m_ObjectsPool[j])CRenderObject();
}
@@ -538,8 +538,6 @@ void CAtomShimRenderAuxGeom::DrawAABBs(const AABB* aabb, uint32 aabbCount, bool
auto defaultScene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
if (auto auxGeom = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(defaultScene))
{
AZ::RPI::AuxGeomDraw::DrawStyle drawStyle = LyDrawStyleToAZDrawStyle(bSolid, bbDrawStyle);
for (int i = 0; i < aabbCount; ++aabbCount)
{
auxGeom->DrawAabb(
@@ -207,8 +207,7 @@ void CAtomShimRenderer::BeginFrame()
shaderOptionsWrap.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("false")));
m_shaderVariantWrap = m_dynamicDraw->UseShaderVariant(shaderOptionsWrap);
AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg = m_dynamicDraw->NewDrawSrg();
const AZ::RHI::ShaderResourceGroupLayout* layout = drawSrg->GetAsset()->GetLayout();
m_dynamicDraw->NewDrawSrg();
m_isFinalInitializationDone = true;
}
@@ -377,11 +376,6 @@ void CAtomShimRenderer::DrawImage(float xpos, float ypos, float w, float h, int
///////////////////////////////////////////
void CAtomShimRenderer::DrawImageWithUV(float xpos, float ypos, float z, float w, float h, int texture_id, float s[4], float t[4], float r, float g, float b, float a, bool filtered)
{
float fx = xpos;
float fy = ypos;
float fw = w;
float fh = h;
SetCullMode(R_CULL_DISABLE);
EF_SetColorOp(eCO_MODULATE, eCO_MODULATE, DEF_TEXARG0, DEF_TEXARG0);
EF_SetSrgbWrite(false);
@@ -559,7 +553,6 @@ void CAtomShimRenderer::SetCamera(const CCamera& cam)
mViewFinal.m31 = mView.m32;
mViewFinal.m32 = -mView.m31;
Matrix44A* m = &m_RP.m_TI[nThreadID].m_matView;
m_RP.m_TI[nThreadID].m_matView = mViewFinal;
mViewFinal.m30 = 0;
@@ -344,7 +344,6 @@ void AtomShimTexture::CreateFromImage(const AZ::Data::Instance<AZ::RPI::Image>&
{
AZ::RHI::Format rhiViewFormat = AZ::RHI::Format::Unknown;
AZ::RHI::ImageViewDescriptor viewDesc = AZ::RHI::ImageViewDescriptor(rhiViewFormat);
AZ::RHI::Factory& factory = AZ::RHI::Factory::Get();
AZ::RHI::Image* rhiImage = image->GetRHIImage();
AZ::RHI::Ptr<AZ::RHI::ImageView> imageView = rhiImage->GetImageView(viewDesc);
@@ -81,7 +81,6 @@ namespace AZ
lodIndexCount = 0;
lodVertexCount = 0;
uint32_t subMeshIndexOffset = 0;
const Data::Asset<RPI::ModelLodAsset>& lodAsset = actor->GetMeshAsset()->GetLodAssets()[lodIndex];
const AZStd::array_view<RPI::ModelLodAsset::Mesh> modelMeshes = lodAsset->GetMeshes();
for (const RPI::ModelLodAsset::Mesh& modelMesh : modelMeshes)
@@ -395,7 +394,6 @@ namespace AZ
uvBufferData.resize_no_construct(lodVertexCount);
// Now iterate over the actual data and populate the data for the per-actor buffers
size_t lodVertexStart = 0;
size_t indexBufferOffset = 0;
size_t vertexBufferOffset = 0;
size_t skinnedMeshSubmeshIndex = 0;
@@ -410,7 +408,6 @@ namespace AZ
// Each of these is one long buffer containing the data for all sub-meshes in the joint
const AZ::Vector3* sourcePositions = static_cast<const AZ::Vector3*>(mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS));
const AZ::Vector3* sourceNormals = static_cast<const AZ::Vector3*>(mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_NORMALS));
const uint32_t* sourceOriginalVertex = static_cast<const uint32_t*>(mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_ORGVTXNUMBERS));
const AZ::Vector4* sourceTangents = static_cast<const AZ::Vector4*>(mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_TANGENTS));
const AZ::Vector3* sourceBitangents = static_cast<const AZ::Vector3*>(mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_BITANGENTS));
const AZ::Vector2* sourceUVs = static_cast<const AZ::Vector2*>(mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_UVCOORDS, 0));
@@ -465,10 +465,15 @@ namespace AZ
MaterialComponentNotificationBus::Handler::BusConnect(m_entityId);
MeshComponentRequestBus::Handler::BusConnect(m_entityId);
LmbrCentral::MeshComponentRequestBus::Handler::BusConnect(m_entityId);
const Data::Instance<RPI::Model> model = m_meshFeatureProcessor->GetModel(*m_meshHandle);
MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelReady, model->GetModelAsset(), model);
}
void AtomActorInstance::UnregisterActor()
{
MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelPreDestroy);
LmbrCentral::MeshComponentRequestBus::Handler::BusDisconnect();
MeshComponentRequestBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
@@ -14,6 +14,7 @@
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
#include <AzFramework/Input/Mappings/InputMappingAnd.h>
#include <AzFramework/Input/Mappings/InputMappingOr.h>
@@ -21,6 +22,9 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <Atom/Feature/ImGui/SystemBus.h>
#include <ImGuiContextScope.h>
#include <ImGui/ImGuiPass.h>
#include <imgui/imgui.h>
using namespace AzFramework;
@@ -31,7 +35,7 @@ namespace AZ
////////////////////////////////////////////////////////////////////////////////////////////////
constexpr const char* DebugConsoleInputContext = "DebugConsoleInputContext";
const InputChannelId ToggleDebugConsoleInputChannelId("ToggleDebugConsole");
const InputChannelId ThumbstickL3AndR3InputChannelId("BothGamepadTriggers");
const InputChannelId ThumbstickL3AndR3InputChannelId("ThumbstickL3AndR3");
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Color GetColorForLogLevel(const AZ::LogLevel& logLevel)
@@ -79,6 +83,16 @@ namespace AZ
, m_maxEntriesToDisplay(maxEntriesToDisplay)
, m_maxInputHistorySize(maxInputHistorySize)
{
// The debug console is currently only supported when running the standalone launcher.
// It does function correctly when running the editor if you remove this check, but it
// conflicts with the legacy debug console that also shows at the bottom of the editor.
AZ::ApplicationTypeQuery applicationType;
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::QueryApplicationType, applicationType);
if (!applicationType.IsGame())
{
return;
}
// Create an input mapping so that the debug console can be toggled by 'L3+R3' on a gamepad.
AZStd::shared_ptr<InputMappingAnd> inputMappingL3AndR3 = AZStd::make_shared<InputMappingAnd>(ThumbstickL3AndR3InputChannelId, m_inputContext);
inputMappingL3AndR3->AddSourceInput(InputDeviceGamepad::Button::L3);
@@ -112,33 +126,34 @@ namespace AZ
// Bind our custom log handler.
AZ::Interface<AZ::ILogger>::Get()->BindLogHandler(m_logHandler);
// Connect to receive tick events.
AZ::TickBus::Handler::BusConnect();
// Connect to receive render tick events.
auto atomViewportRequests = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
const AZ::Name contextName = atomViewportRequests->GetDefaultViewportContextName();
AZ::RPI::ViewportContextNotificationBus::Handler::BusConnect(contextName);
}
////////////////////////////////////////////////////////////////////////////////////////////////
DebugConsole::~DebugConsole()
{
// Disconnect to stop receiving tick events.
AZ::TickBus::Handler::BusDisconnect();
// Disconnect to stop receiving render tick events.
AZ::RPI::ViewportContextNotificationBus::Handler::BusDisconnect();
// Disconnect our custom log handler.
m_logHandler.Disconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
int DebugConsole::GetTickOrder()
void DebugConsole::OnRenderTick()
{
return AZ::ComponentTickBus::TICK_UI;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void DebugConsole::OnTick([[maybe_unused]]float deltaTime,
[[maybe_unused]]AZ::ScriptTimePoint scriptTimePoint)
{
if (m_isShowing)
if (!m_isShowing)
{
DrawDebugConsole();
return;
}
const bool continueShowing = DrawDebugConsole();
if (!continueShowing)
{
ToggleIsShowing();
}
}
@@ -148,7 +163,7 @@ namespace AZ
if (inputChannel.GetInputChannelId() == ToggleDebugConsoleInputChannelId &&
inputChannel.IsStateBegan())
{
m_isShowing = !m_isShowing;
ToggleIsShowing();
return true;
}
return false;
@@ -292,14 +307,26 @@ namespace AZ
}
////////////////////////////////////////////////////////////////////////////////////////////////
void DebugConsole::DrawDebugConsole()
bool DebugConsole::DrawDebugConsole()
{
// Get the default ImGui pass.
AZ::Render::ImGuiPass* defaultImGuiPass = nullptr;
AZ::Render::ImGuiSystemRequestBus::BroadcastResult(defaultImGuiPass, &AZ::Render::ImGuiSystemRequestBus::Events::GetDefaultImGuiPass);
if (!defaultImGuiPass)
{
return false;
}
// Create an ImGui context scope using the default ImGui pass context.
ImGui::ImGuiContextScope contextScope(defaultImGuiPass->GetContext());
// Draw the debug console in a closeable, moveable, and resizeable IMGUI window.
bool continueShowing = true;
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Debug Console", &m_isShowing))
if (!ImGui::Begin("Debug Console", &continueShowing))
{
ImGui::End();
return;
return false;
}
// Show a scrolling child region in which to display all debug log entires.
@@ -375,5 +402,38 @@ namespace AZ
}
ImGui::End();
return continueShowing;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AzFramework::SystemCursorState GetDesiredSystemCursorState()
{
AZ::ApplicationTypeQuery applicationType;
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::QueryApplicationType, applicationType);
return applicationType.IsEditor() ?
AzFramework::SystemCursorState::ConstrainedAndVisible :
AzFramework::SystemCursorState::UnconstrainedAndVisible;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void DebugConsole::ToggleIsShowing()
{
m_isShowing = !m_isShowing;
if (m_isShowing)
{
AzFramework::InputSystemCursorRequestBus::EventResult(m_previousSystemCursorState,
AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::GetSystemCursorState);
AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
GetDesiredSystemCursorState());
}
else
{
AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
m_previousSystemCursorState);
}
}
}
@@ -12,6 +12,7 @@
#pragma once
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Input/Contexts/InputContext.h>
#include <AzFramework/Input/Events/InputChannelEventFilter.h>
#include <AzFramework/Input/Events/InputChannelEventListener.h>
@@ -23,6 +24,8 @@
#include <AzCore/std/containers/deque.h>
#include <AzCore/std/string/string.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
struct ImGuiInputTextCallbackData;
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -43,8 +46,8 @@ namespace AZ
//! - The '~' key on a keyboard.
//! - Both the 'L3+R3' buttons on a gamepad.
//! - The fourth finger press on a touch screen.
class DebugConsole : public AZ::TickBus::Handler
, public AzFramework::InputChannelEventListener
class DebugConsole : public AzFramework::InputChannelEventListener
, public AZ::RPI::ViewportContextNotificationBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
@@ -67,12 +70,8 @@ namespace AZ
~DebugConsole() override;
////////////////////////////////////////////////////////////////////////////////////////////
// AZ::TickBus::Handler
int GetTickOrder() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::TickEvents::OnTick
void OnTick(float deltaTime, AZ::ScriptTimePoint scriptTimePoint) override;
//! \ref AZ::RPI::ViewportContextRequestsInterface
void OnRenderTick() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelEventListener::OnInputChannelEventFiltered
@@ -105,7 +104,12 @@ namespace AZ
////////////////////////////////////////////////////////////////////////////////////////////
//! Draw the debug console.
void DrawDebugConsole();
//! \return True if we should continue showing the debug console, false otherwise.
bool DrawDebugConsole();
////////////////////////////////////////////////////////////////////////////////////////////
//! Toggle whether the debug console is showing or not.
void ToggleIsShowing();
private:
////////////////////////////////////////////////////////////////////////////////////////////
@@ -115,6 +119,7 @@ namespace AZ
AZ::ILogger::LogEvent::Handler m_logHandler; //!< Handler that receives log events to display.
AzFramework::InputContext m_inputContext; //!< Input context used to open/close the console.
char m_inputBuffer[1028] = {}; //!< The character buffer used to accept text input.
AzFramework::SystemCursorState m_previousSystemCursorState; //! The last system cursor state.
int m_currentHistoryIndex = -1; //!< The current index into the input history when browsing.
int m_maxEntriesToDisplay = DefaultMaxEntriesToDisplay; //!< The maximum entries to display.
int m_maxInputHistorySize = DefaultMaxInputHistorySize; //!< The maximum input history size.
@@ -16,6 +16,11 @@
#include <AzFramework/Windowing/WindowBus.h>
#include <Atom/Feature/ImGui/ImGuiUtils.h>
#include <Atom/Feature/ImGui/SystemBus.h>
#include <Atom/RPI.Public/ViewportContext.h>
#if defined(IMGUI_ENABLED)
#include <ImGuiBus.h>
#endif
namespace AZ
{
@@ -49,16 +54,28 @@ namespace AZ
void ImguiAtomSystemComponent::Activate()
{
ImGui::OtherActiveImGuiRequestBus::Handler::BusConnect();
auto atomViewportRequests = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
const AZ::Name contextName = atomViewportRequests->GetDefaultViewportContextName();
AZ::RPI::ViewportContextNotificationBus::Handler::BusConnect(contextName);
}
void ImguiAtomSystemComponent::Deactivate()
{
ImGui::OtherActiveImGuiRequestBus::Handler::BusDisconnect();
AZ::RPI::ViewportContextNotificationBus::Handler::BusDisconnect();
}
void ImguiAtomSystemComponent::RenderImGuiBuffers(const ImDrawData& drawData)
{
Render::ImGuiSystemRequestBus::Broadcast(&Render::ImGuiSystemRequests::RenderImGuiBuffersToDefaultPass, drawData);
Render::ImGuiSystemRequestBus::Broadcast(&Render::ImGuiSystemRequests::RenderImGuiBuffersToCurrentViewport, drawData);
}
void ImguiAtomSystemComponent::OnRenderTick()
{
#if defined(IMGUI_ENABLED)
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::Render);
#endif
}
}
}
@@ -17,6 +17,8 @@
#include <OtherActiveImGuiBus.h>
#include <DebugConsole.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
namespace AZ
{
namespace LYIntegration
@@ -28,6 +30,8 @@ namespace AZ
class ImguiAtomSystemComponent final
: public AZ::Component
, public ImGui::OtherActiveImGuiRequestBus::Handler
// The Imgui Gem's ImGuiManager can handle this directly when engine is fully switched to Atom Renderer
, public AZ::RPI::ViewportContextNotificationBus::Handler
{
public:
AZ_COMPONENT(ImguiAtomSystemComponent, "{D423E075-D971-435A-A9C1-57C3B0623A9B}");
@@ -43,10 +47,14 @@ namespace AZ
void Activate() override;
void Deactivate() override;
private:
// OtherActiveImGuiRequestBus overrides ...
void RenderImGuiBuffers(const ImDrawData& drawData) override;
private:
// ViewportContextNotificationBus overrides...
void OnRenderTick() override;
DebugConsole m_debugConsole;
};
} // namespace LYIntegration
@@ -363,4 +363,3 @@ base_directory = sys.argv[-3]
destination_directory = sys.argv[-2]
modify_naming = sys.argv[-1]
CreateMayaFiles(file_list, base_directory, destination_directory, modify_naming)
@@ -103,6 +103,7 @@ _LOGGER.debug('Initializing: {0}.'.format({module_name}))
class LegacyFilesConverter(QtWidgets.QDialog):
def __init__(self, parent=None):
super(LegacyFilesConverter, self).__init__(parent)
@@ -158,9 +158,10 @@ def bootstrap_dccsi_py_libs(dccsi_dirpath=return_stub_dir()):
"""Builds and adds local site dir libs based on py version"""
from azpy.constants import STR_DCCSI_PYTHON_LIB_PATH # a path string constructor
_DCCSI_PYTHON_LIB_PATH = STR_DCCSI_PYTHON_LIB_PATH.format(dccsi_dirpath,
sys.version_info[0],
sys.version_info[1])
_DCCSI_PYTHON_LIB_PATH = "E:\\P4\\jromnoa_spectra_atom_2\\dev\\Tools\\Python\\3.7.5\\windows\\Lib\\site-packages"
# _DCCSI_PYTHON_LIB_PATH = STR_DCCSI_PYTHON_LIB_PATH.format(dccsi_dirpath,
# sys.version_info[0],
# sys.version_info[1])
if os.path.exists(_DCCSI_PYTHON_LIB_PATH):
_LOGGER.debug('Performed site.addsitedir({})'.format(_DCCSI_PYTHON_LIB_PATH))