diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py
new file mode 100644
index 0000000000..b899d7dcde
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py
@@ -0,0 +1,102 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+
+Hydra script that is used to create a new level with a default rendering setup.
+After the level is setup, screenshots are diffed against golden images are used to verify pass/fail results of the test.
+
+See the run() function for more in-depth test info.
+"""
+
+import os
+import sys
+
+import azlmbr.legacy.general as general
+
+sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+
+import editor_python_test_tools.hydra_editor_utils as hydra
+from editor_python_test_tools.editor_test_helper import EditorTestHelper
+from atom_renderer.atom_utils.benchmark_utils import BenchmarkHelper
+
+SCREEN_WIDTH = 1280
+SCREEN_HEIGHT = 720
+DEGREE_RADIAN_FACTOR = 0.0174533
+
+helper = EditorTestHelper(log_prefix="Test_Atom_BasicLevelSetup")
+
+
+def run():
+ """
+ 1. View -> Layouts -> Restore Default Layout, sets the viewport to ratio 16:9 @ 1280 x 720
+ 2. Runs console command r_DisplayInfo = 0
+ 3. Opens AtomFeatureIntegrationBenchmark level
+ 4. Initializes benchmark helper with benchmark name to capture benchmark metadata.
+ 5. Idles for 100 frames, then collects pass timings for 100 frames.
+ :return: None
+ """
+ def initial_viewport_setup(screen_width, screen_height):
+ general.set_viewport_size(screen_width, screen_height)
+ general.update_viewport()
+ helper.wait_for_condition(
+ function=lambda: helper.isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1)
+ and helper.isclose(a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1),
+ timeout_in_seconds=4.0
+ )
+ result = helper.isclose(a=general.get_viewport_size().x, b=SCREEN_WIDTH, rel_tol=0.1) and helper.isclose(
+ a=general.get_viewport_size().y, b=SCREEN_HEIGHT, rel_tol=0.1)
+ general.log(general.get_viewport_size().x)
+ general.log(general.get_viewport_size().y)
+ general.log(general.get_viewport_size().z)
+ general.log(f"Viewport is set to the expected size: {result}")
+ general.run_console("r_DisplayInfo = 0")
+
+ def after_level_load():
+ """Function to call after creating/opening a level to ensure it loads."""
+ # Give everything a second to initialize.
+ general.idle_enable(True)
+ general.idle_wait(1.0)
+ general.update_viewport()
+ general.idle_wait(0.5) # half a second is more than enough for updating the viewport.
+
+ # Close out problematic windows, FPS meters, and anti-aliasing.
+ if general.is_helpers_shown(): # Turn off the helper gizmos if visible
+ general.toggle_helpers()
+ general.idle_wait(1.0)
+ if general.is_pane_visible("Error Report"): # Close Error Report windows that block focus.
+ general.close_pane("Error Report")
+ if general.is_pane_visible("Error Log"): # Close Error Log windows that block focus.
+ general.close_pane("Error Log")
+ general.idle_wait(1.0)
+ general.run_console("r_displayInfo=0")
+ general.run_console("r_antialiasingmode=0")
+ general.idle_wait(1.0)
+
+ return True
+
+ # Wait for Editor idle loop before executing Python hydra scripts.
+ general.idle_enable(True)
+
+ general.open_level_no_prompt("AtomFeatureIntegrationBenchmark")
+
+ # Basic setup after opening level.
+ after_level_load()
+ initial_viewport_setup(SCREEN_WIDTH, SCREEN_HEIGHT)
+
+ general.enter_game_mode()
+ general.idle_wait(1.0)
+ helper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=2.0)
+ benchmarker = BenchmarkHelper("AtomFeatureIntegrationBenchmark")
+ benchmarker.capture_benchmark_metadata()
+ general.idle_wait_frames(100)
+ for i in range(1, 101):
+ benchmarker.capture_pass_timestamp(i)
+ general.exit_game_mode()
+ helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=2.0)
+ general.log("Capturing complete.")
+
+
+if __name__ == "__main__":
+ run()
diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py
new file mode 100644
index 0000000000..21c7489ed3
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py
@@ -0,0 +1,84 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
+import azlmbr.atom
+import azlmbr.legacy.general as general
+
+FOLDER_PATH = '@user@/Scripts/PerformanceBenchmarks'
+METADATA_FILE = 'benchmark_metadata.json'
+
+class BenchmarkHelper(object):
+ """
+ A helper to capture benchmark data.
+ """
+ def __init__(self, benchmark_name):
+ super().__init__()
+ self.benchmark_name = benchmark_name
+ self.output_path = f'{FOLDER_PATH}/{benchmark_name}'
+ self.done = False
+ self.capturedData = False
+ self.max_frames_to_wait = 200
+
+ def capture_benchmark_metadata(self):
+ """
+ Capture benchmark metadata and block further execution until it has been written to the disk.
+ """
+ self.handler = azlmbr.atom.ProfilingCaptureNotificationBusHandler()
+ self.handler.connect()
+ self.handler.add_callback('OnCaptureBenchmarkMetadataFinished', self.on_data_captured)
+
+ self.done = False
+ self.capturedData = False
+ success = azlmbr.atom.ProfilingCaptureRequestBus(
+ azlmbr.bus.Broadcast, "CaptureBenchmarkMetadata", self.benchmark_name, f'{self.output_path}/{METADATA_FILE}'
+ )
+ if success:
+ self.wait_until_data()
+ general.log('Benchmark metadata captured.')
+ else:
+ general.log('Failed to capture benchmark metadata.')
+ return self.capturedData
+
+ def capture_pass_timestamp(self, frame_number):
+ """
+ Capture pass timestamps and block further execution until it has been written to the disk.
+ """
+ self.handler = azlmbr.atom.ProfilingCaptureNotificationBusHandler()
+ self.handler.connect()
+ self.handler.add_callback('OnCaptureQueryTimestampFinished', self.on_data_captured)
+
+ self.done = False
+ self.capturedData = False
+ success = azlmbr.atom.ProfilingCaptureRequestBus(
+ azlmbr.bus.Broadcast, "CapturePassTimestamp", f'{self.output_path}/frame{frame_number}_timestamps.json')
+ if success:
+ self.wait_until_data()
+ general.log('Pass timestamps captured.')
+ else:
+ general.log('Failed to capture pass timestamps.')
+ return self.capturedData
+
+ def on_data_captured(self, parameters):
+ # the parameters come in as a tuple
+ if parameters[0]:
+ general.log('Captured data successfully.')
+ self.capturedData = True
+ else:
+ general.log('Failed to capture data.')
+ self.done = True
+ self.handler.disconnect()
+
+ def wait_until_data(self):
+ frames_waited = 0
+ while self.done == False:
+ general.idle_wait_frames(1)
+ if frames_waited > self.max_frames_to_wait:
+ general.log('Timed out while waiting for the data to be captured')
+ self.handler.disconnect()
+ break
+ else:
+ frames_waited = frames_waited + 1
+ general.log(f'(waited {frames_waited} frames)')
diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py
index dad92d0932..ede140c075 100644
--- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py
+++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py
@@ -14,6 +14,7 @@ import pytest
import ly_test_tools.environment.file_system as file_system
from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
+from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
@@ -83,3 +84,43 @@ class TestAllComponentsIndepthTests(object):
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
compare_screenshots(test_screenshot, golden_screenshot)
+
+@pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])
+@pytest.mark.parametrize("project", ["AutomatedTesting"])
+@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
+@pytest.mark.parametrize("level", ["AtomFeatureIntegrationBenchmark"])
+class TestPerformanceBenchmarkSuite(object):
+ def test_AtomFeatureIntegrationBenchmark(
+ self, request, editor, workspace, rhi, project, launcher_platform, level):
+ """
+ Please review the hydra script run by this test for more specific test info.
+ Tests the performance of the Simple level.
+ """
+ expected_lines = [
+ "Benchmark metadata captured.",
+ "Pass timestamps captured.",
+ "Capturing complete.",
+ "Captured data successfully."
+ ]
+
+ unexpected_lines = [
+ "Failed to capture data.",
+ "Failed to capture pass timestamps.",
+ "Failed to capture benchmark metadata."
+ ]
+
+ hydra.launch_and_validate_results(
+ request,
+ TEST_DIRECTORY,
+ editor,
+ "hydra_GPUTest_AtomFeatureIntegrationBenchmark.py",
+ timeout=EDITOR_TIMEOUT,
+ expected_lines=expected_lines,
+ unexpected_lines=unexpected_lines,
+ halt_on_unexpected=True,
+ cfg_args=[level],
+ null_renderer=False,
+ )
+
+ aggregator = BenchmarkDataAggregator(workspace, logger, 'periodic')
+ aggregator.upload_metrics(rhi)
diff --git a/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/AtomFeatureIntegrationBenchmark.ly b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/AtomFeatureIntegrationBenchmark.ly
new file mode 100644
index 0000000000..4fc96f242b
--- /dev/null
+++ b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/AtomFeatureIntegrationBenchmark.ly
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5232563c3ff322669808ac4daeda3d822e4ef8c9c87db0fa245f0f9c9c34aada
+size 23379
diff --git a/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/filelist.xml b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/filelist.xml
new file mode 100644
index 0000000000..15b79f5e4f
--- /dev/null
+++ b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/filelist.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/level.pak b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/level.pak
new file mode 100644
index 0000000000..fecbc0b394
--- /dev/null
+++ b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/level.pak
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e075be2cb7cf5aa98e3503c1119b94c3098b35500c98c4db32d025c9e1afa52d
+size 5450
diff --git a/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/tags.txt b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/tags.txt
new file mode 100644
index 0000000000..a5e0705349
--- /dev/null
+++ b/AutomatedTesting/Levels/AtomFeatureIntegrationBenchmark/tags.txt
@@ -0,0 +1,12 @@
+495.045,510.96,35.8437,-0.166,0,-1.82124
+4.79827,4.71364,64.7838,-1.41886,0,2.48964
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt
index fe694f165a..fca16a2093 100644
--- a/Code/Editor/CMakeLists.txt
+++ b/Code/Editor/CMakeLists.txt
@@ -163,6 +163,8 @@ ly_add_target(
editor_files.cmake
PLATFORM_INCLUDE_FILES
Platform/${PAL_PLATFORM_NAME}/editor_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
+ TARGET_PROPERTIES
+ LY_INSTALL_GENERATE_RUN_TARGET TRUE
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
diff --git a/Code/Editor/Util/AffineParts.h b/Code/Editor/Util/AffineParts.h
index cb2c609ecd..a2e3d2c79b 100644
--- a/Code/Editor/Util/AffineParts.h
+++ b/Code/Editor/Util/AffineParts.h
@@ -20,11 +20,11 @@ struct AffineParts
Vec3 scale; //!< Stretch factors.
float fDet; //!< Sign of determinant.
- /** Decompose matrix to its affnie parts.
+ /** Decompose matrix to its affine parts.
*/
void Decompose(const Matrix34& mat);
- /** Decompose matrix to its affnie parts.
+ /** Decompose matrix to its affine parts.
Assume there`s no stretch rotation.
*/
void SpectralDecompose(const Matrix34& mat);
diff --git a/Code/Framework/AzAutoGen/AzAutoGen.props b/Code/Framework/AzAutoGen/AzAutoGen.props
deleted file mode 100644
index 2f809d03a6..0000000000
--- a/Code/Framework/AzAutoGen/AzAutoGen.props
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- True
-
-
diff --git a/Code/Framework/AzCore/AzCore/EBus/Internal/CallstackEntry.h b/Code/Framework/AzCore/AzCore/EBus/Internal/CallstackEntry.h
index 982040ee2f..bcda78aef8 100644
--- a/Code/Framework/AzCore/AzCore/EBus/Internal/CallstackEntry.h
+++ b/Code/Framework/AzCore/AzCore/EBus/Internal/CallstackEntry.h
@@ -84,7 +84,7 @@ namespace AZ
else
{
AZ::Debug::Trace::Instance().Assert(__FILE__, __LINE__, AZ_FUNCTION_SIGNATURE,
- "Bus has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads");
+ "Bus %s has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads", BusType::GetName());
}
}
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 74038c8aed..6b89b8c044 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -57,6 +57,7 @@ namespace AZ::Internal
// and avoid all this logic.
using namespace AZ::SettingsRegistryMergeUtils;
+ using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
AZ::IO::FixedMaxPath engineRoot;
if (auto engineManifestPath = AZ::Utils::GetEngineManifestPath(); !engineManifestPath.empty())
@@ -72,45 +73,16 @@ namespace AZ::Internal
struct EngineInfo
{
AZ::IO::FixedMaxPath m_path;
- AZ::SettingsRegistryInterface::FixedValueString m_moniker;
+ FixedValueString m_moniker;
};
struct EnginePathsVisitor : public AZ::SettingsRegistryInterface::Visitor
{
void Visit(
- [[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName,
+ [[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
{
- m_enginePaths.emplace_back(EngineInfo{AZ::IO::FixedMaxPath{value}.LexicallyNormal(), {}});
- }
-
- AZ::SettingsRegistryInterface::VisitResponse Traverse(
- [[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
- AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type) override
- {
- auto response = AZ::SettingsRegistryInterface::VisitResponse::Continue;
- if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
- {
- if (type == AZ::SettingsRegistryInterface::Type::Array)
- {
- if (valueName.compare("engines") != 0)
- {
- response = AZ::SettingsRegistryInterface::VisitResponse::Skip;
- }
- }
- }
- else if (action == AZ::SettingsRegistryInterface::VisitAction::Value)
- {
- if (type == AZ::SettingsRegistryInterface::Type::String)
- {
- if (valueName.compare("path") != 0)
- {
- response = AZ::SettingsRegistryInterface::VisitResponse::Skip;
- }
- }
- }
-
- return response;
+ m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} });
}
AZStd::vector m_enginePaths{};
@@ -119,11 +91,11 @@ namespace AZ::Internal
EnginePathsVisitor pathVisitor;
if (manifestLoaded)
{
- auto enginePathsKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engines", EngineManifestRootKey);
+ auto enginePathsKey = FixedValueString::format("%s/engines_path", EngineManifestRootKey);
settingsRegistry.Visit(pathVisitor, enginePathsKey);
}
- const auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
+ const auto engineMonikerKey = FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
AZStd::set projectPathsNotFound;
@@ -135,7 +107,15 @@ namespace AZ::Internal
if (settingsRegistry.MergeSettingsFile(
engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineSettingsRootKey))
{
- settingsRegistry.Get(engineInfo.m_moniker, engineMonikerKey);
+ FixedValueString engineName;
+ settingsRegistry.Get(engineName, engineMonikerKey);
+ AZ_Warning("SettingsRegistryMergeUtils",engineInfo.m_moniker == engineName,
+ R"(The engine name key "%s" mapped to engine path "%s" within the global manifest of "%s")"
+ R"( does not match the "engine_name" field "%s" in the engine.json)" "\n"
+ "This engine should be re-registered.",
+ engineInfo.m_moniker.c_str(), engineInfo.m_path.c_str(), engineManifestPath.c_str(),
+ engineName.c_str())
+ engineInfo.m_moniker = engineName;
}
}
diff --git a/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/rapidjson.natvis b/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/rapidjson.natvis
new file mode 100644
index 0000000000..5167714f20
--- /dev/null
+++ b/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/rapidjson.natvis
@@ -0,0 +1,38 @@
+
+
+
+
+ null
+ true
+ false
+ {data_.ss.str}
+ {(const char*)((size_t)data_.s.str & 0x0000FFFFFFFFFFFF)}
+ {data_.n.i.i}
+ {data_.n.u.u}
+ {data_.n.i64}
+ {data_.n.u64}
+ {data_.n.d}
+ Object members={data_.o.size}
+ Array members={data_.a.size}
+
+ - data_.o.size
+ - data_.o.capacity
+
+ data_.o.size
+
+ (rapidjson_ly::GenericMember<$T1,$T2>*)(((size_t)data_.o.members) & 0x0000FFFFFFFFFFFF)
+
+
+ - data_.a.size
+ - data_.a.capacity
+
+ data_.a.size
+
+ (rapidjson_ly::GenericValue<$T1,$T2>*)(((size_t)data_.a.elements) & 0x0000FFFFFFFFFFFF)
+
+
+
+
+
+
+
diff --git a/Code/Framework/AzCore/Platform/Windows/platform_windows_files.cmake b/Code/Framework/AzCore/Platform/Windows/platform_windows_files.cmake
index bcd08a6e56..6386377fcb 100644
--- a/Code/Framework/AzCore/Platform/Windows/platform_windows_files.cmake
+++ b/Code/Framework/AzCore/Platform/Windows/platform_windows_files.cmake
@@ -30,6 +30,7 @@ set(FILES
../Common/VisualStudio/AzCore/Natvis/azcore.natvis
../Common/VisualStudio/AzCore/Natvis/azcore.natstepfilter
../Common/VisualStudio/AzCore/Natvis/azcore.natjmc
+ ../Common/VisualStudio/AzCore/Natvis/rapidjson.natvis
AzCore/Debug/StackTracer_Windows.cpp
../Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp
../Common/WinAPI/AzCore/IO/Streamer/StreamerContext_WinAPI.cpp
diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp
index a61f6a4845..cfea76f1d7 100644
--- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp
@@ -123,6 +123,12 @@ namespace AzPhysics
->Field("Kinematic", &RigidBodyConfiguration::m_kinematic)
->Field("CCD Enabled", &RigidBodyConfiguration::m_ccdEnabled)
->Field("Compute Mass", &RigidBodyConfiguration::m_computeMass)
+ ->Field("Lock Linear X", &RigidBodyConfiguration::m_lockLinearX)
+ ->Field("Lock Linear Y", &RigidBodyConfiguration::m_lockLinearY)
+ ->Field("Lock Linear Z", &RigidBodyConfiguration::m_lockLinearZ)
+ ->Field("Lock Angular X", &RigidBodyConfiguration::m_lockAngularX)
+ ->Field("Lock Angular Y", &RigidBodyConfiguration::m_lockAngularY)
+ ->Field("Lock Angular Z", &RigidBodyConfiguration::m_lockAngularZ)
->Field("Mass", &RigidBodyConfiguration::m_mass)
->Field("Compute COM", &RigidBodyConfiguration::m_computeCenterOfMass)
->Field("Centre of mass offset", &RigidBodyConfiguration::m_centerOfMassOffset)
diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h
index 59829e740c..51dcce842d 100644
--- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h
+++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h
@@ -62,6 +62,16 @@ namespace AzPhysics
bool m_computeInertiaTensor = true;
bool m_computeMass = true;
+ // Flags to restrict motion along specific world-space axes.
+ bool m_lockLinearX = false;
+ bool m_lockLinearY = false;
+ bool m_lockLinearZ = false;
+
+ // Flags to restrict rotation around specific world-space axes.
+ bool m_lockAngularX = false;
+ bool m_lockAngularY = false;
+ bool m_lockAngularZ = false;
+
//! If set, non-simulated shapes will also be included in the mass properties calculation.
bool m_includeAllShapesInMassCalculation = false;
diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp
index cc0851e53d..3a3ab06d02 100644
--- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp
@@ -22,6 +22,7 @@ namespace AzNetworking
const AZ::TimeMs deltaTimeMs = currentTimeMs - m_lastLoggedTimeMs;
m_atoms[m_activeAtom].m_bytesTransmitted += byteCount;
+ m_atoms[m_activeAtom].m_packetsSent++;
m_atoms[m_activeAtom].m_timeAccumulatorMs += deltaTimeMs;
if (m_atoms[m_activeAtom].m_timeAccumulatorMs >= m_maxSampleTimeMs)
@@ -32,6 +33,11 @@ namespace AzNetworking
m_lastLoggedTimeMs = currentTimeMs;
}
+ void DatarateMetrics::LogPacketLost()
+ {
+ m_atoms[m_activeAtom].m_packetsLost++;
+ }
+
float DatarateMetrics::GetBytesPerSecond() const
{
const uint32_t sampleAtom = 1 - m_activeAtom;
@@ -47,6 +53,18 @@ namespace AzNetworking
return (bytesLogged * 1000.0f) / sampleTime; // (* 1000) to convert from bytes per millisecond to bytes per second
}
+ float DatarateMetrics::GetLossRatePercent() const
+ {
+ const uint32_t sampleAtom = 1 - m_activeAtom;
+
+ if (m_atoms[sampleAtom].m_packetsSent == 0)
+ {
+ return 0.0f;
+ }
+
+ return float(m_atoms[sampleAtom].m_packetsLost) / float(m_atoms[sampleAtom].m_packetsSent);
+ }
+
void ConnectionComputeRtt::LogPacketSent(PacketId packetId, AZ::TimeMs currentTimeMs)
{
for (uint32_t i = 0; i < MaxTrackableEntries; i++)
diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h
index c2227b07b6..b576c64e86 100644
--- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h
+++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h
@@ -19,8 +19,10 @@ namespace AzNetworking
{
DatarateAtom() = default;
+ AZ::TimeMs m_timeAccumulatorMs = AZ::TimeMs{ 0 };
uint32_t m_bytesTransmitted = 0;
- AZ::TimeMs m_timeAccumulatorMs = AZ::TimeMs{0};
+ uint32_t m_packetsSent = 0;
+ uint32_t m_packetsLost = 0;
};
//! @class DatarateMetrics
@@ -40,19 +42,26 @@ namespace AzNetworking
//! @param currentTimeMs current process time in milliseconds
void LogPacket(uint32_t byteCount, AZ::TimeMs currentTimeMs);
+ //! Invoked whenever a packet has determined to be lost.
+ void LogPacketLost();
+
//! Retrieve a sample of the datarate being incurred by this connection in bytes per second.
//! @return datarate for traffic sent to or from the connection in bytes per second
float GetBytesPerSecond() const;
+ //! Returns the estimated packet loss rate as a percentage of packets.
+ //! @return the estimated percentage loss rate
+ float GetLossRatePercent() const;
+
private:
//! Used internally to swap buffers used for metric gathering.
void SwapBuffers();
- static constexpr AZ::TimeMs MaxSampleTimeMs = AZ::TimeMs{500};
+ static constexpr AZ::TimeMs MaxSampleTimeMs = AZ::TimeMs{ 2000 };
- AZ::TimeMs m_maxSampleTimeMs = MaxSampleTimeMs;
- AZ::TimeMs m_lastLoggedTimeMs = MaxSampleTimeMs;
+ AZ::TimeMs m_maxSampleTimeMs = MaxSampleTimeMs;
+ AZ::TimeMs m_lastLoggedTimeMs = MaxSampleTimeMs;
uint32_t m_activeAtom = 0;
DatarateAtom m_atoms[2];
};
@@ -69,7 +78,7 @@ namespace AzNetworking
ConnectionPacketEntry(PacketId packetId, AZ::TimeMs sendTimeMs);
PacketId m_packetId = InvalidPacketId;
- AZ::TimeMs m_sendTimeMs = AZ::TimeMs{0};
+ AZ::TimeMs m_sendTimeMs = AZ::TimeMs{0};
};
//! @class ConnectionComputeRtt
@@ -100,8 +109,8 @@ namespace AzNetworking
private:
- static constexpr uint32_t MaxTrackableEntries = 4;
- static constexpr float InitialRoundTripTime = 0.1f; //< Start off with a 100 millisecond estimate for Rtt
+ static constexpr uint32_t MaxTrackableEntries = 8;
+ static constexpr float InitialRoundTripTime = 0.1f; //< Start off with a 100 millisecond estimate for Rtt
float m_roundTripTime = InitialRoundTripTime;
ConnectionPacketEntry m_entries[MaxTrackableEntries];
@@ -117,6 +126,11 @@ namespace AzNetworking
//! Resets all internal metrics to defaults.
void Reset();
+ void LogPacketSent(uint32_t byteCount, AZ::TimeMs currentTimeMs);
+ void LogPacketRecv(uint32_t byteCount, AZ::TimeMs currentTimeMs);
+ void LogPacketLost();
+ void LogPacketAcked();
+
uint32_t m_packetsSent = 0;
uint32_t m_packetsRecv = 0;
uint32_t m_packetsLost = 0;
diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl
index 5d7d18f709..5f196c4ed1 100644
--- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl
+++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl
@@ -40,4 +40,33 @@ namespace AzNetworking
{
*this = ConnectionMetrics();
}
+
+ inline void ConnectionMetrics::LogPacketSent(uint32_t byteCount, AZ::TimeMs currentTimeMs)
+ {
+ if (byteCount > 0)
+ {
+ m_packetsSent++;
+ }
+ m_sendDatarate.LogPacket(byteCount, currentTimeMs);
+ }
+
+ inline void ConnectionMetrics::LogPacketRecv(uint32_t byteCount, AZ::TimeMs currentTimeMs)
+ {
+ if (byteCount > 0)
+ {
+ m_packetsRecv++;
+ }
+ m_recvDatarate.LogPacket(byteCount, currentTimeMs);
+ }
+
+ inline void ConnectionMetrics::LogPacketLost()
+ {
+ m_packetsLost++;
+ m_sendDatarate.LogPacketLost();
+ }
+
+ inline void ConnectionMetrics::LogPacketAcked()
+ {
+ m_packetsAcked++;
+ }
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h
index 1034f17585..363fd1d37b 100644
--- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h
+++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h
@@ -95,11 +95,6 @@ namespace AzNetworking
//! @return the max transmission unit for this connection
virtual uint32_t GetConnectionMtu() const = 0;
- //! Sets connection quality values for testing poor connection conditions.
- //! Currently unsupported on TcpConnections
- //! @param connectionQuality simulated connection quality values to use
- virtual void SetConnectionQuality(const ConnectionQuality& connectionQuality) = 0;
-
//! Returns the connection identifier for this connection instance.
//! @return the connection identifier for this connection instance
ConnectionId GetConnectionId() const;
@@ -128,12 +123,23 @@ namespace AzNetworking
//! @return reference to the connection metric info
ConnectionMetrics& GetMetrics();
+ //! Retrieves debug connection quality settings.
+ //! Currently unsupported on TcpConnections
+ //! @return connection quality structure for this connection
+ const ConnectionQuality& GetConnectionQuality() const;
+
+ //! Retrieves debug connection quality settings, non-const.
+ //! Currently unsupported on TcpConnections
+ //! @return connection quality structure for this connection
+ ConnectionQuality& GetConnectionQuality();
+
private:
// The following data members are here in the interface for performance reasons
ConnectionId m_connectionId = InvalidConnectionId;
IpAddress m_remoteAddress;
ConnectionMetrics m_connectionMetrics;
+ ConnectionQuality m_connectionQuality;
void* m_userData = nullptr;
};
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl
index 646afac8f0..61e92010c7 100644
--- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl
+++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl
@@ -59,4 +59,14 @@ namespace AzNetworking
{
return m_connectionMetrics;
}
+
+ inline const ConnectionQuality& IConnection::GetConnectionQuality() const
+ {
+ return m_connectionQuality;
+ }
+
+ inline ConnectionQuality& IConnection::GetConnectionQuality()
+ {
+ return m_connectionQuality;
+ }
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp
index ea746b9d00..1beb83e19f 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp
@@ -122,7 +122,7 @@ namespace AzNetworking
bool TcpConnection::UpdateRecv()
{
const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
- GetMetrics().m_recvDatarate.LogPacket(0, startTimeMs);
+ GetMetrics().LogPacketRecv(0, startTimeMs);
// Read new data off the input socket
{
@@ -261,11 +261,6 @@ namespace AzNetworking
return 0; // do nothing, unsupported on TCP connections
}
- void TcpConnection::SetConnectionQuality([[maybe_unused]] const ConnectionQuality& connectionQuality)
- {
- ; // do nothing, unsupported on TCP connections
- }
-
bool TcpConnection::SendPacketInternal(PacketType packetType, TcpPacketEncodingBuffer& payloadBuffer, AZ::TimeMs currentTimeMs)
{
AZ_Assert(payloadBuffer.GetCapacity() < AZStd::numeric_limits::max(), "Buffer capacity should be representable using 2 bytes or less");
@@ -333,8 +328,7 @@ namespace AzNetworking
}
m_sendRingbuffer.AdvanceWriteBuffer(headerSize + payloadSize);
- GetMetrics().m_packetsSent++;
- GetMetrics().m_sendDatarate.LogPacket(headerSize + payloadSize, currentTimeMs);
+ GetMetrics().LogPacketSent(headerSize + payloadSize, currentTimeMs);
m_networkInterface.GetMetrics().m_sendPackets++;
UpdateSend();
return true;
@@ -379,8 +373,7 @@ namespace AzNetworking
memcpy(dstData, srcData, packetSize);
m_recvRingbuffer.AdvanceReadBuffer(serializer.GetReadSize() + packetSize);
- GetMetrics().m_packetsRecv++;
- GetMetrics().m_recvDatarate.LogPacket(packetSize, currentTimeMs);
+ GetMetrics().LogPacketRecv(packetSize, currentTimeMs);
m_networkInterface.GetMetrics().m_recvPackets++;
return true;
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h
index af2c69292c..b769aea086 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h
@@ -102,7 +102,6 @@ namespace AzNetworking
bool Disconnect(DisconnectReason reason, TerminationEndpoint endpoint) override;
void SetConnectionMtu(uint32_t connectionMtu) override;
uint32_t GetConnectionMtu() const override;
- void SetConnectionQuality(const ConnectionQuality& connectionQuality) override;
// @}
//! Sets the registered socket file descriptor for this TcpConnection in the associated ConnectionSet instance.
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp
index 798116d180..3efc6a51a8 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp
@@ -152,7 +152,7 @@ namespace AzNetworking
void UdpConnection::ProcessAcked(PacketId packetId, AZ::TimeMs currentTimeMs)
{
- GetMetrics().m_packetsAcked++;
+ GetMetrics().LogPacketAcked();
m_reliableQueue.OnPacketAcked(m_networkInterface, *this, packetId);
// Compute Rtt adjustments
@@ -172,8 +172,7 @@ namespace AzNetworking
GetMetrics().m_connectionRtt.LogPacketSent(packetId, currentTimeMs);
}
- GetMetrics().m_packetsSent++;
- GetMetrics().m_sendDatarate.LogPacket(packetSize, currentTimeMs);
+ GetMetrics().LogPacketSent(packetSize, currentTimeMs);
m_lastSentPacketMs = currentTimeMs;
m_unackedPacketCount = 0;
}
@@ -193,7 +192,7 @@ namespace AzNetworking
return PacketTimeoutResult::Acked;
case PacketAckState::Nacked:
- GetMetrics().m_packetsLost++;
+ GetMetrics().LogPacketLost();
if (reliability == ReliabilityType::Reliable)
{
m_reliableQueue.OnPacketLost(m_networkInterface, *this, packetId);
@@ -224,8 +223,7 @@ namespace AzNetworking
return false;
}
- GetMetrics().m_packetsRecv++;
- GetMetrics().m_recvDatarate.LogPacket(packetSize, currentTimeMs);
+ GetMetrics().LogPacketRecv(packetSize, currentTimeMs);
if (header.GetIsReliable() && !m_reliableQueue.OnPacketReceived(header))
{
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h
index c8e0c2cdc9..67d626d6cb 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h
@@ -66,13 +66,8 @@ namespace AzNetworking
bool Disconnect(DisconnectReason reason, TerminationEndpoint endpoint) override;
void SetConnectionMtu(uint32_t connectionMtu) override;
uint32_t GetConnectionMtu() const override;
- void SetConnectionQuality(const ConnectionQuality& connectionQuality) override;
// @}
- //! Gets connection quality values for testing poor connection conditions.
- //! @return connection quality values for this IConnection instance
- const ConnectionQuality& GetConnectionQuality() const;
-
//! Returns a suitable encryption endpoint for this connection type.
//! @return reference to the connections encryption endpoint
DtlsEndpoint& GetDtlsEndpoint();
@@ -146,8 +141,6 @@ namespace AzNetworking
UdpFragmentQueue m_fragmentQueue;
ConnectionState m_state = ConnectionState::Disconnected;
ConnectionRole m_connectionRole = ConnectionRole::Connector;
-
- ConnectionQuality m_connectionQuality;
DtlsEndpoint m_dtlsEndpoint;
AZ::TimeMs m_lastSentPacketMs;
@@ -160,4 +153,3 @@ namespace AzNetworking
}
#include
-
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl
index 1ab273d53d..b31595263d 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl
@@ -10,16 +10,6 @@
namespace AzNetworking
{
- inline void UdpConnection::SetConnectionQuality(const ConnectionQuality& connectionQuality)
- {
- m_connectionQuality = connectionQuality;
- }
-
- inline const ConnectionQuality& UdpConnection::GetConnectionQuality() const
- {
- return m_connectionQuality;
- }
-
inline DtlsEndpoint& UdpConnection::GetDtlsEndpoint()
{
return m_dtlsEndpoint;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
index 1a29dd4cae..a3ddb856d2 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
@@ -224,8 +224,7 @@ namespace AzNetworking
continue;
}
- connection->GetMetrics().m_recvDatarate.LogPacket(packet.m_receivedBytes + UdpPacketHeaderSize, currentTimeMs);
- connection->GetMetrics().m_packetsRecv++;
+ connection->GetMetrics().LogPacketRecv(packet.m_receivedBytes + UdpPacketHeaderSize, currentTimeMs);
// Decode the packet flag bitset first since it's always uncompressed
UdpPacketHeader header;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp
index bb0b6d0fff..29a99f96a1 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp
@@ -126,7 +126,7 @@ namespace AzNetworking
#ifdef ENABLE_LATENCY_DEBUG
if (connectionQuality.m_lossPercentage > 0)
{
- if (int32_t(m_random.GetRandom() % 100) < (connectionQuality.m_lossPercentage / 2))
+ if (int32_t(m_random.GetRandom() % 100) < (connectionQuality.m_lossPercentage))
{
// Pretend we sent, but don't actually send
return true;
@@ -157,9 +157,11 @@ namespace AzNetworking
#ifdef ENABLE_LATENCY_DEBUG
else if ((connectionQuality.m_latencyMs > AZ::TimeMs{ 0 }) || (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 }))
{
- const AZ::TimeMs jitterMs = aznumeric_cast(m_random.GetRandom()) % (connectionQuality.m_varianceMs / aznumeric_cast(2));
+ const AZ::TimeMs jitterMs = aznumeric_cast(m_random.GetRandom()) % (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 }
+ ? connectionQuality.m_varianceMs
+ : AZ::TimeMs{ 1 });
const AZ::TimeMs currTimeMs = AZ::GetElapsedTimeMs();
- const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs / aznumeric_cast(2)) + jitterMs;
+ const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs) + jitterMs;
DeferredData deferred = DeferredData(address, data, size, encrypt, dtlsEndpoint);
AZ::Interface::Get()->AddCallback([&, deferredData = deferred]
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli
index 97862b6f6e..2fea4650b3 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli
@@ -62,6 +62,7 @@ class ProjectedShadow
float3 m_lightDirection;
float3 m_normalVector;
float3 m_shadowPosition;
+ float m_bias;
};
float ProjectedShadow::GetVisibility(
@@ -238,7 +239,7 @@ float ProjectedShadow::GetVisibilityEsm()
}
const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy);
const float depth = PerspectiveDepthToLinear(
- m_shadowPosition.z,
+ m_shadowPosition.z - m_bias,
coefficients);
const float occluder = shadowmap.SampleLevel(
PassSrg::LinearSampler,
@@ -280,7 +281,7 @@ float ProjectedShadow::GetVisibilityEsmPcf()
}
const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy);
const float depth = PerspectiveDepthToLinear(
- m_shadowPosition.z,
+ m_shadowPosition.z - m_bias,
coefficients);
const float occluder = shadowmap.SampleLevel(
PassSrg::LinearSampler,
@@ -346,7 +347,7 @@ float ProjectedShadow::SamplePcfBicubic()
param.shadowPos = float3(atlasPosition.xy * ViewSrg::m_invShadowmapAtlasSize, atlasPosition.z);
param.shadowMapSize = ViewSrg::m_shadowmapAtlasSize;
param.invShadowMapSize = ViewSrg::m_invShadowmapAtlasSize;
- param.comparisonValue = m_shadowPosition.z - ViewSrg::m_projectedShadows[m_shadowIndex].m_bias;
+ param.comparisonValue = m_shadowPosition.z - m_bias;
param.samplerState = SceneSrg::m_hwPcfSampler;
if (filteringSampleCount <= 4)
@@ -384,8 +385,8 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition)
PassSrg::LinearSampler,
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z), /*LOD=*/0).r;
const float depthDiff = depthInShadowmap - shadowPosition.z;
- float bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias;
- if (depthDiff < -bias)
+
+ if (depthDiff < -m_bias)
{
return true;
}
@@ -428,6 +429,8 @@ void ProjectedShadow::SetShadowPosition()
const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix;
float4 shadowPositionHomogeneous = mul(depthBiasMatrix, float4(m_worldPosition, 1));
m_shadowPosition = shadowPositionHomogeneous.xyz / shadowPositionHomogeneous.w;
+
+ m_bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias / shadowPositionHomogeneous.w;
}
float3 ProjectedShadow::GetAtlasPosition(float2 texturePosition)
diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h
index 9248add6f5..e4986eee93 100644
--- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h
+++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h
@@ -84,6 +84,8 @@ namespace AZ
//! Sets if shadows are enabled
virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0;
+ //! Sets the shadow bias
+ virtual void SetShadowBias(LightHandle handle, float bias) = 0;
//! Sets the shadowmap size (width and height) of the light.
virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
//! Specifies filter method of shadows.
diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h
index 8ad4fb89de..3383378dc7 100644
--- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h
+++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h
@@ -66,6 +66,8 @@ namespace AZ
virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0;
//! Sets the shadowmap size (width and height) of the light.
virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
+ //! Sets the shadow bias
+ virtual void SetShadowBias(LightHandle handle, float bias) = 0;
//! Specifies filter method of shadows.
virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0;
//! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on
diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h
index c0cfff3dd5..6cbb0cfef1 100644
--- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h
+++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h
@@ -50,6 +50,8 @@ namespace AZ::Render
virtual void SetFieldOfViewY(ShadowId id, float fieldOfView) = 0;
//! Sets the maximum resolution of the shadow map
virtual void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) = 0;
+ //! Sets the shadow bias
+ virtual void SetShadowBias(ShadowId id, float bias) = 0;
//! Sets the shadowmap Pcf method.
virtual void SetPcfMethod(ShadowId id, PcfMethod method) = 0;
//! Sets the shadow filter method
diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp
index 54410544a7..55be9d232e 100644
--- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp
@@ -308,6 +308,11 @@ namespace AZ
AZStd::invoke(AZStd::forward(functor), m_shadowFeatureProcessor, shadowId, AZStd::forward(param));
}
}
+
+ void DiskLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias)
+ {
+ SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias);
+ }
void DiskLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
{
diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h
index 0ff8efb1b1..2e97ae1ded 100644
--- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h
+++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h
@@ -50,6 +50,7 @@ namespace AZ
void SetConstrainToConeLight(LightHandle handle, bool useCone) override;
void SetConeAngles(LightHandle handle, float innerDegrees, float outerDegrees) override;
void SetShadowsEnabled(LightHandle handle, bool enabled) override;
+ void SetShadowBias(LightHandle handle, float bias) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp
index 2b54b6ede1..9baa2ae1c2 100644
--- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp
@@ -277,6 +277,11 @@ namespace AZ
}
}
}
+
+ void PointLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias)
+ {
+ SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias);
+ }
void PointLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
{
diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h
index d7bb25c71c..b7b644da9e 100644
--- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h
+++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h
@@ -47,6 +47,7 @@ namespace AZ
void SetAttenuationRadius(LightHandle handle, float attenuationRadius) override;
void SetBulbRadius(LightHandle handle, float bulbRadius) override;
void SetShadowsEnabled(LightHandle handle, bool enabled) override;
+ void SetShadowBias(LightHandle handle, float bias) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp
index 10dcffcf74..03ee176fd0 100644
--- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp
@@ -143,7 +143,15 @@ namespace AZ::Render
shadowProperty.m_desc.m_fieldOfViewYRadians = fieldOfViewYRadians;
UpdateShadowView(shadowProperty);
}
-
+
+ void ProjectedShadowFeatureProcessor::SetShadowBias(ShadowId id, float bias)
+ {
+ AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBias().");
+
+ ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
+ shadowProperty.m_bias = bias;
+ }
+
void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size)
{
AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution().");
@@ -265,23 +273,20 @@ namespace AZ::Render
view->SetCameraTransform(Matrix3x4::CreateFromTransform(desc.m_transform));
ShadowData& shadowData = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex());
- shadowData.m_bias = (nearDist / farDist) * 0.1f;
+
+ // Adjust the manually set bias to a more appropriate range for the shader. Scale the bias by the
+ // near plane so that the bias appears consistent as other light properties change.
+ shadowData.m_bias = nearDist * shadowProperty.m_bias * 0.01f;
FilterParameter& esmData = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex());
- if (FilterMethodIsEsm(shadowData))
- {
- // Set parameters to calculate linear depth if ESM is used.
- m_filterParameterNeedsUpdate = true;
- esmData.m_isEnabled = true;
- esmData.m_n_f_n = nearDist / (farDist - nearDist);
- esmData.m_n_f = nearDist - farDist;
- esmData.m_f = farDist;
- }
- else
- {
- // Reset enabling flag if ESM is not used.
- esmData.m_isEnabled = false;
- }
+
+ // Set parameters to calculate linear depth if ESM is used.
+ esmData.m_n_f_n = nearDist / (farDist - nearDist);
+ esmData.m_n_f = nearDist - farDist;
+ esmData.m_f = farDist;
+
+ esmData.m_isEnabled = FilterMethodIsEsm(shadowData);
+ m_filterParameterNeedsUpdate = m_filterParameterNeedsUpdate || esmData.m_isEnabled;
for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses)
{
diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h
index 4da4e2ff1e..3dbf88addb 100644
--- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h
+++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h
@@ -47,6 +47,7 @@ namespace AZ::Render
void SetAspectRatio(ShadowId id, float aspectRatio) override;
void SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians) override;
void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override;
+ void SetShadowBias(ShadowId id, float bias) override;
void SetPcfMethod(ShadowId id, PcfMethod method);
void SetEsmExponent(ShadowId id, float exponent);
void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override;
@@ -79,6 +80,7 @@ namespace AZ::Render
{
ProjectedShadowDescriptor m_desc;
RPI::ViewPtr m_shadowmapView;
+ float m_bias = 0.1f;
ShadowId m_shadowId;
};
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h
new file mode 100644
index 0000000000..bceea24aad
--- /dev/null
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace AtomToolsFramework
+{
+ //! Base class for Atom tools to inherit from
+ class AtomToolsApplication
+ : public AzFramework::Application
+ , public AzQtComponents::AzQtApplication
+ , protected AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler
+ , protected AzFramework::AssetSystemStatusBus::Handler
+ , protected AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
+ , protected AZ::UserSettingsOwnerRequestBus::Handler
+ {
+ public:
+ AZ_TYPE_INFO(AtomTools::AtomToolsApplication, "{A0DF25BA-6F74-4F11-9F85-0F99278D5986}");
+
+ using Base = AzFramework::Application;
+
+ AtomToolsApplication(int* argc, char*** argv);
+
+ //////////////////////////////////////////////////////////////////////////
+ // AzFramework::Application
+ void CreateReflectionManager() override;
+ void Reflect(AZ::ReflectContext* context) override;
+ void RegisterCoreComponents() override;
+ AZ::ComponentTypeList GetRequiredSystemComponents() const override;
+ void CreateStaticModules(AZStd::vector& outModules) override;
+ const char* GetCurrentConfigurationName() const override;
+ void StartCommon(AZ::Entity* systemEntity) override;
+ void Tick(float deltaOverride = -1.f) override;
+ void Stop() override;
+
+ protected:
+ //////////////////////////////////////////////////////////////////////////
+ // AssetDatabaseRequestsBus::Handler overrides...
+ bool GetAssetDatabaseLocation(AZStd::string& result) override;
+ //////////////////////////////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////////////////////////////
+ // AzFramework::Application overrides...
+ void Destroy() override;
+ //////////////////////////////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////////////////////////////
+ // AzFramework::AssetSystemStatusBus::Handler overrides...
+ void AssetSystemAvailable() override;
+ //////////////////////////////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////////////////////////////
+ // AZ::ComponentApplication overrides...
+ void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
+ //////////////////////////////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////////////////////////////
+ // AZ::UserSettingsOwnerRequestBus::Handler overrides...
+ void SaveSettings() override;
+ //////////////////////////////////////////////////////////////////////////
+
+ ////////////////////////////////////////////////////////////////////////
+ // EditorPythonConsoleNotificationBus::Handler overrides...
+ void OnTraceMessage(AZStd::string_view message) override;
+ void OnErrorMessage(AZStd::string_view message) override;
+ void OnExceptionMessage(AZStd::string_view message) override;
+ ////////////////////////////////////////////////////////////////////////
+
+ virtual AZStd::string GetBuildTargetName() const;
+ virtual AZStd::vector GetCriticalAssetFilters() const;
+
+ virtual void LoadSettings();
+ virtual void UnloadSettings();
+ virtual void CompileCriticalAssets();
+ virtual void ProcessCommandLine(const AZ::CommandLine& commandLine);
+ virtual bool LaunchDiscoveryService();
+ virtual void StartInternal();
+
+ static void PyIdleWaitFrames(uint32_t frames);
+
+ AzToolsFramework::TraceLogger m_traceLogger;
+
+ //! Local user settings are used to store material browser tree expansion state
+ AZ::UserSettingsProvider m_localUserSettings;
+
+ //! Are local settings loaded
+ bool m_activatedLocalUserSettings = false;
+
+ QTimer m_timer;
+
+ AtomToolsFramework::LocalSocket m_socket;
+ AtomToolsFramework::LocalServer m_server;
+ };
+} // namespace AtomToolsFramework
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp
new file mode 100644
index 0000000000..3a542db1a4
--- /dev/null
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
+#include
+#include
+AZ_POP_DISABLE_WARNING
+
+namespace AtomToolsFramework
+{
+ AZStd::string AtomToolsApplication::GetBuildTargetName() const
+ {
+ return AZStd::string("AtomTools");
+ }
+
+ const char* AtomToolsApplication::GetCurrentConfigurationName() const
+ {
+#if defined(_RELEASE)
+ return "ReleaseAtomTools";
+#elif defined(_DEBUG)
+ return "DebugAtomTools";
+#else
+ return "ProfileAtomTools";
+#endif
+ }
+
+ AtomToolsApplication::AtomToolsApplication(int* argc, char*** argv)
+ : Application(argc, argv)
+ , AzQtApplication(*argc, *argv)
+ {
+ connect(&m_timer, &QTimer::timeout, this, [&]()
+ {
+ this->PumpSystemEventLoopUntilEmpty();
+ this->Tick();
+ });
+ }
+
+ void AtomToolsApplication::CreateReflectionManager()
+ {
+ Base::CreateReflectionManager();
+ GetSerializeContext()->CreateEditContext();
+ }
+
+ void AtomToolsApplication::Reflect(AZ::ReflectContext* context)
+ {
+ Base::Reflect(context);
+
+ AzToolsFramework::AssetBrowser::AssetBrowserEntry::Reflect(context);
+ AzToolsFramework::AssetBrowser::RootAssetBrowserEntry::Reflect(context);
+ AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry::Reflect(context);
+ AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry::Reflect(context);
+ AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry::Reflect(context);
+
+ AzToolsFramework::QTreeViewWithStateSaving::Reflect(context);
+ AzToolsFramework::QWidgetSavedState::Reflect(context);
+
+ if (auto behaviorContext = azrtti_cast(context))
+ {
+ auto targetName = GetBuildTargetName();
+
+ // this will put these methods into the 'azlmbr.AtomTools.general' module
+ auto addGeneral = [targetName](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
+ {
+ methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
+ ->Attribute(AZ::Script::Attributes::Category, "Editor")
+ ->Attribute(AZ::Script::Attributes::Module, targetName);
+ };
+ // The reflection here is based on patterns in CryEditPythonHandler::Reflect
+ addGeneral(behaviorContext->Method(
+ "idle_wait_frames", &AtomToolsApplication::PyIdleWaitFrames, nullptr,
+ "Waits idling for a frames. Primarily used for auto-testing."));
+ }
+ }
+
+ void AtomToolsApplication::RegisterCoreComponents()
+ {
+ Base::RegisterCoreComponents();
+ RegisterComponentDescriptor(AzToolsFramework::AssetBrowser::AssetBrowserComponent::CreateDescriptor());
+ RegisterComponentDescriptor(AzToolsFramework::Thumbnailer::ThumbnailerComponent::CreateDescriptor());
+ RegisterComponentDescriptor(AzToolsFramework::Components::PropertyManagerComponent::CreateDescriptor());
+ RegisterComponentDescriptor(AzToolsFramework::AssetSystem::AssetSystemComponent::CreateDescriptor());
+ RegisterComponentDescriptor(AzToolsFramework::PerforceComponent::CreateDescriptor());
+ }
+
+ AZ::ComponentTypeList AtomToolsApplication::GetRequiredSystemComponents() const
+ {
+ AZ::ComponentTypeList components = Base::GetRequiredSystemComponents();
+
+ components.insert(
+ components.end(),
+ {
+ azrtti_typeid(),
+ azrtti_typeid(),
+ azrtti_typeid(),
+ azrtti_typeid(),
+ });
+
+ return components;
+ }
+
+ void AtomToolsApplication::CreateStaticModules(AZStd::vector& outModules)
+ {
+ Base::CreateStaticModules(outModules);
+ outModules.push_back(aznew AzToolsFramework::AzToolsFrameworkModule);
+ }
+
+ void AtomToolsApplication::StartCommon(AZ::Entity* systemEntity)
+ {
+ AzFramework::AssetSystemStatusBus::Handler::BusConnect();
+ AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect();
+
+ Base::StartCommon(systemEntity);
+
+ StartInternal();
+
+ m_timer.start();
+ }
+
+ void AtomToolsApplication::Destroy()
+ {
+ AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
+ AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
+
+ AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
+ Base::Destroy();
+ }
+
+ AZStd::vector AtomToolsApplication::GetCriticalAssetFilters() const
+ {
+ return AZStd::vector({});
+ }
+
+ void AtomToolsApplication::AssetSystemAvailable()
+ {
+ bool connectedToAssetProcessor = false;
+
+ // When the AssetProcessor is already launched it should take less than a second to perform a connection
+ // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
+ // and able to negotiate a connection when running a debug build
+ // and to negotiate a connection
+
+ auto targetName = GetBuildTargetName();
+
+ AzFramework::AssetSystem::ConnectionSettings connectionSettings;
+ AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
+ connectionSettings.m_connectionDirection =
+ AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
+ connectionSettings.m_connectionIdentifier = GetBuildTargetName();
+ connectionSettings.m_loggingCallback = [targetName]([[maybe_unused]] AZStd::string_view logData)
+ {
+ AZ_TracePrintf(targetName.c_str(), "%.*s", aznumeric_cast(logData.size()), logData.data());
+ };
+ AzFramework::AssetSystemRequestBus::BroadcastResult(
+ connectedToAssetProcessor, &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
+
+ if (connectedToAssetProcessor)
+ {
+ CompileCriticalAssets();
+ }
+
+ AzFramework::AssetSystemStatusBus::Handler::BusDisconnect();
+ }
+
+ void AtomToolsApplication::CompileCriticalAssets()
+ {
+ AZ_TracePrintf(GetBuildTargetName().c_str(), "Compiling critical assets.\n");
+
+ QStringList failedAssets;
+
+ // Forced asset processor to synchronously process all critical assets
+ // Note: with AssetManager's current implementation, a compiled asset won't be added in asset registry until next system tick.
+ // So the asset id won't be found right after CompileAssetSync call.
+ for (const AZStd::string& assetFilters : GetCriticalAssetFilters())
+ {
+ AZ_TracePrintf(GetBuildTargetName().c_str(), "Compiling critical asset matching: %s.\n", assetFilters.c_str());
+
+ // Wait for the asset be compiled
+ AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
+ AzFramework::AssetSystemRequestBus::BroadcastResult(
+ status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, assetFilters);
+ if (status != AzFramework::AssetSystem::AssetStatus_Compiled)
+ {
+ failedAssets.append(assetFilters.c_str());
+ }
+ }
+
+ if (!failedAssets.empty())
+ {
+ QMessageBox::critical(
+ activeWindow(), QString("Failed to compile critical assets"),
+ QString("Failed to compile the following critical assets:\n%1\n%2")
+ .arg(failedAssets.join(",\n"))
+ .arg("Make sure this is an Atom project."));
+ ExitMainLoop();
+ }
+ }
+
+ void AtomToolsApplication::SaveSettings()
+ {
+ if (m_activatedLocalUserSettings)
+ {
+ AZ::SerializeContext* context = nullptr;
+ AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
+ AZ_Assert(context, "No serialize context");
+
+ char resolvedPath[AZ_MAX_PATH_LEN] = "";
+ AZStd::string fileName = "@user@/" + GetBuildTargetName() + "UserSettings.xml";
+
+ AZ::IO::FileIOBase::GetInstance()->ResolvePath(
+ fileName.c_str(), resolvedPath, AZ_ARRAY_SIZE(resolvedPath));
+ m_localUserSettings.Save(resolvedPath, context);
+ }
+ }
+
+ void AtomToolsApplication::LoadSettings()
+ {
+ AZ::SerializeContext* context = nullptr;
+ AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
+ AZ_Assert(context, "No serialize context");
+
+ char resolvedPath[AZ_MAX_PATH_LEN] = "";
+ AZStd::string fileName = "@user@/" + GetBuildTargetName() + "UserSettings.xml";
+
+ AZ::IO::FileIOBase::GetInstance()->ResolvePath(fileName.c_str(), resolvedPath, AZ_MAX_PATH_LEN);
+
+ m_localUserSettings.Load(resolvedPath, context);
+ m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
+ AZ::UserSettingsOwnerRequestBus::Handler::BusConnect(AZ::UserSettings::CT_LOCAL);
+ m_activatedLocalUserSettings = true;
+ }
+
+ void AtomToolsApplication::UnloadSettings()
+ {
+ if (m_activatedLocalUserSettings)
+ {
+ SaveSettings();
+ m_localUserSettings.Deactivate();
+ AZ::UserSettingsOwnerRequestBus::Handler::BusDisconnect();
+ m_activatedLocalUserSettings = false;
+ }
+ }
+
+ void AtomToolsApplication::ProcessCommandLine(const AZ::CommandLine& commandLine)
+ {
+ const AZStd::string timeoputSwitchName = "timeout";
+ if (commandLine.HasSwitch(timeoputSwitchName))
+ {
+ const AZStd::string& timeoutValue = commandLine.GetSwitchValue(timeoputSwitchName, 0);
+ const uint32_t timeoutInMs = atoi(timeoutValue.c_str());
+ AZ_Printf(GetBuildTargetName().c_str(), "Timeout scheduled, shutting down in %u ms", timeoutInMs);
+ QTimer::singleShot(
+ timeoutInMs,
+ [this]
+ {
+ AZ_Printf(GetBuildTargetName().c_str(), "Timeout reached, shutting down");
+ ExitMainLoop();
+ });
+ }
+
+ // Process command line options for running one or more python scripts on startup
+ const AZStd::string runPythonScriptSwitchName = "runpython";
+ size_t runPythonScriptCount = commandLine.GetNumSwitchValues(runPythonScriptSwitchName);
+ for (size_t runPythonScriptIndex = 0; runPythonScriptIndex < runPythonScriptCount; ++runPythonScriptIndex)
+ {
+ const AZStd::string runPythonScriptPath = commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex);
+ AZStd::vector runPythonArgs;
+
+ AZ_Printf(GetBuildTargetName().c_str(), "Launching script: %s", runPythonScriptPath.c_str());
+ AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
+ &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, runPythonScriptPath, runPythonArgs);
+ }
+
+ const AZStd::string exitAfterCommandsSwitchName = "exitaftercommands";
+ if (commandLine.HasSwitch(exitAfterCommandsSwitchName))
+ {
+ ExitMainLoop();
+ }
+ }
+
+ bool AtomToolsApplication::LaunchDiscoveryService()
+ {
+ // Determine if this is the first launch of the tool by attempting to connect to a running server
+ if (m_socket.Connect(QApplication::applicationName()))
+ {
+ // If the server was located, the application is already running.
+ // Forward commandline options to other application instance.
+ QByteArray buffer;
+ buffer.append("ProcessCommandLine:");
+
+ // Add the command line options from this process to the message, skipping the executable path
+ for (int argi = 1; argi < m_argC; ++argi)
+ {
+ buffer.append(QString(m_argV[argi]).append("\n").toUtf8());
+ }
+
+ // Inject command line option to always bring the main window to the foreground
+ buffer.append("--activatewindow\n");
+
+ m_socket.Send(buffer);
+ m_socket.Disconnect();
+ return false;
+ }
+
+ // Setup server to handle basic commands
+ m_server.SetReadHandler(
+ [this](const QByteArray& buffer)
+ {
+ // Handle commmand line params from connected socket
+ if (buffer.startsWith("ProcessCommandLine:"))
+ {
+ // Remove header and parse commands
+ AZStd::string params(buffer.data(), buffer.size());
+ params = params.substr(strlen("ProcessCommandLine:"));
+
+ AZStd::vector tokens;
+ AZ::StringFunc::Tokenize(params, tokens, "\n");
+
+ if (!tokens.empty())
+ {
+ AZ::CommandLine commandLine;
+ commandLine.Parse(tokens);
+ ProcessCommandLine(commandLine);
+ }
+ }
+ });
+
+ // Launch local server
+ if (!m_server.Connect(QApplication::applicationName()))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ void AtomToolsApplication::StartInternal()
+ {
+ if (WasExitMainLoopRequested())
+ {
+ return;
+ }
+
+ AZStd::string fileName = GetBuildTargetName() + ".log";
+
+ m_traceLogger.WriteStartupLog(fileName.c_str());
+
+ if (!LaunchDiscoveryService())
+ {
+ ExitMainLoop();
+ return;
+ }
+
+ AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect();
+ AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
+ &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
+
+ AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml");
+
+ AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets();
+
+ LoadSettings();
+
+ auto editorPythonEventsInterface = AZ::Interface::Get();
+ if (editorPythonEventsInterface)
+ {
+ // The PythonSystemComponent does not call StartPython to allow for lazy python initialization, so start it here
+ // The PythonSystemComponent will call StopPython when it deactivates, so we do not need our own corresponding call to
+ // StopPython
+ editorPythonEventsInterface->StartPython();
+ }
+
+ // Delay execution of commands and scripts post initialization
+ QTimer::singleShot(
+ 0,
+ [this]()
+ {
+ ProcessCommandLine(m_commandLine);
+ });
+ }
+
+ bool AtomToolsApplication::GetAssetDatabaseLocation(AZStd::string& result)
+ {
+ AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
+ AZ::IO::FixedMaxPath assetDatabaseSqlitePath;
+ if (settingsRegistry &&
+ settingsRegistry->Get(assetDatabaseSqlitePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
+ {
+ assetDatabaseSqlitePath /= "assetdb.sqlite";
+ result = AZStd::string_view(assetDatabaseSqlitePath.Native());
+ return true;
+ }
+
+ return false;
+ }
+
+ void AtomToolsApplication::Tick(float deltaOverride)
+ {
+ TickSystem();
+ Base::Tick(deltaOverride);
+
+ if (WasExitMainLoopRequested())
+ {
+ m_timer.disconnect();
+ quit();
+ }
+ }
+
+ void AtomToolsApplication::Stop()
+ {
+ UnloadSettings();
+ Base::Stop();
+ }
+
+ void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
+ {
+ appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game;
+ }
+
+ void AtomToolsApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message)
+ {
+#if defined(AZ_ENABLE_TRACING)
+ AZStd::vector lines;
+ AzFramework::StringFunc::Tokenize(
+ message, lines, "\n",
+ false, // Keep empty strings
+ false // Keep space strings
+ );
+
+ for (auto& line : lines)
+ {
+ AZ_TracePrintf(GetBuildTargetName().c_str(), "Python: %s\n", line.c_str());
+ }
+#endif
+ }
+
+ void AtomToolsApplication::OnErrorMessage(AZStd::string_view message)
+ {
+ // Use AZ_TracePrintf instead of AZ_Error or AZ_Warning to avoid all the metadata noise
+ OnTraceMessage(message);
+ }
+
+ void AtomToolsApplication::OnExceptionMessage([[maybe_unused]] AZStd::string_view message)
+ {
+ AZ_Error(GetBuildTargetName().c_str(), false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message));
+ }
+
+ // Copied from PyIdleWaitFrames in CryEdit.cpp
+ void AtomToolsApplication::PyIdleWaitFrames(uint32_t frames)
+ {
+ struct Ticker : public AZ::TickBus::Handler
+ {
+ Ticker(QEventLoop* loop, uint32_t targetFrames)
+ : m_loop(loop)
+ , m_targetFrames(targetFrames)
+ {
+ AZ::TickBus::Handler::BusConnect();
+ }
+ ~Ticker()
+ {
+ AZ::TickBus::Handler::BusDisconnect();
+ }
+
+ void OnTick(float deltaTime, AZ::ScriptTimePoint time) override
+ {
+ AZ_UNUSED(deltaTime);
+ AZ_UNUSED(time);
+ if (++m_elapsedFrames == m_targetFrames)
+ {
+ m_loop->quit();
+ }
+ }
+ QEventLoop* m_loop = nullptr;
+ uint32_t m_elapsedFrames = 0;
+ uint32_t m_targetFrames = 0;
+ };
+
+ QEventLoop loop;
+ Ticker ticker(&loop, frames);
+ loop.exec();
+ }
+} // namespace AtomToolsFramework
diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake
index dfe222bfc5..86fc3f5f5e 100644
--- a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake
+++ b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake
@@ -7,6 +7,7 @@
#
set(FILES
+ Include/AtomToolsFramework/Application/AtomToolsApplication.h
Include/AtomToolsFramework/Communication/LocalServer.h
Include/AtomToolsFramework/Communication/LocalSocket.h
Include/AtomToolsFramework/Debug/TraceRecorder.h
@@ -23,6 +24,7 @@ set(FILES
Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h
+ Source/Application/AtomToolsApplication.cpp
Source/Communication/LocalServer.cpp
Source/Communication/LocalSocket.cpp
Source/Debug/TraceRecorder.cpp
diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp
index 10ad1df5f3..7b9372e72b 100644
--- a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp
+++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp
@@ -6,7 +6,22 @@
*
*/
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+
#include
+#include
#include
#include
#include
@@ -26,24 +41,9 @@
#include
#include
-#include
-
-#include
-#include
-
#include
#include
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-#include
-
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include
#include
@@ -52,12 +52,12 @@ AZ_POP_DISABLE_WARNING
namespace MaterialEditor
{
//! This function returns the build system target name of "MaterialEditor
- AZStd::string_view GetBuildTargetName()
+ AZStd::string MaterialEditorApplication::GetBuildTargetName() const
{
#if !defined (LY_CMAKE_TARGET)
#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
#endif
- return AZStd::string_view{ LY_CMAKE_TARGET };
+ return AZStd::string{ LY_CMAKE_TARGET };
}
const char* MaterialEditorApplication::GetCurrentConfigurationName() const
@@ -72,19 +72,13 @@ namespace MaterialEditor
}
MaterialEditorApplication::MaterialEditorApplication(int* argc, char*** argv)
- : Application(argc, argv)
- , AzQtApplication(*argc, *argv)
+ : AtomToolsApplication(argc, argv)
+
{
QApplication::setApplicationName("O3DE Material Editor");
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
*AZ::SettingsRegistry::Get(), GetBuildTargetName());
-
- connect(&m_timer, &QTimer::timeout, this, [&]()
- {
- this->PumpSystemEventLoopUntilEmpty();
- this->Tick();
- });
}
MaterialEditorApplication::~MaterialEditorApplication()
@@ -94,88 +88,14 @@ namespace MaterialEditor
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
}
- void MaterialEditorApplication::CreateReflectionManager()
- {
- Application::CreateReflectionManager();
- GetSerializeContext()->CreateEditContext();
- }
-
- void MaterialEditorApplication::Reflect(AZ::ReflectContext* context)
- {
- Application::Reflect(context);
-
- AzToolsFramework::AssetBrowser::AssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::RootAssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry::Reflect(context);
-
- AzToolsFramework::QTreeViewWithStateSaving::Reflect(context);
- AzToolsFramework::QWidgetSavedState::Reflect(context);
-
- if (auto behaviorContext = azrtti_cast(context))
- {
- // this will put these methods into the 'azlmbr.materialeditor.general' module
- auto addGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
- {
- methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Editor")
- ->Attribute(AZ::Script::Attributes::Module, "materialeditor.general");
- };
- // The reflection here is based on patterns in CryEditPythonHandler::Reflect
- addGeneral(behaviorContext->Method("idle_wait_frames", &MaterialEditorApplication::PyIdleWaitFrames, nullptr, "Waits idling for a frames. Primarily used for auto-testing."));
- }
- }
-
- void MaterialEditorApplication::RegisterCoreComponents()
- {
- Application::RegisterCoreComponents();
- RegisterComponentDescriptor(AzToolsFramework::AssetBrowser::AssetBrowserComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::Thumbnailer::ThumbnailerComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::Components::PropertyManagerComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::AssetSystem::AssetSystemComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::PerforceComponent::CreateDescriptor());
- }
-
- AZ::ComponentTypeList MaterialEditorApplication::GetRequiredSystemComponents() const
- {
- AZ::ComponentTypeList components = Application::GetRequiredSystemComponents();
-
- components.insert(components.end(), {
- azrtti_typeid(),
- azrtti_typeid(),
- azrtti_typeid(),
- azrtti_typeid(),
- });
-
- return components;
- }
-
void MaterialEditorApplication::CreateStaticModules(AZStd::vector& outModules)
{
- Application::CreateStaticModules(outModules);
- outModules.push_back(aznew AzToolsFramework::AzToolsFrameworkModule);
+ Base::CreateStaticModules(outModules);
outModules.push_back(aznew MaterialDocumentModule);
outModules.push_back(aznew MaterialViewportModule);
outModules.push_back(aznew MaterialEditorWindowModule);
}
- void MaterialEditorApplication::StartCommon(AZ::Entity* systemEntity)
- {
- {
- //[GFX TODO][ATOM-408] This needs to be updated in some way to support the MaterialViewport render widget
- }
-
- AzFramework::AssetSystemStatusBus::Handler::BusConnect();
- AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect();
-
- AzFramework::Application::StartCommon(systemEntity);
-
- StartInternal();
-
- m_timer.start();
- }
-
void MaterialEditorApplication::OnMaterialEditorWindowClosing()
{
ExitMainLoop();
@@ -187,120 +107,14 @@ namespace MaterialEditor
MaterialEditor::MaterialEditorWindowFactoryRequestBus::Broadcast(
&MaterialEditor::MaterialEditorWindowFactoryRequestBus::Handler::DestroyMaterialEditorWindow);
- AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
- AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
MaterialEditorWindowNotificationBus::Handler::BusDisconnect();
- AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
- m_logFile = {};
- m_startupLogSink = {};
-
- AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
- Application::Destroy();
+ Base::Destroy();
}
- void MaterialEditorApplication::AssetSystemAvailable()
+ AZStd::vector MaterialEditorApplication::GetCriticalAssetFilters() const
{
- bool connectedToAssetProcessor = false;
-
- // When the AssetProcessor is already launched it should take less than a second to perform a connection
- // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
- // and able to negotiate a connection when running a debug build
- // and to negotiate a connection
-
- AzFramework::AssetSystem::ConnectionSettings connectionSettings;
- AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
- connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
- connectionSettings.m_connectionIdentifier = "MaterialEditor";
- connectionSettings.m_loggingCallback = []([[maybe_unused]] AZStd::string_view logData)
- {
- AZ_TracePrintf("Material Editor", "%.*s", aznumeric_cast(logData.size()), logData.data());
- };
- AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor,
- &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
- if (connectedToAssetProcessor)
- {
- CompileCriticalAssets();
- }
-
- AzFramework::AssetSystemStatusBus::Handler::BusDisconnect();
- }
-
-
- void MaterialEditorApplication::CompileCriticalAssets()
- {
- AZ_TracePrintf("MaterialEditor", "Compiling critical assets.\n");
-
- // List of common asset filters for things that need to be compiled to run the material editor
- // Some of these things will not be necessary once we have proper support for queued asset loading and reloading
- const AZStd::string assetFiltersArray[] =
- {
- "passes/",
- "config/",
- "MaterialEditor/",
- };
-
- QStringList failedAssets;
-
- // Forced asset processor to synchronously process all critical assets
- // Note: with AssetManager's current implementation, a compiled asset won't be added in asset registry until next system tick.
- // So the asset id won't be found right after CompileAssetSync call.
- for (const AZStd::string& assetFilters : assetFiltersArray)
- {
- AZ_TracePrintf("MaterialEditor", "Compiling critical asset matching: %s.\n", assetFilters.c_str());
-
- // Wait for the asset be compiled
- AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
- AzFramework::AssetSystemRequestBus::BroadcastResult(
- status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, assetFilters);
- if (status != AzFramework::AssetSystem::AssetStatus_Compiled)
- {
- failedAssets.append(assetFilters.c_str());
- }
- }
-
- if (!failedAssets.empty())
- {
- QMessageBox::critical(activeWindow(),
- QString("Failed to compile critical assets"),
- QString("Failed to compile the following critical assets:\n%1\n%2")
- .arg(failedAssets.join(",\n"))
- .arg("Make sure this is an Atom project."));
- ExitMainLoop();
- }
- }
-
- void MaterialEditorApplication::SaveSettings()
- {
- if (m_activatedLocalUserSettings)
- {
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
- AZ_Assert(context, "No serialize context");
-
- char resolvedPath[AZ_MAX_PATH_LEN] = "";
- AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/MaterialEditorUserSettings.xml", resolvedPath, AZ_ARRAY_SIZE(resolvedPath));
- m_localUserSettings.Save(resolvedPath, context);
- }
- }
-
- bool MaterialEditorApplication::OnOutput(const char* window, const char* message)
- {
- // Suppress spam from the Source Control system
- if (0 == strncmp(window, AzToolsFramework::SCC_WINDOW, AZ_ARRAY_SIZE(AzToolsFramework::SCC_WINDOW)))
- {
- return true;
- }
-
- if (m_logFile)
- {
- m_logFile->AppendLog(AzFramework::LogFile::SEV_NORMAL, window, message);
- }
- else
- {
- m_startupLogSink.push_back({ window, message });
- }
- return false;
+ return AZStd::vector({ "passes/", "config/", "MaterialEditor" });
}
void MaterialEditorApplication::ProcessCommandLine(const AZ::CommandLine& commandLine)
@@ -312,195 +126,27 @@ namespace MaterialEditor
&MaterialEditor::MaterialEditorWindowRequestBus::Handler::ActivateWindow);
}
- const AZStd::string timeoputSwitchName = "timeout";
- if (commandLine.HasSwitch(timeoputSwitchName))
- {
- const AZStd::string& timeoutValue = commandLine.GetSwitchValue(timeoputSwitchName, 0);
- const uint32_t timeoutInMs = atoi(timeoutValue.c_str());
- AZ_Printf("MaterialEditor", "Timeout scheduled, shutting down in %u ms", timeoutInMs);
- QTimer::singleShot(timeoutInMs, [this] {
- AZ_Printf("MaterialEditor", "Timeout reached, shutting down");
- ExitMainLoop();
- });
- }
-
- // Process command line options for running one or more python scripts on startup
- const AZStd::string runPythonScriptSwitchName = "runpython";
- size_t runPythonScriptCount = commandLine.GetNumSwitchValues(runPythonScriptSwitchName);
- for (size_t runPythonScriptIndex = 0; runPythonScriptIndex < runPythonScriptCount; ++runPythonScriptIndex)
- {
- const AZStd::string runPythonScriptPath = commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex);
- AZStd::vector runPythonArgs;
-
- AZ_Printf("MaterialEditor", "Launching script: %s", runPythonScriptPath.c_str());
- AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
- &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs,
- runPythonScriptPath,
- runPythonArgs);
- }
-
// Process command line options for opening one or more material documents on startup
size_t openDocumentCount = commandLine.GetNumMiscValues();
for (size_t openDocumentIndex = 0; openDocumentIndex < openDocumentCount; ++openDocumentIndex)
{
const AZStd::string openDocumentPath = commandLine.GetMiscValue(openDocumentIndex);
- AZ_Printf("MaterialEditor", "Opening document: %s", openDocumentPath.c_str());
+ AZ_Printf(GetBuildTargetName().c_str(), "Opening document: %s", openDocumentPath.c_str());
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath);
}
- const AZStd::string exitAfterCommandsSwitchName = "exitaftercommands";
- if (commandLine.HasSwitch(exitAfterCommandsSwitchName))
- {
- ExitMainLoop();
- }
- }
-
- void MaterialEditorApplication::LoadSettings()
- {
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
- AZ_Assert(context, "No serialize context");
-
- char resolvedPath[AZ_MAX_PATH_LEN] = "";
- AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/EditorUserSettings.xml", resolvedPath, AZ_MAX_PATH_LEN);
-
- m_localUserSettings.Load(resolvedPath, context);
- m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
- AZ::UserSettingsOwnerRequestBus::Handler::BusConnect(AZ::UserSettings::CT_LOCAL);
- m_activatedLocalUserSettings = true;
- }
-
- void MaterialEditorApplication::UnloadSettings()
- {
- if (m_activatedLocalUserSettings)
- {
- SaveSettings();
- m_localUserSettings.Deactivate();
- AZ::UserSettingsOwnerRequestBus::Handler::BusDisconnect();
- m_activatedLocalUserSettings = false;
- }
- }
-
- bool MaterialEditorApplication::LaunchDiscoveryService()
- {
- // Determine if this is the first launch of the tool by attempting to connect to a running server
- if (m_socket.Connect(QApplication::applicationName()))
- {
- // If the server was located, the application is already running.
- // Forward commandline options to other application instance.
- QByteArray buffer;
- buffer.append("ProcessCommandLine:");
-
- // Add the command line options from this process to the message, skipping the executable path
- for (int argi = 1; argi < m_argC; ++argi)
- {
- buffer.append(QString(m_argV[argi]).append("\n").toUtf8());
- }
-
- // Inject command line option to always bring the main window to the foreground
- buffer.append("--activatewindow\n");
-
- m_socket.Send(buffer);
- m_socket.Disconnect();
- return false;
- }
-
- // Setup server to handle basic commands
- m_server.SetReadHandler([this](const QByteArray& buffer) {
- // Handle commmand line params from connected socket
- if (buffer.startsWith("ProcessCommandLine:"))
- {
- // Remove header and parse commands
- AZStd::string params(buffer.data(), buffer.size());
- params = params.substr(strlen("ProcessCommandLine:"));
-
- AZStd::vector tokens;
- AZ::StringFunc::Tokenize(params, tokens, "\n");
-
- if (!tokens.empty())
- {
- AZ::CommandLine commandLine;
- commandLine.Parse(tokens);
- ProcessCommandLine(commandLine);
- }
- }
- });
-
- // Launch local server
- if (!m_server.Connect(QApplication::applicationName()))
- {
- return false;
- }
-
- return true;
+ Base::ProcessCommandLine(commandLine);
}
void MaterialEditorApplication::StartInternal()
{
- if (WasExitMainLoopRequested())
- {
- return;
- }
-
- m_traceLogger.WriteStartupLog("MaterialEditor.log");
-
- if (!LaunchDiscoveryService())
- {
- ExitMainLoop();
- return;
- }
-
- AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect();
- AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
-
- AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml");
-
- AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets();
-
- LoadSettings();
+ Base::StartInternal();
MaterialEditorWindowNotificationBus::Handler::BusConnect();
MaterialEditor::MaterialEditorWindowFactoryRequestBus::Broadcast(
&MaterialEditor::MaterialEditorWindowFactoryRequestBus::Handler::CreateMaterialEditorWindow);
-
- auto editorPythonEventsInterface = AZ::Interface::Get();
- if (editorPythonEventsInterface)
- {
- // The PythonSystemComponent does not call StartPython to allow for lazy python initialization, so start it here
- // The PythonSystemComponent will call StopPython when it deactivates, so we do not need our own corresponding call to StopPython
- editorPythonEventsInterface->StartPython();
- }
-
- // Delay execution of commands and scripts post initialization
- QTimer::singleShot(0, [this]() { ProcessCommandLine(m_commandLine); });
- }
-
- bool MaterialEditorApplication::GetAssetDatabaseLocation(AZStd::string& result)
- {
- AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
- AZ::IO::FixedMaxPath assetDatabaseSqlitePath;
- if (settingsRegistry && settingsRegistry->Get(assetDatabaseSqlitePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
- {
- assetDatabaseSqlitePath /= "assetdb.sqlite";
- result = AZStd::string_view(assetDatabaseSqlitePath.Native());
- return true;
- }
-
- return false;
- }
-
- void MaterialEditorApplication::Tick(float deltaOverride)
- {
- TickSystem();
- Application::Tick(deltaOverride);
-
- if (WasExitMainLoopRequested())
- {
- m_timer.disconnect();
- quit();
- }
}
void MaterialEditorApplication::Stop()
@@ -508,76 +154,6 @@ namespace MaterialEditor
MaterialEditor::MaterialEditorWindowFactoryRequestBus::Broadcast(
&MaterialEditor::MaterialEditorWindowFactoryRequestBus::Handler::DestroyMaterialEditorWindow);
- UnloadSettings();
- AzFramework::Application::Stop();
+ Base::Stop();
}
-
- void MaterialEditorApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
- {
- appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game;
- }
-
- void MaterialEditorApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message)
- {
-#if defined(AZ_ENABLE_TRACING)
- AZStd::vector lines;
- AzFramework::StringFunc::Tokenize(
- message,
- lines,
- "\n",
- false, // Keep empty strings
- false // Keep space strings
- );
-
- for (auto& line : lines)
- {
- AZ_TracePrintf("MaterialEditor", "Python: %s\n", line.c_str());
- }
-#endif
- }
-
- void MaterialEditorApplication::OnErrorMessage(AZStd::string_view message)
- {
- // Use AZ_TracePrintf instead of AZ_Error or AZ_Warning to avoid all the metadata noise
- OnTraceMessage(message);
- }
-
- void MaterialEditorApplication::OnExceptionMessage([[maybe_unused]] AZStd::string_view message)
- {
- AZ_Error("MaterialEditor", false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message));
- }
-
- // Copied from PyIdleWaitFrames in CryEdit.cpp
- void MaterialEditorApplication::PyIdleWaitFrames(uint32_t frames)
- {
- struct Ticker : public AZ::TickBus::Handler
- {
- Ticker(QEventLoop* loop, uint32_t targetFrames) : m_loop(loop), m_targetFrames(targetFrames)
- {
- AZ::TickBus::Handler::BusConnect();
- }
- ~Ticker()
- {
- AZ::TickBus::Handler::BusDisconnect();
- }
-
- void OnTick(float deltaTime, AZ::ScriptTimePoint time) override
- {
- AZ_UNUSED(deltaTime);
- AZ_UNUSED(time);
- if (++m_elapsedFrames == m_targetFrames)
- {
- m_loop->quit();
- }
- }
- QEventLoop* m_loop = nullptr;
- uint32_t m_elapsedFrames = 0;
- uint32_t m_targetFrames = 0;
- };
-
- QEventLoop loop;
- Ticker ticker(&loop, frames);
- loop.exec();
- }
-
} // namespace MaterialEditor
diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h
index e6fe9401f6..b1c742d4dd 100644
--- a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h
+++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h
@@ -10,19 +10,7 @@
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
#include
@@ -31,41 +19,24 @@ namespace MaterialEditor
class MaterialThumbnailRenderer;
class MaterialEditorApplication
- : public AzFramework::Application
- , public AzQtComponents::AzQtApplication
- , private AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler
+ : public AtomToolsFramework::AtomToolsApplication
, private MaterialEditorWindowNotificationBus::Handler
- , private AzFramework::AssetSystemStatusBus::Handler
- , private AZ::UserSettingsOwnerRequestBus::Handler
- , private AZ::Debug::TraceMessageBus::Handler
- , private AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
{
public:
AZ_TYPE_INFO(MaterialEditor::MaterialEditorApplication, "{30F90CA5-1253-49B5-8143-19CEE37E22BB}");
- using Base = AzFramework::Application;
+ using Base = AtomToolsFramework::AtomToolsApplication;
MaterialEditorApplication(int* argc, char*** argv);
virtual ~MaterialEditorApplication();
//////////////////////////////////////////////////////////////////////////
// AzFramework::Application
- void CreateReflectionManager() override;
- void Reflect(AZ::ReflectContext* context) override;
- void RegisterCoreComponents() override;
- AZ::ComponentTypeList GetRequiredSystemComponents() const override;
void CreateStaticModules(AZStd::vector& outModules) override;
const char* GetCurrentConfigurationName() const override;
- void StartCommon(AZ::Entity* systemEntity) override;
- void Tick(float deltaOverride = -1.f) override;
void Stop() override;
private:
- //////////////////////////////////////////////////////////////////////////
- // AssetDatabaseRequestsBus::Handler overrides...
- bool GetAssetDatabaseLocation(AZStd::string& result) override;
- //////////////////////////////////////////////////////////////////////////
-
//////////////////////////////////////////////////////////////////////////
// MaterialEditorWindowNotificationBus::Handler overrides...
void OnMaterialEditorWindowClosing() override;
@@ -76,66 +47,12 @@ namespace MaterialEditor
void Destroy() override;
//////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////
- // AZ::ComponentApplication overrides...
- void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
- //////////////////////////////////////////////////////////////////////////
+ void ProcessCommandLine(const AZ::CommandLine& commandLine) override;
+ void StartInternal() override;
+ AZStd::string GetBuildTargetName() const override;
- ////////////////////////////////////////////////////////////////////////
- // EditorPythonConsoleNotificationBus::Handler overrides...
- void OnTraceMessage(AZStd::string_view message) override;
- void OnErrorMessage(AZStd::string_view message) override;
- void OnExceptionMessage(AZStd::string_view message) override;
- ////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////
- // AzFramework::AssetSystemStatusBus::Handler overrides...
- void AssetSystemAvailable() override;
- //////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////
- // AZ::UserSettingsOwnerRequestBus::Handler overrides...
- void SaveSettings() override;
- //////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////
- // AZ::Debug::TraceMessageBus::Handler overrides...
- bool OnOutput(const char* window, const char* message) override;
- //////////////////////////////////////////////////////////////////////////
-
- void CompileCriticalAssets();
-
- void ProcessCommandLine(const AZ::CommandLine& commandLine);
-
- void LoadSettings();
- void UnloadSettings();
-
- bool LaunchDiscoveryService();
-
- void StartInternal();
-
- static void PyIdleWaitFrames(uint32_t frames);
-
- struct LogMessage
- {
- AZStd::string window;
- AZStd::string message;
- };
-
- AZStd::vector m_startupLogSink;
- AZStd::unique_ptr m_logFile;
-
- AzToolsFramework::TraceLogger m_traceLogger;
-
- //! Local user settings are used to store material browser tree expansion state
- AZ::UserSettingsProvider m_localUserSettings;
-
- //! Are local settings loaded
- bool m_activatedLocalUserSettings = false;
-
- QTimer m_timer;
-
- AtomToolsFramework::LocalSocket m_socket;
- AtomToolsFramework::LocalServer m_server;
- };
+ //! List of common asset filters for things that need to be compiled to run the material editor
+ //! Some of these things will not be necessary once we have proper support for queued asset loading and reloading
+ AZStd::vector GetCriticalAssetFilters() const override;
+ };
} // namespace MaterialEditor
diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp
index a39480637b..7d2aa02e19 100644
--- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp
+++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp
@@ -48,7 +48,7 @@ AZ_POP_DISABLE_WARNING
namespace ShaderManagementConsole
{
- AZStd::string_view GetBuildTargetName()
+ AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const
{
#if !defined (LY_CMAKE_TARGET)
#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
@@ -68,99 +68,22 @@ namespace ShaderManagementConsole
}
ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv)
- : Application(argc, argv)
- , AzQtApplication(*argc, *argv)
+ : AtomToolsApplication(argc, argv)
{
QApplication::setApplicationName("O3DE Shader Management Console");
// The settings registry has been created at this point, so add the CMake target
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
*AZ::SettingsRegistry::Get(), GetBuildTargetName());
-
- connect(&m_timer, &QTimer::timeout, this, [&]()
- {
- this->PumpSystemEventLoopUntilEmpty();
- this->Tick();
- });
- }
-
- void ShaderManagementConsoleApplication::CreateReflectionManager()
- {
- Application::CreateReflectionManager();
- GetSerializeContext()->CreateEditContext();
- }
-
- void ShaderManagementConsoleApplication::Reflect(AZ::ReflectContext* context)
- {
- Application::Reflect(context);
-
- AzToolsFramework::AssetBrowser::AssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::RootAssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry::Reflect(context);
- AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry::Reflect(context);
-
- AzToolsFramework::QTreeViewWithStateSaving::Reflect(context);
- AzToolsFramework::QWidgetSavedState::Reflect(context);
-
- if (auto behaviorContext = azrtti_cast(context))
- {
- // this will put these methods into the 'azlmbr.shadermanagementconsole.general' module
- auto addGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
- {
- methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
- ->Attribute(AZ::Script::Attributes::Category, "Editor")
- ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole.general");
- };
- // The reflection here is based on patterns in CryEditPythonHandler::Reflect
- addGeneral(behaviorContext->Method("idle_wait_frames", &ShaderManagementConsoleApplication::PyIdleWaitFrames, nullptr, "Waits idling for a frames. Primarily used for auto-testing."));
- }
- }
-
- void ShaderManagementConsoleApplication::RegisterCoreComponents()
- {
- Application::RegisterCoreComponents();
- RegisterComponentDescriptor(AzToolsFramework::AssetBrowser::AssetBrowserComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::Thumbnailer::ThumbnailerComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::Components::PropertyManagerComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::AssetSystem::AssetSystemComponent::CreateDescriptor());
- RegisterComponentDescriptor(AzToolsFramework::PerforceComponent::CreateDescriptor());
- }
-
- AZ::ComponentTypeList ShaderManagementConsoleApplication::GetRequiredSystemComponents() const
- {
- AZ::ComponentTypeList components = Application::GetRequiredSystemComponents();
-
- components.insert(components.end(), {
- azrtti_typeid(),
- azrtti_typeid(),
- azrtti_typeid(),
- azrtti_typeid(),
- });
-
- return components;
}
void ShaderManagementConsoleApplication::CreateStaticModules(AZStd::vector& outModules)
{
- Application::CreateStaticModules(outModules);
- outModules.push_back(aznew AzToolsFramework::AzToolsFrameworkModule);
+ Base::CreateStaticModules(outModules);
outModules.push_back(aznew ShaderManagementConsoleDocumentModule);
outModules.push_back(aznew ShaderManagementConsoleWindowModule);
}
- void ShaderManagementConsoleApplication::StartCommon(AZ::Entity* systemEntity)
- {
- AzFramework::AssetSystemStatusBus::Handler::BusConnect();
- AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect();
-
- AzFramework::Application::StartCommon(systemEntity);
-
- StartInternal();
-
- m_timer.start();
- }
-
void ShaderManagementConsoleApplication::OnShaderManagementConsoleWindowClosing()
{
ExitMainLoop();
@@ -174,110 +97,13 @@ namespace ShaderManagementConsole
ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Broadcast(&ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Handler::DestroyShaderManagementConsoleWindow);
ShaderManagementConsoleWindowNotificationBus::Handler::BusDisconnect();
- AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
- AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
-
- Application::Destroy();
+ Base::Destroy();
}
- void ShaderManagementConsoleApplication::AssetSystemAvailable()
+ AZStd::vector ShaderManagementConsoleApplication::GetCriticalAssetFilters() const
{
- // Try connect to AP first before try to launch it manually.
- bool connected = false;
- auto ConnectToAssetProcessorWithIdentifier = [&connected](AzFramework::AssetSystem::AssetSystemRequests* assetSystemRequests)
- {
- // When the AssetProcessor is already launched it should take less than a second to perform a connection
- // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
- // and able to negotiate a connection when running a debug build
- // and to negotiate a connection
-
- AzFramework::AssetSystem::ConnectionSettings connectionSettings;
- AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
- connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
- connectionSettings.m_connectionIdentifier = "Shader Management Console";
- connectionSettings.m_loggingCallback = []([[maybe_unused]] AZStd::string_view logData)
- {
- AZ_TracePrintf("Shader Management Console", "%.*s", aznumeric_cast(logData.size()), logData.data());
- };
-
- connected = assetSystemRequests->EstablishAssetProcessorConnection(connectionSettings);
- };
- AzFramework::AssetSystemRequestBus::Broadcast(ConnectToAssetProcessorWithIdentifier);
-
- if (connected)
- {
- CompileCriticalAssets();
- }
-
- AzFramework::AssetSystemStatusBus::Handler::BusDisconnect();
- }
-
- void ShaderManagementConsoleApplication::CompileCriticalAssets()
- {
- AZ_TracePrintf("Shader Management Console", "Compiling critical assets.\n");
-
- // List of common asset filters for things that need to be compiled to run
- // Some of these things will not be necessary once we have proper support for queued asset loading and reloading
- const AZStd::string assetFilterss[] =
- {
- "passes/",
- "config/",
- };
-
- QStringList failedAssets;
-
- // Forced asset processor to synchronously process all critical assets
- // Note: with AssetManager's current implementation, a compiled asset won't be added in asset registry until next system tick.
- // So the asset id won't be found right after CompileAssetSync call.
- for (const AZStd::string& assetFilters : assetFilterss)
- {
- AZ_TracePrintf("Shader Management Console", "Compiling critical asset matching: %s.\n", assetFilters.c_str());
-
- // Wait for the asset be compiled
- AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
- AzFramework::AssetSystemRequestBus::BroadcastResult(
- status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, assetFilters);
- if (status != AzFramework::AssetSystem::AssetStatus_Compiled)
- {
- failedAssets.append(assetFilters.c_str());
- }
- }
-
- if (!failedAssets.empty())
- {
- QMessageBox::critical(activeWindow(),
- QString("Failed to compile critical assets"),
- QString("Failed to compile the following critical assets:\n%1\n%2")
- .arg(failedAssets.join(",\n"))
- .arg("Make sure this is an Atom project."));
- m_closing = true;
- }
- }
-
- void ShaderManagementConsoleApplication::SaveSettings()
- {
- if (m_activatedLocalUserSettings)
- {
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
- AZ_Assert(context, "No serialize context");
-
- char resolvedPath[AZ_MAX_PATH_LEN] = "";
- AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/EditorUserSettings.xml", resolvedPath, AZ_ARRAY_SIZE(resolvedPath));
- m_localUserSettings.Save(resolvedPath, context);
- }
- }
-
- bool ShaderManagementConsoleApplication::OnPrintf(const char* window, const char* /*message*/)
- {
- // Suppress spam from the Source Control system
- if (0 == strncmp(window, AzToolsFramework::SCC_WINDOW, AZ_ARRAY_SIZE(AzToolsFramework::SCC_WINDOW)))
- {
- return true;
- }
-
- return false;
+ return AZStd::vector({ "passes/", "config/" });
}
void ShaderManagementConsoleApplication::ProcessCommandLine()
@@ -304,173 +130,12 @@ namespace ShaderManagementConsole
}
}
- void ShaderManagementConsoleApplication::LoadSettings()
- {
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
- AZ_Assert(context, "No serialize context");
-
- char resolvedPath[AZ_MAX_PATH_LEN] = "";
- AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/EditorUserSettings.xml", resolvedPath, AZ_MAX_PATH_LEN);
-
- m_localUserSettings.Load(resolvedPath, context);
- m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
- AZ::UserSettingsOwnerRequestBus::Handler::BusConnect(AZ::UserSettings::CT_LOCAL);
- m_activatedLocalUserSettings = true;
- }
-
- void ShaderManagementConsoleApplication::UnloadSettings()
- {
- if (m_activatedLocalUserSettings)
- {
- SaveSettings();
- m_localUserSettings.Deactivate();
- AZ::UserSettingsOwnerRequestBus::Handler::BusDisconnect();
- m_activatedLocalUserSettings = false;
- }
- }
-
- bool ShaderManagementConsoleApplication::LaunchDiscoveryService()
- {
- const QStringList arguments = { "-fail_silently" };
-
- return AtomToolsFramework::LaunchTool("GridHub", AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT, arguments);
- }
-
void ShaderManagementConsoleApplication::StartInternal()
{
- if (m_closing)
- {
- return;
- }
-
- m_traceLogger.WriteStartupLog("ShaderManagementConsole.log");
-
- //[GFX TODO][ATOM-415] Try to factor out some of this stuff with AtomSampleViewerApplication
- AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect();
- AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
-
- AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml");
-
- AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets();
-
- LoadSettings();
-
- LaunchDiscoveryService();
+ Base::StartInternal();
ShaderManagementConsoleWindowNotificationBus::Handler::BusConnect();
ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Broadcast(&ShaderManagementConsole::ShaderManagementConsoleWindowRequestBus::Handler::CreateShaderManagementConsoleWindow);
-
- auto editorPythonEventsInterface = AZ::Interface::Get();
- if (editorPythonEventsInterface)
- {
- // The PythonSystemComponent does not call StartPython to allow for lazy python initialization, so start it here
- // The PythonSystemComponent will call StopPython when it deactivates, so we do not need our own corresponding call to StopPython
- editorPythonEventsInterface->StartPython();
- }
-
- ProcessCommandLine();
}
-
- bool ShaderManagementConsoleApplication::GetAssetDatabaseLocation(AZStd::string& result)
- {
- AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
- AZ::IO::FixedMaxPath assetDatabaseSqlitePath;
- if (settingsRegistry && settingsRegistry->Get(assetDatabaseSqlitePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
- {
- assetDatabaseSqlitePath /= "assetdb.sqlite";
- result = AZStd::string_view(assetDatabaseSqlitePath.Native());
- return true;
- }
-
- return false;
- }
-
- void ShaderManagementConsoleApplication::Tick(float deltaOverride)
- {
- TickSystem();
- Application::Tick(deltaOverride);
-
- if (m_closing)
- {
- m_timer.disconnect();
- quit();
- }
- }
-
- void ShaderManagementConsoleApplication::Stop()
- {
- UnloadSettings();
- AzFramework::Application::Stop();
- }
-
- void ShaderManagementConsoleApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
- {
- appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game;
- }
-
- void ShaderManagementConsoleApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message)
- {
-#if defined(AZ_ENABLE_TRACING)
- AZStd::vector lines;
- AzFramework::StringFunc::Tokenize(
- message,
- lines,
- "\n",
- false, // Keep empty strings
- false // Keep space strings
- );
-
- for (auto& line : lines)
- {
- AZ_TracePrintf("Shader Management Console", "Python: %s\n", line.c_str());
- }
-#endif
- }
-
- void ShaderManagementConsoleApplication::OnErrorMessage(AZStd::string_view message)
- {
- // Use AZ_TracePrintf instead of AZ_Error or AZ_Warning to avoid all the metadata noise
- OnTraceMessage(message);
- }
-
- void ShaderManagementConsoleApplication::OnExceptionMessage([[maybe_unused]] AZStd::string_view message)
- {
- AZ_Error("Shader Management Console", false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message));
- }
-
- // Copied from PyIdleWaitFrames in CryEdit.cpp
- void ShaderManagementConsoleApplication::PyIdleWaitFrames(uint32_t frames)
- {
- struct Ticker : public AZ::TickBus::Handler
- {
- Ticker(QEventLoop* loop, uint32_t targetFrames) : m_loop(loop), m_targetFrames(targetFrames)
- {
- AZ::TickBus::Handler::BusConnect();
- }
- ~Ticker()
- {
- AZ::TickBus::Handler::BusDisconnect();
- }
-
- void OnTick(float deltaTime, AZ::ScriptTimePoint time) override
- {
- AZ_UNUSED(deltaTime);
- AZ_UNUSED(time);
- if (++m_elapsedFrames == m_targetFrames)
- {
- m_loop->quit();
- }
- }
- QEventLoop* m_loop = nullptr;
- uint32_t m_elapsedFrames = 0;
- uint32_t m_targetFrames = 0;
- };
-
- QEventLoop loop;
- Ticker ticker(&loop, frames);
- loop.exec();
- }
-
} // namespace ShaderManagementConsole
diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h
index a137e3a646..5d3696fee3 100644
--- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h
+++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h
@@ -8,63 +8,32 @@
#pragma once
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-#include
-#include
-
#include
#include
-
-#include
+#include
#include
namespace ShaderManagementConsole
{
class ShaderManagementConsoleApplication
- : public AzFramework::Application
- , public AzQtComponents::AzQtApplication
- , private AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler
+ : public AtomToolsFramework::AtomToolsApplication
, private ShaderManagementConsoleWindowNotificationBus::Handler
- , private AzFramework::AssetSystemStatusBus::Handler
- , private AZ::UserSettingsOwnerRequestBus::Handler
- , private AZ::Debug::TraceMessageBus::Handler
- , private AzToolsFramework::EditorPythonConsoleNotificationBus::Handler
{
public:
- AZ_TYPE_INFO(ShaderManagementConsole::ShaderManagementConsoleApplication, "{30F90CA5-1253-49B5-8143-19CEE37E22BB}");
+ AZ_TYPE_INFO(ShaderManagementConsole::ShaderManagementConsoleApplication, "{A31B1AEB-4DA3-49CD-884A-CC998FF7546F}");
- using Base = AzFramework::Application;
+ using Base = AtomToolsFramework::AtomToolsApplication;
ShaderManagementConsoleApplication(int* argc, char*** argv);
virtual ~ShaderManagementConsoleApplication() = default;
//////////////////////////////////////////////////////////////////////////
// AzFramework::Application
- void CreateReflectionManager() override;
- void Reflect(AZ::ReflectContext* context) override;
- void RegisterCoreComponents() override;
- AZ::ComponentTypeList GetRequiredSystemComponents() const override;
void CreateStaticModules(AZStd::vector& outModules) override;
const char* GetCurrentConfigurationName() const override;
- void StartCommon(AZ::Entity* systemEntity) override;
- void Tick(float deltaOverride = -1.f) override;
- void Stop() override;
private:
- //////////////////////////////////////////////////////////////////////////
- // AssetDatabaseRequestsBus::Handler overrides...
- bool GetAssetDatabaseLocation(AZStd::string& result) override;
- //////////////////////////////////////////////////////////////////////////
-
//////////////////////////////////////////////////////////////////////////
// ShaderManagementConsoleWindowNotificationBus::Handler overrides...
void OnShaderManagementConsoleWindowClosing() override;
@@ -75,57 +44,9 @@ namespace ShaderManagementConsole
void Destroy() override;
//////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////
- // AzFramework::ApplicationRequests::Bus overrides...
- void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
- //////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////
- // EditorPythonConsoleNotificationBus::Handler overrides...
- void OnTraceMessage(AZStd::string_view message) override;
- void OnErrorMessage(AZStd::string_view message) override;
- void OnExceptionMessage(AZStd::string_view message) override;
- ////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////
- // AzFramework::AssetSystemStatusBus::Handler overrides...
- void AssetSystemAvailable() override;
- //////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////
- // AZ::UserSettingsOwnerRequestBus::Handler overrides...
- void SaveSettings() override;
- //////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////
- // AZ::Debug::TraceMessageBus::Handler overrides...
- bool OnPrintf(const char* window, const char* message) override;
- //////////////////////////////////////////////////////////////////////////
-
- void CompileCriticalAssets();
-
void ProcessCommandLine();
-
- void LoadSettings();
- void UnloadSettings();
-
- bool LaunchDiscoveryService();
-
- void StartInternal();
-
- static void PyIdleWaitFrames(uint32_t frames);
-
- AzToolsFramework::TraceLogger m_traceLogger;
-
- //! Local user settings are used to store asset browser tree expansion state
- AZ::UserSettingsProvider m_localUserSettings;
-
- //! Are local settings loaded
- bool m_activatedLocalUserSettings = false;
-
- QTimer m_timer;
-
- bool m_started = false;
- bool m_closing = false;
+ void StartInternal() override;
+ AZStd::string GetBuildTargetName() const override;
+ AZStd::vector GetCriticalAssetFilters() const override;
};
} // namespace ShaderManagementConsole
diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h
index c0ee4f8e02..66be0b9137 100644
--- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h
+++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h
@@ -75,6 +75,8 @@ namespace AZ
private:
static constexpr float RowHeight = 50.0;
static constexpr int DefaultFramesToCollect = 50;
+ static constexpr float MediumFrameTimeLimit = 16.6; // 60 fps
+ static constexpr float HighFrameTimeLimit = 33.3; // 30 fps
// Draw the shared header between the two windows
void DrawCommonHeader();
@@ -127,8 +129,11 @@ namespace AZ
// Draw the ruler with frame time labels
void DrawRuler();
+ // Draw the frame time histogram
+ void DrawFrameTimeHistogram();
+
// Converts raw ticks to a pixel value suitable to give to ImDrawList, handles window scrolling
- float ConvertTickToPixelSpace(AZStd::sys_time_t tick) const;
+ float ConvertTickToPixelSpace(AZStd::sys_time_t tick, AZStd::sys_time_t leftBound, AZStd::sys_time_t rightBound) const;
AZStd::sys_time_t GetViewportTickWidth() const;
diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl
index b59747421e..735dace1c7 100644
--- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl
+++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl
@@ -280,7 +280,7 @@ namespace AZ
{
ImGui::Columns(3, "Options", true);
ImGui::Text("Frames To Collect:");
- ImGui::SliderInt("", &m_framesToCollect, 10, 100, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
+ ImGui::SliderInt("", &m_framesToCollect, 10, 10000, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
ImGui::NextColumn();
@@ -295,6 +295,13 @@ namespace AZ
"Hold the right mouse button to move around. Zoom by scrolling the mouse wheel while holding .");
}
+ ImGui::Columns(1, "FrameTimeColumn", true);
+
+ if (ImGui::BeginChild("FrameTimeHistogram", { 0, 50 }, true, ImGuiWindowFlags_NoScrollbar))
+ {
+ DrawFrameTimeHistogram();
+ }
+ ImGui::EndChild();
ImGui::Columns(1, "RulerColumn", true);
@@ -519,8 +526,8 @@ namespace AZ
ImDrawList* drawList = ImGui::GetWindowDrawList();
- const float startPixel = ConvertTickToPixelSpace(block.m_startTick);
- const float endPixel = ConvertTickToPixelSpace(block.m_endTick);
+ const float startPixel = ConvertTickToPixelSpace(block.m_startTick, m_viewportStartTick, m_viewportEndTick);
+ const float endPixel = ConvertTickToPixelSpace(block.m_endTick, m_viewportStartTick, m_viewportEndTick);
const ImVec2 startPoint = { startPixel, wy + targetRow * RowHeight };
const ImVec2 endPoint = { endPixel, wy + targetRow * RowHeight + 40 };
@@ -656,7 +663,7 @@ namespace AZ
while (endTickItr != m_frameEndTicks.end() && *endTickItr < m_viewportEndTick)
{
- const float horizontalPixel = ConvertTickToPixelSpace(*endTickItr);
+ const float horizontalPixel = ConvertTickToPixelSpace(*endTickItr, m_viewportStartTick, m_viewportEndTick);
drawList->AddLine({ horizontalPixel, wy }, { horizontalPixel, wy + windowHeight }, red);
++endTickItr;
}
@@ -684,8 +691,8 @@ namespace AZ
break;
}
- const float lastFrameBoundaryPixel = ConvertTickToPixelSpace(lastFrameBoundaryTick);
- const float nextFrameBoundaryPixel = ConvertTickToPixelSpace(nextFrameBoundaryTick);
+ const float lastFrameBoundaryPixel = ConvertTickToPixelSpace(lastFrameBoundaryTick, m_viewportStartTick, m_viewportEndTick);
+ const float nextFrameBoundaryPixel = ConvertTickToPixelSpace(nextFrameBoundaryTick, m_viewportStartTick, m_viewportEndTick);
const AZStd::string label =
AZStd::string::format("%.2f ms", CpuProfilerImGuiHelper::TicksToMs(nextFrameBoundaryTick - lastFrameBoundaryTick));
@@ -738,16 +745,98 @@ namespace AZ
}
}
+ inline void ImGuiCpuProfiler::DrawFrameTimeHistogram()
+ {
+ ImDrawList* drawList = ImGui::GetWindowDrawList();
+ const auto [wx, wy] = ImGui::GetWindowPos();
+ const ImU32 orange = ImGui::GetColorU32({ 1, .7, 0, 1 });
+ const ImU32 red = ImGui::GetColorU32({ 1, 0, 0, 1 });
+
+ const AZStd::sys_time_t ticksPerSecond = AZStd::GetTimeTicksPerSecond();
+ const AZStd::sys_time_t viewportCenter = m_viewportEndTick - (m_viewportEndTick - m_viewportStartTick) / 2;
+ const AZStd::sys_time_t leftHistogramBound = viewportCenter - ticksPerSecond;
+ const AZStd::sys_time_t rightHistogramBound = viewportCenter + ticksPerSecond;
+
+ // Draw frame limit lines
+ drawList->AddLine(
+ { wx, wy + ImGui::GetWindowHeight() - MediumFrameTimeLimit },
+ { wx + ImGui::GetWindowWidth(), wy + ImGui::GetWindowHeight() - MediumFrameTimeLimit },
+ orange);
+
+ drawList->AddLine(
+ { wx, wy + ImGui::GetWindowHeight() - HighFrameTimeLimit },
+ { wx + ImGui::GetWindowWidth(), wy + ImGui::GetWindowHeight() - HighFrameTimeLimit },
+ red);
+
+
+ // Draw viewport bound rectangle
+ const float leftViewportPixel = ConvertTickToPixelSpace(m_viewportStartTick, leftHistogramBound, rightHistogramBound);
+ const float rightViewportPixel = ConvertTickToPixelSpace(m_viewportEndTick, leftHistogramBound, rightHistogramBound);
+ const ImVec2 topLeftPos = { leftViewportPixel, wy };
+ const ImVec2 botRightPos = { rightViewportPixel, wy + ImGui::GetWindowHeight() };
+ const ImU32 gray = ImGui::GetColorU32({ 1, 1, 1, .3 });
+ drawList->AddRectFilled(topLeftPos, botRightPos, gray);
+
+ // Find the first onscreen frame execution time
+ auto frameEndTickItr = AZStd::lower_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), leftHistogramBound);
+ if (frameEndTickItr != m_frameEndTicks.begin())
+ {
+ --frameEndTickItr;
+ }
+
+ // Since we only store the frame end ticks, we must calculate the execution times on the fly by comparing pairs of elements.
+ AZStd::sys_time_t lastFrameEndTick = *frameEndTickItr;
+ while (*frameEndTickItr < rightHistogramBound && ++frameEndTickItr != m_frameEndTicks.end())
+ {
+ const AZStd::sys_time_t frameEndTick = *frameEndTickItr;
+
+ const float framePixelPos = ConvertTickToPixelSpace(frameEndTick, leftHistogramBound, rightHistogramBound);
+ const float frameTimeMs = CpuProfilerImGuiHelper::TicksToMs(frameEndTick - lastFrameEndTick);
+
+ const ImVec2 lineBottom = { framePixelPos, ImGui::GetWindowHeight() + wy };
+ const ImVec2 lineTop = { framePixelPos, ImGui::GetWindowHeight() + wy - frameTimeMs };
+
+ ImU32 lineColor = ImGui::GetColorU32({ .3, .3, .3, 1 }); // Gray
+ if (frameTimeMs > HighFrameTimeLimit)
+ {
+ lineColor = ImGui::GetColorU32({1, 0, 0, 1}); // Red
+ }
+ else if (frameTimeMs > MediumFrameTimeLimit)
+ {
+ lineColor = ImGui::GetColorU32({1, .7, 0, 1}); // Orange
+ }
+
+ drawList->AddLine(lineBottom, lineTop, lineColor, 3.0);
+
+ lastFrameEndTick = frameEndTick;
+ }
+
+ // Handle input
+ ImGui::InvisibleButton("HistogramInputCapture", { ImGui::GetWindowWidth(), ImGui::GetWindowHeight() });
+ ImGuiIO& io = ImGui::GetIO();
+ if (ImGui::IsItemClicked(ImGuiMouseButton_Left))
+ {
+ const float mousePixelX = io.MousePos.x;
+ const float percentWindow = (mousePixelX - wx) / ImGui::GetWindowWidth();
+ const AZStd::sys_time_t newViewportCenterTick = leftHistogramBound +
+ aznumeric_cast((rightHistogramBound - leftHistogramBound) * percentWindow);
+
+ const AZStd::sys_time_t viewportWidth = GetViewportTickWidth();
+ m_viewportEndTick = newViewportCenterTick + viewportWidth / 2;
+ m_viewportStartTick = newViewportCenterTick - viewportWidth / 2;
+ }
+ }
+
inline AZStd::sys_time_t ImGuiCpuProfiler::GetViewportTickWidth() const
{
return m_viewportEndTick - m_viewportStartTick;
}
- inline float ImGuiCpuProfiler::ConvertTickToPixelSpace(AZStd::sys_time_t tick) const
+ inline float ImGuiCpuProfiler::ConvertTickToPixelSpace(AZStd::sys_time_t tick, AZStd::sys_time_t leftBound, AZStd::sys_time_t rightBound) const
{
const float wx = ImGui::GetWindowPos().x;
- const float tickSpaceShifted = aznumeric_cast(tick - m_viewportStartTick); // This will be close to zero, so FP inaccuracy should not be too bad
- const float tickSpaceNormalized = tickSpaceShifted / GetViewportTickWidth();
+ const float tickSpaceShifted = aznumeric_cast(tick - leftBound); // This will be close to zero, so FP inaccuracy should not be too bad
+ const float tickSpaceNormalized = tickSpaceShifted / (rightBound - leftBound);
const float pixelSpace = tickSpaceNormalized * ImGui::GetWindowWidth() + wx;
return pixelSpace;
}
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h
index 9286db2817..06b00a6b84 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h
@@ -101,6 +101,12 @@ namespace AZ
//! Sets if shadows should be enabled.
virtual void SetEnableShadow(bool enabled) = 0;
+
+ //! Returns the shadow bias.
+ virtual float GetShadowBias() const = 0;
+
+ //! Sets the shadow bias.
+ virtual void SetShadowBias(float bias) = 0;
//! Returns the maximum width and height of shadowmap.
virtual ShadowmapSize GetShadowmapMaxSize() const = 0;
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h
index 8a3bb337a2..b890b3e264 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h
@@ -56,6 +56,7 @@ namespace AZ
// Shadows (only used for supported shapes)
bool m_enableShadow = false;
+ float m_bias = 0.1f;
ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256;
ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None;
PcfMethod m_pcfMethod = PcfMethod::Bicubic;
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp
index c442ee39ae..b9da5ccff4 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp
@@ -18,7 +18,7 @@ namespace AZ
if (auto serializeContext = azrtti_cast(context))
{
serializeContext->Class()
- ->Version(6) // ATOM-15654
+ ->Version(7) // ATOM-16034
->Field("LightType", &AreaLightComponentConfig::m_lightType)
->Field("Color", &AreaLightComponentConfig::m_color)
->Field("IntensityMode", &AreaLightComponentConfig::m_intensityMode)
@@ -33,6 +33,7 @@ namespace AZ
->Field("OuterShutterAngleDegrees", &AreaLightComponentConfig::m_outerShutterAngleDegrees)
// Shadows
->Field("Enable Shadow", &AreaLightComponentConfig::m_enableShadow)
+ ->Field("Shadow Bias", &AreaLightComponentConfig::m_bias)
->Field("Shadowmap Max Size", &AreaLightComponentConfig::m_shadowmapMaxSize)
->Field("Shadow Filter Method", &AreaLightComponentConfig::m_shadowFilterMethod)
->Field("Softening Boundary Width", &AreaLightComponentConfig::m_boundaryWidthInDegrees)
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp
index a6bf66d20e..b90b145320 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp
@@ -68,6 +68,8 @@ namespace AZ::Render
->Event("GetEnableShadow", &AreaLightRequestBus::Events::GetEnableShadow)
->Event("SetEnableShadow", &AreaLightRequestBus::Events::SetEnableShadow)
+ ->Event("GetShadowBias", &AreaLightRequestBus::Events::GetShadowBias)
+ ->Event("SetShadowBias", &AreaLightRequestBus::Events::SetShadowBias)
->Event("GetShadowmapMaxSize", &AreaLightRequestBus::Events::GetShadowmapMaxSize)
->Event("SetShadowmapMaxSize", &AreaLightRequestBus::Events::SetShadowmapMaxSize)
->Event("GetShadowFilterMethod", &AreaLightRequestBus::Events::GetShadowFilterMethod)
@@ -94,6 +96,7 @@ namespace AZ::Render
->VirtualProperty("OuterShutterAngle", "GetOuterShutterAngle", "SetOuterShutterAngle")
->VirtualProperty("ShadowsEnabled", "GetEnableShadow", "SetEnableShadow")
+ ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias")
->VirtualProperty("ShadowmapMaxSize", "GetShadowmapMaxSize", "SetShadowmapMaxSize")
->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod")
->VirtualProperty("SofteningBoundaryWidthAngle", "GetSofteningBoundaryWidthAngle", "SetSofteningBoundaryWidthAngle")
@@ -307,6 +310,7 @@ namespace AZ::Render
m_lightShapeDelegate->SetEnableShadow(m_configuration.m_enableShadow);
if (m_configuration.m_enableShadow)
{
+ m_lightShapeDelegate->SetShadowBias(m_configuration.m_bias);
m_lightShapeDelegate->SetShadowmapMaxSize(m_configuration.m_shadowmapMaxSize);
m_lightShapeDelegate->SetShadowFilterMethod(m_configuration.m_shadowFilterMethod);
m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(m_configuration.m_boundaryWidthInDegrees);
@@ -467,6 +471,20 @@ namespace AZ::Render
m_lightShapeDelegate->SetEnableShadow(enabled);
}
}
+
+ float AreaLightComponentController::GetShadowBias() const
+ {
+ return m_configuration.m_bias;
+ }
+
+ void AreaLightComponentController::SetShadowBias(float bias)
+ {
+ m_configuration.m_bias = bias;
+ if (m_lightShapeDelegate)
+ {
+ m_lightShapeDelegate->SetShadowBias(bias);
+ }
+ }
ShadowmapSize AreaLightComponentController::GetShadowmapMaxSize() const
{
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h
index 0a835a75d0..d290beb81d 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h
@@ -76,6 +76,8 @@ namespace AZ
bool GetEnableShadow() const override;
void SetEnableShadow(bool enabled) override;
+ float GetShadowBias() const override;
+ void SetShadowBias(float bias) override;
ShadowmapSize GetShadowmapMaxSize() const override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
ShadowFilterMethod GetShadowFilterMethod() const override;
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp
index 06d0b39981..c6e4441d57 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp
@@ -123,6 +123,14 @@ namespace AZ::Render
}
}
+ void DiskLightDelegate::SetShadowBias(float bias)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
+ {
+ GetFeatureProcessor()->SetShadowBias(GetLightHandle(), bias);
+ }
+ }
+
void DiskLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h
index 53ddae669b..6931068635 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h
@@ -41,6 +41,7 @@ namespace AZ
void SetShutterAngles(float innerAngleDegrees, float outerAngleDegrees) override;
void SetEnableShadow(bool enabled) override;
+ void SetShadowBias(float bias) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp
index b58ca6448a..e45f460f18 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp
@@ -131,6 +131,15 @@ namespace AZ
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled)
+ ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_bias, "Bias", "How deep in shadow a surface must be before being affected by it.")
+ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
+ ->Attribute(Edit::Attributes::Min, 0.0f)
+ ->Attribute(Edit::Attributes::Max, 100.0f)
+ ->Attribute(Edit::Attributes::SoftMin, 0.0f)
+ ->Attribute(Edit::Attributes::SoftMax, 1.0f)
+ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
+ ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
+ ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled)
->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowFilterMethod, "Shadow filter method",
"Filtering method of edge-softening of shadows.\n"
" None: no filtering\n"
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h
index 18d88b72af..415878081c 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h
@@ -53,6 +53,7 @@ namespace AZ
void SetShutterAngles([[maybe_unused]]float innerAngleDegrees, [[maybe_unused]]float outerAngleDegrees) override {};
void SetEnableShadow(bool enabled) override { m_shadowsEnabled = enabled; };
+ void SetShadowBias([[maybe_unused]] float bias) override {};
void SetShadowmapMaxSize([[maybe_unused]] ShadowmapSize size) override {};
void SetShadowFilterMethod([[maybe_unused]] ShadowFilterMethod method) override {};
void SetSofteningBoundaryWidthAngle([[maybe_unused]] float widthInDegrees) override {};
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h
index 52f9958d40..f18c3ef9af 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h
@@ -67,8 +67,10 @@ namespace AZ
// Shadows
- //! Sets if shadows should be enabled
+ //! Sets if shadows should be enabled
virtual void SetEnableShadow(bool enabled) = 0;
+ //! Sets the shadow bias
+ virtual void SetShadowBias(float bias) = 0;
//! Sets the maximum resolution of the shadow map
virtual void SetShadowmapMaxSize(ShadowmapSize size) = 0;
//! Sets the filter method for the shadow
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp
index 1da6847269..edf08ba8c9 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp
@@ -11,121 +11,124 @@
#include
#include
-namespace AZ
+namespace AZ::Render
{
- namespace Render
+ SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible)
+ : LightDelegateBase(entityId, isVisible)
+ , m_shapeBus(shapeBus)
{
- SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible)
- : LightDelegateBase(entityId, isVisible)
- , m_shapeBus(shapeBus)
- {
- InitBase(entityId);
- }
+ InitBase(entityId);
+ }
- float SphereLightDelegate::CalculateAttenuationRadius(float lightThreshold) const
- {
- // Calculate the radius at which the irradiance will be equal to cutoffIntensity.
- float intensity = GetPhotometricValue().GetCombinedIntensity(PhotometricUnit::Lumen);
- return sqrt(intensity / lightThreshold);
- }
+ float SphereLightDelegate::CalculateAttenuationRadius(float lightThreshold) const
+ {
+ // Calculate the radius at which the irradiance will be equal to cutoffIntensity.
+ float intensity = GetPhotometricValue().GetCombinedIntensity(PhotometricUnit::Lumen);
+ return sqrt(intensity / lightThreshold);
+ }
- void SphereLightDelegate::HandleShapeChanged()
+ void SphereLightDelegate::HandleShapeChanged()
+ {
+ if (GetLightHandle().IsValid())
{
- if (GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetPosition(GetLightHandle(), GetTransform().GetTranslation());
- GetFeatureProcessor()->SetBulbRadius(GetLightHandle(), GetRadius());
- }
+ GetFeatureProcessor()->SetPosition(GetLightHandle(), GetTransform().GetTranslation());
+ GetFeatureProcessor()->SetBulbRadius(GetLightHandle(), GetRadius());
}
+ }
- float SphereLightDelegate::GetSurfaceArea() const
- {
- float radius = GetRadius();
- return 4.0f * Constants::Pi * radius * radius;
- }
+ float SphereLightDelegate::GetSurfaceArea() const
+ {
+ float radius = GetRadius();
+ return 4.0f * Constants::Pi * radius * radius;
+ }
- float SphereLightDelegate::GetRadius() const
- {
- return m_shapeBus->GetRadius() * GetTransform().GetUniformScale();
- }
+ float SphereLightDelegate::GetRadius() const
+ {
+ return m_shapeBus->GetRadius() * GetTransform().GetUniformScale();
+ }
- void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const
+ void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const
+ {
+ if (isSelected)
{
- if (isSelected)
- {
- debugDisplay.SetColor(color);
+ debugDisplay.SetColor(color);
- // Draw a sphere for the attenuation radius
- debugDisplay.DrawWireSphere(transform.GetTranslation(), GetConfig()->m_attenuationRadius);
- }
+ // Draw a sphere for the attenuation radius
+ debugDisplay.DrawWireSphere(transform.GetTranslation(), GetConfig()->m_attenuationRadius);
}
+ }
- void SphereLightDelegate::SetEnableShadow(bool enabled)
+ void SphereLightDelegate::SetEnableShadow(bool enabled)
+ {
+ Base::SetEnableShadow(enabled);
+
+ if (GetLightHandle().IsValid())
{
- Base::SetEnableShadow(enabled);
-
- if (GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled);
- }
+ GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled);
}
-
- void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
+ }
+
+ void SphereLightDelegate::SetShadowBias(float bias)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size);
- }
+ GetFeatureProcessor()->SetShadowBias(GetLightHandle(), bias);
}
+ }
- void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method)
+ void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method);
- }
+ GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size);
}
+ }
- void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
+ void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
- }
+ GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method);
}
+ }
- void SphereLightDelegate::SetPredictionSampleCount(uint32_t count)
+ void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count);
- }
+ GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
}
+ }
- void SphereLightDelegate::SetFilteringSampleCount(uint32_t count)
+ void SphereLightDelegate::SetPredictionSampleCount(uint32_t count)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count);
- }
+ GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count);
}
+ }
- void SphereLightDelegate::SetPcfMethod(PcfMethod method)
+ void SphereLightDelegate::SetFilteringSampleCount(uint32_t count)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method);
- }
+ GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count);
}
+ }
- void SphereLightDelegate::SetEsmExponent(float esmExponent)
+ void SphereLightDelegate::SetPcfMethod(PcfMethod method)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
- if (GetShadowsEnabled() && GetLightHandle().IsValid())
- {
- GetFeatureProcessor()->SetEsmExponent(GetLightHandle(), esmExponent);
- }
+ GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method);
}
+ }
-
- } // namespace Render
-} // namespace AZ
+ void SphereLightDelegate::SetEsmExponent(float esmExponent)
+ {
+ if (GetShadowsEnabled() && GetLightHandle().IsValid())
+ {
+ GetFeatureProcessor()->SetEsmExponent(GetLightHandle(), esmExponent);
+ }
+ }
+} // namespace AZ::Render
diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h
index 6dd872d693..984af56c17 100644
--- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h
+++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h
@@ -31,6 +31,7 @@ namespace AZ
float GetSurfaceArea() const override;
float GetEffectiveSolidAngle() const override { return PhotometricValue::OmnidirectionalSteradians; }
void SetEnableShadow(bool enabled) override;
+ void SetShadowBias(float bias) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json
index dc5f968bc7..699ed8419a 100644
--- a/Gems/AudioEngineWwise/gem.json
+++ b/Gems/AudioEngineWwise/gem.json
@@ -8,5 +8,5 @@
"canonical_tags": ["Gem"],
"user_tags": ["Audio", "Utility", "Tools"],
"icon_path": "preview.png",
- "requirements": "Users will need to download WWise from the AudioKinetic web site: https://www.audiokinetic.com/download/"
+ "requirements": "Users will need to download Wwise from the Audiokinetic web site: https://www.audiokinetic.com/download/"
}
diff --git a/Gems/EMotionFX/Code/CMakeLists.txt b/Gems/EMotionFX/Code/CMakeLists.txt
index 5c38faf2a6..ed5447ba25 100644
--- a/Gems/EMotionFX/Code/CMakeLists.txt
+++ b/Gems/EMotionFX/Code/CMakeLists.txt
@@ -219,8 +219,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExampleNoDependency.animgraph
${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyAnimGraphExample.animgraph
${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyMotionSetExample.motionset
- ${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/LegacyAnimGraphExample.animgraph
- ${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/LegacyMotionSetExample.motionset
${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset
${CMAKE_CURRENT_SOURCE_DIR}/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExampleNoDependency.motionset
OUTPUT_SUBDIRECTORY
diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp
index a04f6a327d..fdc4e4d0e5 100644
--- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp
@@ -93,9 +93,7 @@ namespace CommandSystem
}
// load anim graph from file
- EMotionFX::Importer::AnimGraphSettings settings;
- settings.mDisableNodeVisualization = false;
- EMotionFX::AnimGraph* animGraph = EMotionFX::GetImporter().LoadAnimGraph(filename.c_str(), &settings);
+ EMotionFX::AnimGraph* animGraph = EMotionFX::GetImporter().LoadAnimGraph(filename.c_str());
if (!animGraph)
{
outResult = AZStd::string::format("Failed to load anim graph from %s.", filename.c_str());
diff --git a/Gems/EMotionFX/Code/EMotionFX/Pipeline/EMotionFXBuilder/AnimGraphBuilderWorker.cpp b/Gems/EMotionFX/Code/EMotionFX/Pipeline/EMotionFXBuilder/AnimGraphBuilderWorker.cpp
index 927ba67b76..cd81d81170 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Pipeline/EMotionFXBuilder/AnimGraphBuilderWorker.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/Pipeline/EMotionFXBuilder/AnimGraphBuilderWorker.cpp
@@ -97,7 +97,7 @@ namespace EMotionFX
AZ_UNUSED(sourceFile);
AZ::ObjectStream::FilterDescriptor loadFilter = AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES);
- AZStd::unique_ptr animGraph(GetImporter().LoadAnimGraph(fullPath, nullptr, loadFilter));
+ AZStd::unique_ptr animGraph(GetImporter().LoadAnimGraph(fullPath, loadFilter));
if (!animGraph)
{
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphAttributeTypes.h b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphAttributeTypes.h
index 9514ffdb20..9f7b33a66d 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphAttributeTypes.h
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphAttributeTypes.h
@@ -79,9 +79,6 @@ namespace EMotionFX
AttributePose(AnimGraphPose* pose)
: MCore::Attribute(TYPE_ID) { mValue = pose; }
~AttributePose() {}
-
- uint32 GetDataSize() const override { return 0; }
- bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override { MCORE_UNUSED(stream); MCORE_UNUSED(streamEndianType); MCORE_UNUSED(version); return false; } // unsupported
};
@@ -130,9 +127,6 @@ namespace EMotionFX
AttributeMotionInstance(MotionInstance* motionInstance)
: MCore::Attribute(TYPE_ID) { mValue = motionInstance; }
~AttributeMotionInstance() {}
-
- uint32 GetDataSize() const override { return 0; }
- bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override { MCORE_UNUSED(stream); MCORE_UNUSED(streamEndianType); MCORE_UNUSED(version); return false; } // unsupported
};
class AnimGraphPropertyUtils
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/AnimGraphFileFormat.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/AnimGraphFileFormat.cpp
deleted file mode 100644
index f79057dfa8..0000000000
--- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/AnimGraphFileFormat.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-
-#include "AnimGraphFileFormat.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace EMotionFX
-{
- namespace FileFormat
- {
- AZ::TypeId GetParameterTypeIdForInterfaceType(uint32 interfaceType)
- {
- switch (interfaceType)
- {
- case MCore::ATTRIBUTE_INTERFACETYPE_FLOATSPINNER:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_FLOATSLIDER:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_INTSPINNER:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_INTSLIDER:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_CHECKBOX:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR2:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3GIZMO:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR4:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_COLOR:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_STRING:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3:
- return azrtti_typeid();
- case MCore::ATTRIBUTE_INTERFACETYPE_TAG:
- return azrtti_typeid();
-
- case MCore::ATTRIBUTE_INTERFACETYPE_COMBOBOX:
- case MCore::ATTRIBUTE_INTERFACETYPE_PROPERTYSET:
- case MCore::ATTRIBUTE_INTERFACETYPE_DEFAULT:
- default:
- break;
- }
- return AZ::TypeId();
- }
-
- uint32 GetInterfaceTypeForParameterTypeId(const AZ::TypeId& parameterTypeId)
- {
- if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_FLOATSPINNER;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_FLOATSLIDER;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_INTSPINNER;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_INTSLIDER;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_CHECKBOX;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR2;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3GIZMO;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR4;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_COLOR;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_STRING;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3;
- }
- else if (parameterTypeId == azrtti_typeid())
- {
- return MCore::ATTRIBUTE_INTERFACETYPE_TAG;
- }
- return MCORE_INVALIDINDEX32;
- }
- }
-} // namespace EMotionFX
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/AnimGraphFileFormat.h b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/AnimGraphFileFormat.h
deleted file mode 100644
index 3b88c3605f..0000000000
--- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/AnimGraphFileFormat.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-
-#pragma once
-
-#include "SharedFileFormatStructs.h"
-#include "AzCore/RTTI/TypeInfo.h"
-
-namespace EMotionFX
-{
- namespace FileFormat // so now we are in the namespace EMotionFX::FileFormat
- {
- // collection of animGraph chunk IDs
- enum
- {
- ANIMGRAPH_CHUNK_BLENDNODE = 400,
- ANIMGRAPH_CHUNK_STATETRANSITIONS = 401,
- ANIMGRAPH_CHUNK_NODECONNECTIONS = 402,
- ANIMGRAPH_CHUNK_PARAMETERS = 403,
- ANIMGRAPH_CHUNK_NODEGROUPS = 404,
- ANIMGRAPH_CHUNK_GROUPPARAMETERS = 405,
- ANIMGRAPH_CHUNK_GAMECONTROLLERSETTINGS = 406,
- ANIMGRAPH_CHUNK_ADDITIONALINFO = 407,
- ANIMGRAPH_FORCE_32BIT = 0xFFFFFFFF
- };
-
- enum
- {
- ANIMGRAPH_NODEFLAG_COLLAPSED = 1 << 0,
- ANIMGRAPH_NODEFLAG_VISUALIZED = 1 << 1,
- ANIMGRAPH_NODEFLAG_DISABLED = 1 << 2,
- ANIMGRAPH_NODEFLAG_VIRTUALFINALOUTPUT = 1 << 3
- };
-
- /*
- AnimGraph_Header
-
- ANIMGRAPH_CHUNK_PARAMETERS: (global animgraph parameters)
- uint32 numParameters
- AnimGraph_ParamInfo[numParameters]
-
- ANIMGRAPH_CHUNK_BLENDNODE:
- AnimGraph_NodeHeader
-
- ANIMGRAPH_CHUNK_NODECONNECTIONS: (for last loaded BLENDNODE)
- uint32 numConnections
- AnimGraph_NodeConnection[numConnections]
-
- ANIMGRAPH_CHUNK_STATETRANSITIONS: (for last loaded node, assumed to be a state machine)
- uint32 numStateTransitions
- uint32 blendNodeIndex (the state machine the transitions are for)
- AnimGraph_StateTransition[numStateTransitions]
-
- ANIMGRAPH_CHUNK_NODEGROUPS:
- uint32 numNodeGroups
- AnimGraph_NodeGroup[numNodeGroups]
-
- ANIMGRAPH_CHUNK_GAMECONTROLLERSETTINGS:
- uint32 activePresetIndex
- uint32 numPresets
- AnimGraph_GameControllerPreset[numPresets]
- */
-
- // AnimGraph file header
- struct AnimGraph_Header
- {
- char mFourCC[4];
- uint8 mEndianType;
- uint32 mFileVersion;
- uint32 mNumNodes;
- uint32 mNumStateTransitions;
- uint32 mNumNodeConnections;
- uint32 mNumParameters;
-
- // followed by:
- // string mName;
- // string mCopyright;
- // string mDescription;
- // string mCompany;
- // string mEMFXVersion;
- // string mEMStudioBuildDate;
- };
-
- // additional info
- struct AnimGraph_AdditionalInfo
- {
- uint8 mUnitType;
- };
-
-
- // the node header
- struct AnimGraph_NodeHeader
- {
- uint32 mTypeID;
- uint32 mParentIndex;
- uint32 mVersion;
- uint32 mNumCustomDataBytes; // number of bytes of node custom data to follow
- uint32 mNumChildNodes;
- uint32 mNumAttributes;
- int32 mVisualPosX;
- int32 mVisualPosY;
- uint32 mVisualizeColor;
- uint8 mFlags;
-
- // followed by:
- // string mName;
- // animGraphNode->Save(...) or animGraphNode->Load(...), writing or reading mNumBytes bytes
- };
-
-
- struct AnimGraph_ParameterInfo
- {
- uint32 mNumComboValues;
- uint32 mInterfaceType;
- uint32 mAttributeType;
- uint16 mFlags;
- char mHasMinMax;
-
- // followed by:
- // string mName
- // string mInternalName
- // string mDescription
- // if (mHasMinMax == 1)
- // {
- // AnimGraph_Attribute mMinValue
- // AnimGraph_Attribute mMaxValue
- // }
- // AnimGraph_Attribute mDefaultValue
- // string mComboValues[mNumComboValues]
- };
-
-
- // a node connection
- struct AnimGraph_NodeConnection
- {
- uint32 mSourceNode;
- uint32 mTargetNode;
- uint16 mSourceNodePort;
- uint16 mTargetNodePort;
- };
-
-
- // a state transition
- struct AnimGraph_StateTransition
- {
- uint32 mSourceNode;
- uint32 mDestNode;
- int32 mStartOffsetX;
- int32 mStartOffsetY;
- int32 mEndOffsetX;
- int32 mEndOffsetY;
- uint32 mNumConditions;
-
- // followed by:
- // AnimGraph_NodeHeader (and its followed by data, EXCEPT THE NAME STRING, which is skipped)
- // AnimGraph_NodeHeader[mConditions] (and its followed by data, EXCEPT THE NAME STRING, which is skipped)
- };
-
-
- // a node group
- struct AnimGraph_NodeGroup
- {
- FileColor mColor;
- uint8 mIsVisible;
- uint32 mNumNodes;
-
- // followed by:
- // string mName
- // uint32[mNumNodes] (node indices that belong to the group)
- };
-
-
- // a group parameter
- struct AnimGraph_GroupParameter
- {
- uint32 mNumParameters;
- uint8 mCollapsed;
-
- // followed by:
- // string mName
- // uint32[mNumParameters] (parameter indices that belong to the group)
- };
-
-
- // a game controller parameter info
- struct AnimGraph_GameControllerParameterInfo
- {
- uint8 mAxis;
- uint8 mMode;
- uint8 mInvert;
-
- // followed by:
- // string mName
- };
-
-
- // a game controller button info
- struct AnimGraph_GameControllerButtonInfo
- {
- uint8 mButtonIndex;
- uint8 mMode;
-
- // followed by:
- // string mString
- };
-
-
- // a game controller preset
- struct AnimGraph_GameControllerPreset
- {
- uint32 mNumParameterInfos;
- uint32 mNumButtonInfos;
-
- // followed by:
- // string mName
- // AnimGraph_GameControllerParameterInfo[mNumParameterInfos]
- // AnimGraph_GameControllerButtonInfo[mNumButtonInfos]
- };
-
- // Conversion functions to support attributes with the old serialization.
- // Once we deprecate the old format we can remove these two functions.
- AZ::TypeId GetParameterTypeIdForInterfaceType(uint32 interfaceType);
- uint32 GetInterfaceTypeForParameterTypeId(const AZ::TypeId& parameterTypeId);
-
- } // namespace FileFormat
-} // namespace EMotionFX
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp
index 87ab61bf59..e1a3e0bbfa 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -53,7 +54,6 @@
#include "../AnimGraphTransitionCondition.h"
#include "../MCore/Source/Endian.h"
#include "../NodeMap.h"
-#include "LegacyAnimGraphNodeParser.h"
#include
#include
@@ -245,14 +245,10 @@ namespace EMotionFX
{
mFileHighVersion = 1;
mFileLowVersion = 0;
- mIsUnicodeFile = true;
// allocate the string buffer used for reading in variable sized strings
mStringStorageSize = 256;
mStringStorage = (char*)MCore::Allocate(mStringStorageSize, EMFX_MEMCATEGORY_IMPORTER);
- mBlendNodes.SetMemoryCategory(EMFX_MEMCATEGORY_IMPORTER);
- mBlendNodes.Reserve(1024);
- //mConvertString.Reserve( 256 );
}
@@ -281,57 +277,8 @@ namespace EMotionFX
mStringStorage = nullptr;
mStringStorageSize = 0;
- //mConvertString.Clear();
-
- // get rid of the blend nodes array
- mBlendNodes.Clear();
-
- m_entryNodeIndexToStateMachineIdLookupTable.clear();
}
-
- // check if the strings in the file are encoded using unicode or multi-byte
- bool SharedHelperData::GetIsUnicodeFile(const char* dateString, MCore::Array* sharedData)
- {
- // find the helper data
- SharedData* data = Importer::FindSharedData(sharedData, SharedHelperData::TYPE_ID);
- SharedHelperData* helperData = static_cast(data);
-
- AZStd::vector dateParts;
- AzFramework::StringFunc::Tokenize(dateString, dateParts, MCore::CharacterConstants::space, false /* keep empty strings */, true /* keep space strings */);
-
- // decode the month
- int32 month = 0;
- const AZStd::string& monthString = dateParts[0];
- const char* monthStrings[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
- for (int32 i = 0; i < 12; ++i)
- {
- if (monthString == monthStrings[i])
- {
- month = i + 1;
- break;
- }
- }
-
- //int32 day = dateParts[1].ToInt();
- int32 year;
- if (!AzFramework::StringFunc::LooksLikeInt(dateParts[2].c_str(), &year))
- {
- return false;
- }
-
- // set if the file contains unicode strings or not based on the compilcation date
- if (year < 2012 || (year == 2012 && month < 11))
- {
- helperData->mIsUnicodeFile = false;
- }
-
- //LogInfo( "String: '%s', Decoded: %i.%i.%i - isUnicode=%i", dateString, day, month, year, helperData->mIsUnicodeFile );
-
- return helperData->mIsUnicodeFile;
- }
-
-
const char* SharedHelperData::ReadString(MCore::Stream* file, MCore::Array* sharedData, MCore::Endian::EEndianType endianType)
{
MCORE_ASSERT(file);
@@ -366,25 +313,6 @@ namespace EMotionFX
return helperData->mStringStorage;
}
-
- // get the array of anim graph nodes
- MCore::Array& SharedHelperData::GetBlendNodes(MCore::Array* sharedData)
- {
- // find the helper data
- SharedData* data = Importer::FindSharedData(sharedData, SharedHelperData::TYPE_ID);
- SharedHelperData* helperData = static_cast(data);
- return helperData->mBlendNodes;
- }
-
- // Get the table of entry state indices to state machines IDs
- AZStd::map& SharedHelperData::GetEntryStateToStateMachineTable(MCore::Array* sharedData)
- {
- // Find the helper data
- SharedData* data = Importer::FindSharedData(sharedData, SharedHelperData::TYPE_ID);
- SharedHelperData* helperData = static_cast(data);
- return helperData->m_entryNodeIndexToStateMachineIdLookupTable;
- }
-
//-----------------------------------------------------------------------------
// constructor
@@ -1999,7 +1927,6 @@ namespace EMotionFX
//----------------------------------------------------------------------------------------------------------
- //
bool ChunkProcessorActorAttachmentNodes::Process(MCore::File* file, Importer::ImportParameters& importParams)
{
const MCore::Endian::EEndianType endianType = importParams.mEndianType;
@@ -2055,866 +1982,6 @@ namespace EMotionFX
return true;
}
-
- //----------------------------------------------------------------------------------------------------------
-
- // animGraph state transitions
- bool ChunkProcessorAnimGraphStateTransitions::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- // read the number of transitions to follow
- uint32 numTransitions;
- file->Read(&numTransitions, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&numTransitions, importParams.mEndianType);
-
- // read the state machine index
- uint32 stateMachineIndex;
- file->Read(&stateMachineIndex, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&stateMachineIndex, importParams.mEndianType);
-
- // get the loaded anim graph nodes
- MCore::Array& blendNodes = SharedHelperData::GetBlendNodes(importParams.mSharedData);
- if (stateMachineIndex >= blendNodes.GetLength())
- {
- if (GetLogging())
- {
- AZ_Error("EMotionFX", false, "State machine refers to invalid blend node, state machine index: %d, amount of blend node: %d", stateMachineIndex, blendNodes.GetLength());
- }
- return false;
- }
- AZ_Assert(azrtti_typeid(blendNodes[stateMachineIndex]) == azrtti_typeid(), "ChunkProcessorAnimGraphStateTransitions::Process : Unexpected node type expected AnimGraphStateMachine. Found %u instead", azrtti_typeid(blendNodes[stateMachineIndex]));
- AnimGraphStateMachine* stateMachine = static_cast(blendNodes[stateMachineIndex]);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Num transitions for state machine '%s' = %d", blendNodes[stateMachineIndex]->GetName(), numTransitions);
- }
-
- stateMachine->ReserveTransitions(numTransitions);
-
- // read the transitions
- FileFormat::AnimGraph_StateTransition transition;
- for (uint32 i = 0; i < numTransitions; ++i)
- {
- // read the transition
- file->Read(&transition, sizeof(FileFormat::AnimGraph_StateTransition));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&transition.mSourceNode, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&transition.mDestNode, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&transition.mNumConditions, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&transition.mStartOffsetX, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&transition.mStartOffsetY, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&transition.mEndOffsetX, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&transition.mEndOffsetY, importParams.mEndianType);
-
- //----------------------------------------------
- // read the node header
- FileFormat::AnimGraph_NodeHeader nodeHeader;
- file->Read(&nodeHeader, sizeof(FileFormat::AnimGraph_NodeHeader));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mTypeID, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mParentIndex, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mVersion, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mNumCustomDataBytes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mNumChildNodes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mNumAttributes, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&nodeHeader.mVisualPosX, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&nodeHeader.mVisualPosY, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mVisualizeColor, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- State Transition Node:");
- MCore::LogDetailedInfo(" + Type = %d", nodeHeader.mTypeID);
- MCore::LogDetailedInfo(" + Version = %d", nodeHeader.mVersion);
- MCore::LogDetailedInfo(" + Num data bytes = %d", nodeHeader.mNumCustomDataBytes);
- MCore::LogDetailedInfo(" + Num attributes = %d", nodeHeader.mNumAttributes);
- MCore::LogDetailedInfo(" + Num conditions = %d", transition.mNumConditions);
- MCore::LogDetailedInfo(" + Source node = %d", transition.mSourceNode);
- MCore::LogDetailedInfo(" + Dest node = %d", transition.mDestNode);
- }
-
- // create the transition object
- AnimGraphStateTransition* emfxTransition = nullptr;
- if (GetNewTypeIdByOldNodeTypeId(nodeHeader.mTypeID) == azrtti_typeid())
- {
- emfxTransition = aznew AnimGraphStateTransition();
- }
-
- if (emfxTransition)
- {
- if (transition.mDestNode >= blendNodes.GetLength())
- {
- if (GetLogging())
- {
- AZ_Error("EMotionFX", false, "State machine transition refers to invalid destination blend node, transition index %d, blend node: %d", i, transition.mDestNode);
- }
- delete emfxTransition;
- emfxTransition = nullptr;
- }
- // A source node index of MCORE_INVALIDINDEX32 indicates that the transition is a wildcard transition. Don't go into error state in this case.
- else if (transition.mSourceNode != MCORE_INVALIDINDEX32 && transition.mSourceNode >= blendNodes.GetLength())
- {
- if (GetLogging())
- {
- AZ_Error("EMotionFX", false, "State machine transition refers to invalid source blend node, transition index %d, blend node: %d", i, transition.mSourceNode);
- }
- delete emfxTransition;
- emfxTransition = nullptr;
- }
- else
- {
- AnimGraphNode* targetNode = blendNodes[transition.mDestNode];
- if (targetNode == nullptr)
- {
- delete emfxTransition;
- emfxTransition = nullptr;
- }
- else
- {
- AZ_Assert(azrtti_istypeof(emfxTransition), "ChunkProcessorAnimGraphStateTransitions::Process : Unexpected node type expected AnimGraphStateTransition. Found %u instead", azrtti_typeid(blendNodes[stateMachineIndex]));
-
- // Now apply the transition settings
- // Check if we are dealing with a wildcard transition
- if (transition.mSourceNode == MCORE_INVALIDINDEX32)
- {
- emfxTransition->SetSourceNode(nullptr);
- emfxTransition->SetIsWildcardTransition(true);
- }
- else
- {
- // set the source node
- emfxTransition->SetSourceNode(blendNodes[transition.mSourceNode]);
- }
-
- // set the destination node
- emfxTransition->SetTargetNode(targetNode);
-
- emfxTransition->SetVisualOffsets(transition.mStartOffsetX, transition.mStartOffsetY, transition.mEndOffsetX, transition.mEndOffsetY);
-
- // now read the attributes
- if (!LegacyAnimGraphNodeParser::ParseLegacyAttributes(file, nodeHeader.mNumAttributes, importParams.mEndianType, importParams, *emfxTransition))
- {
- delete emfxTransition;
- emfxTransition = nullptr;
- AZ_Error("EMotionFX", false, "Unable to parse state transition");
- return false;
- }
- // add the transition to the state machine
- stateMachine->AddTransition(emfxTransition);
- }
- }
- }
-
- if (emfxTransition)
- {
- // iterate through all conditions
- for (uint32 c = 0; c < transition.mNumConditions; ++c)
- {
- // read the condition node header
- FileFormat::AnimGraph_NodeHeader conditionHeader;
- file->Read(&conditionHeader, sizeof(FileFormat::AnimGraph_NodeHeader));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mTypeID, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mVersion, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mNumCustomDataBytes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mNumAttributes, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo(" - Transition Condition:");
- MCore::LogDetailedInfo(" + Type = %d", conditionHeader.mTypeID);
- MCore::LogDetailedInfo(" + Version = %d", conditionHeader.mVersion);
- MCore::LogDetailedInfo(" + Num data bytes = %d", conditionHeader.mNumCustomDataBytes);
- MCore::LogDetailedInfo(" + Num attributes = %d", conditionHeader.mNumAttributes);
- }
-
- AnimGraphTransitionCondition* emfxCondition = nullptr;
- if (!LegacyAnimGraphNodeParser::ParseTransitionConditionChunk(file, importParams, conditionHeader, emfxCondition))
- {
- AZ_Error("EMotionFX", false, "Unable to parse Transition condition of type %u in legacy file", azrtti_typeid(emfxCondition));
- delete emfxCondition;
- emfxCondition = nullptr;
- return false;
- }
- // add the condition to the transition
- emfxTransition->AddCondition(emfxCondition);
- }
-
- //emfxTransition->Init( animGraph );
- }
- // something went wrong with creating the transition
- else
- {
- MCore::LogWarning("Cannot load and instantiate state transition. State transition from %d to %d will be skipped.", transition.mSourceNode, transition.mDestNode);
-
- // skip reading the attributes
- if (!ForwardAttributes(file, importParams.mEndianType, nodeHeader.mNumAttributes))
- {
- return false;
- }
-
- // skip reading the node custom data
- if (file->Forward(nodeHeader.mNumCustomDataBytes) == false)
- {
- return false;
- }
-
- // iterate through all conditions and skip them as well
- for (uint32 c = 0; c < transition.mNumConditions; ++c)
- {
- // read the condition node header
- FileFormat::AnimGraph_NodeHeader conditionHeader;
- file->Read(&conditionHeader, sizeof(FileFormat::AnimGraph_NodeHeader));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mTypeID, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mVersion, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mNumCustomDataBytes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&conditionHeader.mNumAttributes, importParams.mEndianType);
-
- // skip reading the attributes
- if (!ForwardAttributes(file, importParams.mEndianType, conditionHeader.mNumAttributes))
- {
- return false;
- }
-
- // skip reading the node custom data
- if (file->Forward(conditionHeader.mNumCustomDataBytes) == false)
- {
- return false;
- }
- }
- }
- }
-
- return true;
- }
-
- //----------------------------------------------------------------------------------------------------------
-
- // animGraph state transitions
- bool ChunkProcessorAnimGraphAdditionalInfo::Process(MCore::File* file, Importer::ImportParameters& /*importParams*/)
- {
- return file->Forward(sizeof(FileFormat::AnimGraph_AdditionalInfo));
- }
-
- //----------------------------------------------------------------------------------------------------------
-
- // animGraph node connections
- bool ChunkProcessorAnimGraphNodeConnections::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- // read the number of transitions to follow
- uint32 numConnections;
- file->Read(&numConnections, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&numConnections, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Num node connections = %d", numConnections);
- }
-
- // get the array of currently loaded nodes
- MCore::Array& blendNodes = SharedHelperData::GetBlendNodes(importParams.mSharedData);
-
- // read the connections
- FileFormat::AnimGraph_NodeConnection connection;
- for (uint32 i = 0; i < numConnections; ++i)
- {
- // read the transition
- file->Read(&connection, sizeof(FileFormat::AnimGraph_NodeConnection));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&connection.mSourceNode, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&connection.mTargetNode, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt16(&connection.mSourceNodePort, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt16(&connection.mTargetNodePort, importParams.mEndianType);
-
- // log details
- if (GetLogging())
- {
- MCore::LogDetailedInfo(" + Connection #%d = From node %d (port id %d) into node %d (port id %d)", i, connection.mSourceNode, connection.mSourceNodePort, connection.mTargetNode, connection.mTargetNodePort);
- }
-
- // get the source and the target node and check if they are valid
- AnimGraphNode* sourceNode = blendNodes[connection.mSourceNode];
- AnimGraphNode* targetNode = blendNodes[connection.mTargetNode];
- if (sourceNode == nullptr || targetNode == nullptr)
- {
- MCore::LogWarning("EMotionFX::ChunkProcessorAnimGraphNodeConnections() - Connection cannot be created because the source or target node is invalid! (sourcePortID=%d targetPortID=%d sourceNode=%d targetNode=%d)", connection.mSourceNodePort, connection.mTargetNodePort, connection.mSourceNode, connection.mTargetNode);
- continue;
- }
-
- // create the connection
- const uint32 sourcePort = blendNodes[connection.mSourceNode]->FindOutputPortByID(connection.mSourceNodePort);
- const uint32 targetPort = blendNodes[connection.mTargetNode]->FindInputPortByID(connection.mTargetNodePort);
- if (sourcePort != MCORE_INVALIDINDEX32 && targetPort != MCORE_INVALIDINDEX32)
- {
- blendNodes[connection.mTargetNode]->AddConnection(blendNodes[connection.mSourceNode], static_cast(sourcePort), static_cast(targetPort));
- }
- else
- {
- MCore::LogWarning("EMotionFX::ChunkProcessorAnimGraphNodeConnections() - Connection cannot be created because the source or target port doesn't exist! (sourcePortID=%d targetPortID=%d sourceNode='%s' targetNode=%s')", connection.mSourceNodePort, connection.mTargetNodePort, blendNodes[connection.mSourceNode]->GetName(), blendNodes[connection.mTargetNode]->GetName());
- }
- }
-
- return true;
- }
-
- //----------------------------------------------------------------------------------------------------------
-
- // animGraph node
- bool ChunkProcessorAnimGraphNode::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- AnimGraph* animGraph = importParams.mAnimGraph;
- MCORE_ASSERT(animGraph);
-
- // read the node header
- FileFormat::AnimGraph_NodeHeader nodeHeader;
- file->Read(&nodeHeader, sizeof(FileFormat::AnimGraph_NodeHeader));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mTypeID, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mParentIndex, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mVersion, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mNumCustomDataBytes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mNumChildNodes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mNumAttributes, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&nodeHeader.mVisualizeColor, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&nodeHeader.mVisualPosX, importParams.mEndianType);
- MCore::Endian::ConvertSignedInt32(&nodeHeader.mVisualPosY, importParams.mEndianType);
-
- const char* nodeName = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Blend Node:");
- MCore::LogDetailedInfo(" + Name = %s", nodeName);
- MCore::LogDetailedInfo(" + Parent index = %d", nodeHeader.mParentIndex);
- MCore::LogDetailedInfo(" + Type = %d", nodeHeader.mTypeID);
- MCore::LogDetailedInfo(" + Version = %d", nodeHeader.mVersion);
- MCore::LogDetailedInfo(" + Num data bytes = %d", nodeHeader.mNumCustomDataBytes);
- MCore::LogDetailedInfo(" + Num child nodes = %d", nodeHeader.mNumChildNodes);
- MCore::LogDetailedInfo(" + Num attributes = %d", nodeHeader.mNumAttributes);
- MCore::LogDetailedInfo(" + Visualize Color = %d, %d, %d", MCore::ExtractRed(nodeHeader.mVisualizeColor), MCore::ExtractGreen(nodeHeader.mVisualizeColor), MCore::ExtractBlue(nodeHeader.mVisualizeColor));
- MCore::LogDetailedInfo(" + Visual pos = (%d, %d)", nodeHeader.mVisualPosX, nodeHeader.mVisualPosY);
- MCore::LogDetailedInfo(" + Collapsed = %s", (nodeHeader.mFlags & FileFormat::ANIMGRAPH_NODEFLAG_COLLAPSED) ? "Yes" : "No");
- MCore::LogDetailedInfo(" + Visualized = %s", (nodeHeader.mFlags & FileFormat::ANIMGRAPH_NODEFLAG_VISUALIZED) ? "Yes" : "No");
- MCore::LogDetailedInfo(" + Disabled = %s", (nodeHeader.mFlags & FileFormat::ANIMGRAPH_NODEFLAG_DISABLED) ? "Yes" : "No");
- MCore::LogDetailedInfo(" + Virtual FinalOut= %s", (nodeHeader.mFlags & FileFormat::ANIMGRAPH_NODEFLAG_VIRTUALFINALOUTPUT) ? "Yes" : "No");
- }
-
- AnimGraphNode* node = nullptr;
- if (!LegacyAnimGraphNodeParser::ParseAnimGraphNodeChunk(file
- , importParams
- , nodeName
- , nodeHeader
- , node))
- {
- if (importParams.mAnimGraph->GetRootStateMachine() == node)
- {
- importParams.mAnimGraph->SetRootStateMachine(nullptr);
- }
-
- if (node)
- {
- AnimGraphNode* parentNode = node->GetParentNode();
- if (parentNode)
- {
- parentNode->RemoveChildNodeByPointer(node, false);
- }
- }
-
- delete node;
- node = nullptr;
- return false;
- }
-
- EMotionFX::GetEventManager().OnCreatedNode(animGraph, node);
-
- return true;
- }
-
- //----------------------------------------------------------------------------------------------------------
-
- // animGraph parameters
- bool ChunkProcessorAnimGraphParameters::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- AnimGraph* animGraph = importParams.mAnimGraph;
- MCORE_ASSERT(animGraph);
-
- // read the number of parameters
- uint32 numParams;
- file->Read(&numParams, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&numParams, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Num parameters = %d", numParams);
- }
-
- // read all parameters
- for (uint32 p = 0; p < numParams; ++p)
- {
- // read the parameter info header
- FileFormat::AnimGraph_ParameterInfo paramInfo;
- file->Read(¶mInfo, sizeof(FileFormat::AnimGraph_ParameterInfo));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(¶mInfo.mNumComboValues, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(¶mInfo.mInterfaceType, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(¶mInfo.mAttributeType, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt16(¶mInfo.mFlags, importParams.mEndianType);
-
- // check the attribute type
- const uint32 attribType = paramInfo.mAttributeType;
- if (attribType == 0)
- {
- MCore::LogError("EMotionFX::ChunkProcessorAnimGraphParameters::Process() - Failed to convert interface type %d to an attribute type.", attribType);
- return false;
- }
-
- const AZ::TypeId parameterTypeId = EMotionFX::FileFormat::GetParameterTypeIdForInterfaceType(paramInfo.mInterfaceType);
- const AZStd::string name = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
- AZStd::unique_ptr newParam(EMotionFX::ParameterFactory::Create(parameterTypeId));
- AZ_Assert(azrtti_istypeof(newParam.get()), "Expected a value parameter");
-
- if (!newParam)
- {
- MCore::LogError("EMotionFX::ChunkProcessorAnimGraphParameters::Process() - Failed to create parameter: '%s'.", name.c_str());
- return false;
- }
-
- // read the strings
- newParam->SetName(name);
- SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType); // We dont use internal name anymore
- newParam->SetDescription(SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType));
-
- // log the details
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Parameter #%d:", p);
- MCore::LogDetailedInfo(" + Name = %s", newParam->GetName().c_str());
- MCore::LogDetailedInfo(" + Description = %s", newParam->GetDescription().c_str());
- MCore::LogDetailedInfo(" + type = %s", newParam->RTTI_GetTypeName());
- MCore::LogDetailedInfo(" + Attribute type = %d", paramInfo.mAttributeType);
- MCore::LogDetailedInfo(" + Has MinMax = %d", paramInfo.mHasMinMax);
- MCore::LogDetailedInfo(" + Flags = %d", paramInfo.mFlags);
- }
-
- MCore::Attribute* attr(MCore::GetAttributeFactory().CreateAttributeByType(attribType));
- EMotionFX::ValueParameter* valueParameter = static_cast(newParam.get());
-
- // create the min, max and default value attributes
- if (paramInfo.mHasMinMax == 1)
- {
- // min value
- attr->Read(file, importParams.mEndianType);
- valueParameter->SetMinValueFromAttribute(attr);
-
- // max value
- attr->Read(file, importParams.mEndianType);
- valueParameter->SetMaxValueFromAttribute(attr);
- }
-
- // default value
- attr->Read(file, importParams.mEndianType);
- valueParameter->SetDefaultValueFromAttribute(attr);
- delete attr;
-
- // Parameters were previously stored in "AttributeSettings". The calss supported
- // multiple values, however, the UI did not, so this ended up not being used.
- // Support for multiple values in parameters is possible, however we dont need it now.
- // Leaving this code as reference
- for (uint32 i = 0; i < paramInfo.mNumComboValues; ++i)
- {
- SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
- }
-
- if (!animGraph->AddParameter(newParam.get()))
- {
- MCore::LogError("EMotionFX::ChunkProcessorAnimGraphParameters::Process() - Failed to add parameter: '%s'.", name.c_str());
- return false;
- }
- newParam.release(); // ownership moved to animGraph
- }
-
- return true;
- }
-
-
- // animGraph node groups
- bool ChunkProcessorAnimGraphNodeGroups::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- AnimGraph* animGraph = importParams.mAnimGraph;
- MCORE_ASSERT(animGraph);
-
- // read the number of node groups
- uint32 numNodeGroups;
- file->Read(&numNodeGroups, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&numNodeGroups, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Num Node Groups = %d", numNodeGroups);
- }
-
- // read all node groups
- for (uint32 g = 0; g < numNodeGroups; ++g)
- {
- // read the node group header
- FileFormat::AnimGraph_NodeGroup nodeGroupChunk;
- file->Read(&nodeGroupChunk, sizeof(FileFormat::AnimGraph_NodeGroup));
-
- MCore::RGBAColor emfxColor(nodeGroupChunk.mColor.mR, nodeGroupChunk.mColor.mG, nodeGroupChunk.mColor.mB, nodeGroupChunk.mColor.mA);
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&nodeGroupChunk.mNumNodes, importParams.mEndianType);
- MCore::Endian::ConvertRGBAColor(&emfxColor, importParams.mEndianType);
-
- const AZ::Color color128 = MCore::EmfxColorToAzColor(emfxColor);
- const AZ::u32 color32 = color128.ToU32();
-
- const char* groupName = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
- const uint32 numNodes = nodeGroupChunk.mNumNodes;
-
- // create and fill the new node group
- AnimGraphNodeGroup* nodeGroup = aznew AnimGraphNodeGroup(groupName);
- animGraph->AddNodeGroup(nodeGroup);
- nodeGroup->SetIsVisible(nodeGroupChunk.mIsVisible != 0);
- nodeGroup->SetColor(color32);
-
- // set the nodes of the node group
- MCore::Array& blendNodes = SharedHelperData::GetBlendNodes(importParams.mSharedData);
- nodeGroup->SetNumNodes(numNodes);
- for (uint32 i = 0; i < numNodes; ++i)
- {
- // read the node index of the current node inside the group
- uint32 nodeNr;
- file->Read(&nodeNr, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&nodeNr, importParams.mEndianType);
-
- MCORE_ASSERT(nodeNr != MCORE_INVALIDINDEX32);
-
- // set the id of the given node to the group
- if (nodeNr != MCORE_INVALIDINDEX32 && blendNodes[nodeNr])
- {
- nodeGroup->SetNode(i, blendNodes[nodeNr]->GetId());
- }
- else
- {
- nodeGroup->SetNode(i, AnimGraphNodeId::InvalidId);
- }
- }
-
- // log the details
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Node Group #%d:", g);
- MCore::LogDetailedInfo(" + Name = %s", nodeGroup->GetName());
- MCore::LogDetailedInfo(" + Color = (%.2f, %.2f, %.2f, %.2f)", static_cast(color128.GetR()), static_cast(color128.GetG()), static_cast(color128.GetB()), static_cast(color128.GetA()));
- MCore::LogDetailedInfo(" + Num Nodes = %i", nodeGroup->GetNumNodes());
- }
- }
-
- return true;
- }
-
-
- // animGraph group parameters
- bool ChunkProcessorAnimGraphGroupParameters::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- AnimGraph* animGraph = importParams.mAnimGraph;
- MCORE_ASSERT(animGraph);
-
- // read the number of group parameters
- uint32 numGroupParameters;
- file->Read(&numGroupParameters, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(&numGroupParameters, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Num group parameters = %d", numGroupParameters);
- }
-
- // Group parameters is going to re-shuffle the value parameter indices, therefore we
- // need to update the connections downstream of parameter nodes.
- EMotionFX::ValueParameterVector valueParametersBeforeChange = animGraph->RecursivelyGetValueParameters();
-
- // Since relocating a parameter to another parent changes its index, we are going to
- // compute all the relationships leaving the value parameters at the root, then relocate
- // them.
- AZStd::vector > parametersByGroup;
-
- // read all group parameters
- for (uint32 g = 0; g < numGroupParameters; ++g)
- {
- // read the group parameter header
- FileFormat::AnimGraph_GroupParameter groupChunk;
- file->Read(&groupChunk, sizeof(FileFormat::AnimGraph_GroupParameter));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&groupChunk.mNumParameters, importParams.mEndianType);
-
- const char* groupName = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
- const uint32 numParameters = groupChunk.mNumParameters;
-
- // create and fill the new group parameter
- AZStd::unique_ptr parameter(EMotionFX::ParameterFactory::Create(azrtti_typeid()));
- parameter->SetName(groupName);
-
- // Previously collapsed/expanded state in group parameters was stored in the animgraph file. However, that
- // would require to check out the animgraph file if you expand/collapse a group. Because this change was not
- // done through commands, the dirty state was not properly restored.
- // Collapsing state should be more of a setting per-user than something saved in the animgraph
- //groupParameter->SetIsCollapsed(groupChunk.mCollapsed != 0);
-
- if (!animGraph->AddParameter(parameter.get()))
- {
- continue;
- }
- const EMotionFX::GroupParameter* groupParameter = static_cast(parameter.release());
-
- parametersByGroup.emplace_back(groupParameter, EMotionFX::ParameterVector());
- AZStd::vector& parametersInGroup = parametersByGroup.back().second;
-
- // set the parameters of the group parameter
- for (uint32 i = 0; i < numParameters; ++i)
- {
- // read the parameter index
- uint32 parameterIndex;
- file->Read(¶meterIndex, sizeof(uint32));
- MCore::Endian::ConvertUnsignedInt32(¶meterIndex, importParams.mEndianType);
-
- MCORE_ASSERT(parameterIndex != MCORE_INVALIDINDEX32);
- if (parameterIndex != MCORE_INVALIDINDEX32)
- {
- const EMotionFX::Parameter* childParameter = animGraph->FindValueParameter(parameterIndex);
- parametersInGroup.emplace_back(const_cast(childParameter));
- }
- }
-
- // log the details
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Group parameter #%d:", g);
- MCore::LogDetailedInfo(" + Name = %s", groupParameter->GetName().c_str());
- MCore::LogDetailedInfo(" + Num Parameters = %i", groupParameter->GetNumParameters());
- }
- }
-
- // Now move the parameters to their groups
- for (const AZStd::pair& groupAndParameters : parametersByGroup)
- {
- const EMotionFX::GroupParameter* groupParameter = groupAndParameters.first;
- for (EMotionFX::Parameter* parameter : groupAndParameters.second)
- {
- animGraph->TakeParameterFromParent(parameter);
- animGraph->AddParameter(const_cast(parameter), groupParameter);
- }
- }
-
- const EMotionFX::ValueParameterVector valueParametersAfterChange = animGraph->RecursivelyGetValueParameters();
-
- AZStd::vector affectedObjects;
- animGraph->RecursiveCollectObjectsOfType(azrtti_typeid(), affectedObjects);
-
- for (EMotionFX::AnimGraphObject* affectedObject : affectedObjects)
- {
- EMotionFX::ObjectAffectedByParameterChanges* affectedObjectByParameterChanges = azdynamic_cast(affectedObject);
- affectedObjectByParameterChanges->ParameterOrderChanged(valueParametersBeforeChange, valueParametersAfterChange);
- }
-
- return true;
- }
-
-
- // animGraph game controller settings
- bool ChunkProcessorAnimGraphGameControllerSettings::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- uint32 i;
-
- AnimGraph* animGraph = importParams.mAnimGraph;
- MCORE_ASSERT(animGraph);
-
- // get the game controller settings for the anim graph and clear it
- AnimGraphGameControllerSettings& gameControllerSettings = animGraph->GetGameControllerSettings();
- gameControllerSettings.Clear();
-
- // read the number of presets and the active preset index
- uint32 activePresetIndex, numPresets;
- file->Read(&activePresetIndex, sizeof(uint32));
- file->Read(&numPresets, sizeof(uint32));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&activePresetIndex, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&numPresets, importParams.mEndianType);
-
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Game Controller Settings (NumPresets=%d, ActivePreset=%d)", numPresets, activePresetIndex);
- }
-
- // preallocate memory for the presets
- gameControllerSettings.SetNumPresets(numPresets);
-
- // read all presets
- for (uint32 p = 0; p < numPresets; ++p)
- {
- // read the preset chunk
- FileFormat::AnimGraph_GameControllerPreset presetChunk;
- file->Read(&presetChunk, sizeof(FileFormat::AnimGraph_GameControllerPreset));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&presetChunk.mNumParameterInfos, importParams.mEndianType);
- MCore::Endian::ConvertUnsignedInt32(&presetChunk.mNumButtonInfos, importParams.mEndianType);
-
- // read the preset name and get the number of parameter and button infos
- const char* presetName = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
- const uint32 numParamInfos = presetChunk.mNumParameterInfos;
- const uint32 numButtonInfos = presetChunk.mNumButtonInfos;
-
- // create and fill the new preset
- AnimGraphGameControllerSettings::Preset* preset = aznew AnimGraphGameControllerSettings::Preset(presetName);
- gameControllerSettings.SetPreset(p, preset);
-
- // read the parameter infos
- preset->SetNumParamInfos(numParamInfos);
- for (i = 0; i < numParamInfos; ++i)
- {
- // read the parameter info chunk
- FileFormat::AnimGraph_GameControllerParameterInfo paramInfoChunk;
- file->Read(¶mInfoChunk, sizeof(FileFormat::AnimGraph_GameControllerParameterInfo));
-
- // read the parameter name
- const char* parameterName = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
-
- // construct and fill the parameter info
- AnimGraphGameControllerSettings::ParameterInfo* parameterInfo = aznew AnimGraphGameControllerSettings::ParameterInfo(parameterName);
- parameterInfo->m_axis = paramInfoChunk.mAxis;
- parameterInfo->m_invert = (paramInfoChunk.mInvert != 0);
- parameterInfo->m_mode = (AnimGraphGameControllerSettings::ParameterMode)paramInfoChunk.mMode;
-
- preset->SetParamInfo(i, parameterInfo);
- }
-
- // read the button infos
- preset->SetNumButtonInfos(numButtonInfos);
- for (i = 0; i < numButtonInfos; ++i)
- {
- // read the button info chunk
- FileFormat::AnimGraph_GameControllerButtonInfo buttonInfoChunk;
- file->Read(&buttonInfoChunk, sizeof(FileFormat::AnimGraph_GameControllerButtonInfo));
-
- // read the button string
- const char* buttonString = SharedHelperData::ReadString(file, importParams.mSharedData, importParams.mEndianType);
-
- // construct and fill the button info
- AnimGraphGameControllerSettings::ButtonInfo* buttonInfo = aznew AnimGraphGameControllerSettings::ButtonInfo(buttonInfoChunk.mButtonIndex);
- buttonInfo->m_mode = (AnimGraphGameControllerSettings::ButtonMode)buttonInfoChunk.mMode;
- buttonInfo->m_string = buttonString;
-
- preset->SetButtonInfo(i, buttonInfo);
- }
-
- // log the details
- if (GetLogging())
- {
- MCore::LogDetailedInfo("- Preset '%s':", preset->GetName());
- MCore::LogDetailedInfo(" + Num Param Infos = %d", preset->GetNumParamInfos());
- MCore::LogDetailedInfo(" + Num Button Infos = %d", preset->GetNumButtonInfos());
- }
- }
-
- // set the active preset
- if (activePresetIndex != MCORE_INVALIDINDEX32)
- {
- AnimGraphGameControllerSettings::Preset* activePreset = gameControllerSettings.GetPreset(activePresetIndex);
- gameControllerSettings.SetActivePreset(activePreset);
- }
-
- return true;
- }
-
- //----------------------------------------------------------------------------------------------------------
- // MotionSet
- //----------------------------------------------------------------------------------------------------------
-
- // all submotions in one chunk
- bool ChunkProcessorMotionSet::Process(MCore::File* file, Importer::ImportParameters& importParams)
- {
- const MCore::Endian::EEndianType endianType = importParams.mEndianType;
-
- FileFormat::MotionSetsChunk motionSetsChunk;
- file->Read(&motionSetsChunk, sizeof(FileFormat::MotionSetsChunk));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&motionSetsChunk.mNumSets, endianType);
-
- // get the number of motion sets and iterate through them
- const uint32 numMotionSets = motionSetsChunk.mNumSets;
- for (uint32 i = 0; i < numMotionSets; ++i)
- {
- FileFormat::MotionSetChunk motionSetChunk;
- file->Read(&motionSetChunk, sizeof(FileFormat::MotionSetChunk));
-
- // convert endian
- MCore::Endian::ConvertUnsignedInt32(&motionSetChunk.mNumChildSets, endianType);
- MCore::Endian::ConvertUnsignedInt32(&motionSetChunk.mNumMotionEntries, endianType);
-
- // get the parent set
- const char* parentSetName = SharedHelperData::ReadString(file, importParams.mSharedData, endianType);
- GetMotionManager().Lock();
- MotionSet* parentSet = GetMotionManager().FindMotionSetByName(parentSetName, importParams.m_isOwnedByRuntime);
- GetMotionManager().Unlock();
-
- // read the motion set name and create our new motion set
- const char* motionSetName = SharedHelperData::ReadString(file, importParams.mSharedData, endianType);
- MotionSet* motionSet = aznew MotionSet(motionSetName, parentSet);
- motionSet->SetIsOwnedByRuntime(importParams.m_isOwnedByRuntime);
-
- // set the root motion set to the importer params motion set, this will be returned by the Importer::LoadMotionSet() function
- if (parentSet == nullptr)
- {
- assert(importParams.mMotionSet == nullptr);
- importParams.mMotionSet = motionSet;
- }
-
- // read the filename and set it
- /*const char* motionSetFileName = */ SharedHelperData::ReadString(file, importParams.mSharedData, endianType);
- //motionSet->SetFileName( motionSetFileName );
-
- // in case this is not a root motion set add the new motion set as child set to the parent set
- if (parentSet)
- {
- parentSet->AddChildSet(motionSet);
- }
-
- // Read all motion entries.
- const uint32 numMotionEntries = motionSetChunk.mNumMotionEntries;
- motionSet->ReserveMotionEntries(numMotionEntries);
- AZStd::string nativeMotionFileName;
- for (uint32 j = 0; j < numMotionEntries; ++j)
- {
- // read the motion entry
- const char* motionFileName = SharedHelperData::ReadString(file, importParams.mSharedData, endianType);
- nativeMotionFileName = motionFileName;
-
- // read the string id and set it
- const char* motionStringID = SharedHelperData::ReadString(file, importParams.mSharedData, endianType);
-
- // add the motion entry to the motion set
- MotionSet::MotionEntry* motionEntry = aznew MotionSet::MotionEntry(nativeMotionFileName.c_str(), motionStringID);
- motionSet->AddMotionEntry(motionEntry);
- }
- }
-
- return true;
- }
-
-
-
//----------------------------------------------------------------------------------------------------------
// NodeMap
//----------------------------------------------------------------------------------------------------------
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h
index 3d0412eb3f..306e4af0c8 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/Importer/ChunkProcessors.h
@@ -16,8 +16,6 @@
#include "SharedFileFormatStructs.h"
#include "ActorFileFormat.h"
#include "MotionFileFormat.h"
-#include "AnimGraphFileFormat.h"
-#include "MotionSetFileFormat.h"
#include "NodeMapFileFormat.h"
#include "Importer.h"
#include
@@ -32,7 +30,6 @@ namespace EMotionFX
class Importer;
class AnimGraphNode;
-
/**
* Shared importer data class.
* Chunks can load data, which might be shared between other chunks during import.
@@ -57,20 +54,11 @@ namespace EMotionFX
virtual void Reset() {}
protected:
- /**
- * The constructor.
- */
SharedData()
: BaseObject() {}
-
- /**
- * The destructor.
- */
virtual ~SharedData() { Reset(); }
};
-
-
/**
* Helper class for reading strings from files and file information storage.
*/
@@ -108,34 +96,12 @@ namespace EMotionFX
*/
static const char* ReadString(MCore::Stream* file, MCore::Array