Merge branch 'development' of https://github.com/o3de/o3de into mp_lerp_jitter
This commit is contained in:
+102
@@ -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()
|
||||
@@ -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)')
|
||||
@@ -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)
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5232563c3ff322669808ac4daeda3d822e4ef8c9c87db0fa245f0f9c9c34aada
|
||||
size 23379
|
||||
@@ -0,0 +1,6 @@
|
||||
<download name="AtomFeatureIntegrationBenchmark" type="Map">
|
||||
<index src="filelist.xml" dest="filelist.xml"/>
|
||||
<files>
|
||||
<file src="level.pak" dest="level.pak" size="154A" md5="da50266269914f05d06d80f0025953fc"/>
|
||||
</files>
|
||||
</download>
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e075be2cb7cf5aa98e3503c1119b94c3098b35500c98c4db32d025c9e1afa52d
|
||||
size 5450
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup>
|
||||
<DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<EngineInfo> 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<AZ::IO::FixedMaxPath> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<!-- rapidjson::GenericValue - basic support -->
|
||||
<Type Name="rapidjson_ly::GenericValue<*,*>">
|
||||
<DisplayString Condition="(data_.f.flags & kTypeMask) == kNullType">null</DisplayString>
|
||||
<DisplayString Condition="data_.f.flags == kTrueFlag">true</DisplayString>
|
||||
<DisplayString Condition="data_.f.flags == kFalseFlag">false</DisplayString>
|
||||
<DisplayString Condition="data_.f.flags == kShortStringFlag">{data_.ss.str}</DisplayString>
|
||||
<DisplayString Condition="(data_.f.flags & kTypeMask) == kStringType">{(const char*)((size_t)data_.s.str & 0x0000FFFFFFFFFFFF)}</DisplayString>
|
||||
<DisplayString Condition="(data_.f.flags & kNumberIntFlag) == kNumberIntFlag">{data_.n.i.i}</DisplayString>
|
||||
<DisplayString Condition="(data_.f.flags & kNumberUintFlag) == kNumberUintFlag">{data_.n.u.u}</DisplayString>
|
||||
<DisplayString Condition="(data_.f.flags & kNumberInt64Flag) == kNumberInt64Flag">{data_.n.i64}</DisplayString>
|
||||
<DisplayString Condition="(data_.f.flags & kNumberUint64Flag) == kNumberUint64Flag">{data_.n.u64}</DisplayString>
|
||||
<DisplayString Condition="(data_.f.flags & kNumberDoubleFlag) == kNumberDoubleFlag">{data_.n.d}</DisplayString>
|
||||
<DisplayString Condition="data_.f.flags == kObjectType">Object members={data_.o.size}</DisplayString>
|
||||
<DisplayString Condition="data_.f.flags == kArrayType">Array members={data_.a.size}</DisplayString>
|
||||
<Expand>
|
||||
<Item Condition="data_.f.flags == kObjectType" Name="[size]">data_.o.size</Item>
|
||||
<Item Condition="data_.f.flags == kObjectType" Name="[capacity]">data_.o.capacity</Item>
|
||||
<ArrayItems Condition="data_.f.flags == kObjectType">
|
||||
<Size>data_.o.size</Size>
|
||||
<!-- NOTE: Rapidjson stores some extra data in the high bits of pointers, which is why the mask -->
|
||||
<ValuePointer>(rapidjson_ly::GenericMember<$T1,$T2>*)(((size_t)data_.o.members) & 0x0000FFFFFFFFFFFF)</ValuePointer>
|
||||
</ArrayItems>
|
||||
|
||||
<Item Condition="data_.f.flags == kArrayType" Name="[size]">data_.a.size</Item>
|
||||
<Item Condition="data_.f.flags == kArrayType" Name="[capacity]">data_.a.capacity</Item>
|
||||
<ArrayItems Condition="data_.f.flags == kArrayType">
|
||||
<Size>data_.a.size</Size>
|
||||
<!-- NOTE: Rapidjson stores some extra data in the high bits of pointers, which is why the mask -->
|
||||
<ValuePointer>(rapidjson_ly::GenericValue<$T1,$T2>*)(((size_t)data_.a.elements) & 0x0000FFFFFFFFFFFF)</ValuePointer>
|
||||
</ArrayItems>
|
||||
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -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
|
||||
|
||||
+6
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<uint16_t>::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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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 <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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<AZ::TimeMs>(m_random.GetRandom()) % (connectionQuality.m_varianceMs / aznumeric_cast<AZ::TimeMs>(2));
|
||||
const AZ::TimeMs jitterMs = aznumeric_cast<AZ::TimeMs>(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<AZ::TimeMs>(2)) + jitterMs;
|
||||
const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs) + jitterMs;
|
||||
|
||||
DeferredData deferred = DeferredData(address, data, size, encrypt, dtlsEndpoint);
|
||||
AZ::Interface<AZ::IEventScheduler>::Get()->AddCallback([&, deferredData = deferred]
|
||||
|
||||
+8
-5
@@ -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)
|
||||
|
||||
+2
@@ -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.
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
@@ -308,6 +308,11 @@ namespace AZ
|
||||
AZStd::invoke(AZStd::forward<Functor>(functor), m_shadowFeatureProcessor, shadowId, AZStd::forward<ParamType>(param));
|
||||
}
|
||||
}
|
||||
|
||||
void DiskLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias)
|
||||
{
|
||||
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias);
|
||||
}
|
||||
|
||||
void DiskLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ShadowDataIndex>(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<FilterParamIndex>(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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
+112
@@ -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 <AtomToolsFramework/Communication/LocalServer.h>
|
||||
#include <AtomToolsFramework/Communication/LocalSocket.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/UserSettings/UserSettingsProvider.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzQtComponents/Application/AzQtApplication.h>
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
|
||||
#include <AzToolsFramework/Logger/TraceLogger.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
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<AZ::Module*>& 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<AZStd::string> 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
|
||||
@@ -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 <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AtomToolsFramework/Application/AtomToolsApplication.h>
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzFramework/Asset/AssetSystemComponent.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/Network/AssetProcessorConnection.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
#include <AzToolsFramework/Asset/AssetSystemComponent.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AzToolsFrameworkModule.h>
|
||||
#include <AzToolsFramework/SourceControl/PerforceComponent.h>
|
||||
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerComponent.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
|
||||
#include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
|
||||
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
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<AZ::BehaviorContext*>(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<AzToolsFramework::AssetBrowser::AssetBrowserComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::Components::PropertyManagerComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::PerforceComponent>(),
|
||||
});
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
void AtomToolsApplication::CreateStaticModules(AZStd::vector<AZ::Module*>& 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<AZStd::string> AtomToolsApplication::GetCriticalAssetFilters() const
|
||||
{
|
||||
return AZStd::vector<AZStd::string>({});
|
||||
}
|
||||
|
||||
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<int>(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<AZStd::string_view> 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<AZStd::string> 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<AzToolsFramework::EditorPythonEventsInterface>::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<AZStd::string> 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
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
|
||||
#include <Atom/Document/MaterialDocumentModule.h>
|
||||
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
|
||||
|
||||
#include <Atom/Viewport/MaterialViewportModule.h>
|
||||
|
||||
#include <Atom/Window/MaterialEditorWindowModule.h>
|
||||
#include <Atom/Window/MaterialEditorWindowFactoryRequestBus.h>
|
||||
#include <Atom/Window/MaterialEditorWindowRequestBus.h>
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
@@ -26,24 +41,9 @@
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
|
||||
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
|
||||
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
|
||||
#include <Source/MaterialEditorApplication.h>
|
||||
#include <MaterialEditor_Traits_Platform.h>
|
||||
|
||||
#include <Atom/Document/MaterialDocumentModule.h>
|
||||
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
|
||||
|
||||
#include <Atom/Viewport/MaterialViewportModule.h>
|
||||
|
||||
#include <Atom/Window/MaterialEditorWindowModule.h>
|
||||
#include <Atom/Window/MaterialEditorWindowFactoryRequestBus.h>
|
||||
#include <Atom/Window/MaterialEditorWindowRequestBus.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QObject>
|
||||
#include <QMessageBox>
|
||||
@@ -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<AZ::BehaviorContext*>(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<AzToolsFramework::AssetBrowser::AssetBrowserComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::Components::PropertyManagerComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::PerforceComponent>(),
|
||||
});
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
void MaterialEditorApplication::CreateStaticModules(AZStd::vector<AZ::Module*>& 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<AZStd::string> 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<int>(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<AZStd::string>({ "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<AZStd::string_view> 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<AZStd::string> 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<AzToolsFramework::EditorPythonEventsInterface>::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<AZStd::string> 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
|
||||
|
||||
@@ -10,19 +10,7 @@
|
||||
|
||||
#include <Atom/Document/MaterialDocumentSystemRequestBus.h>
|
||||
#include <Atom/Window/MaterialEditorWindowNotificationBus.h>
|
||||
#include <AtomToolsFramework/Communication/LocalServer.h>
|
||||
#include <AtomToolsFramework/Communication/LocalSocket.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
#include <AzCore/UserSettings/UserSettingsProvider.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzFramework/Logging/LogFile.h>
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
|
||||
#include <AzToolsFramework/Logger/TraceLogger.h>
|
||||
#include <AzQtComponents/Application/AzQtApplication.h>
|
||||
#include <AtomToolsFramework/Application/AtomToolsApplication.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
@@ -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<AZ::Module*>& 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<LogMessage> m_startupLogSink;
|
||||
AZStd::unique_ptr<AzFramework::LogFile> 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<AZStd::string> GetCriticalAssetFilters() const override;
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
|
||||
+7
-342
@@ -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<AZ::BehaviorContext*>(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<AzToolsFramework::AssetBrowser::AssetBrowserComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::Components::PropertyManagerComponent>(),
|
||||
azrtti_typeid<AzToolsFramework::PerforceComponent>(),
|
||||
});
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleApplication::CreateStaticModules(AZStd::vector<AZ::Module*>& 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<AZStd::string> 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<int>(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<AZStd::string>({ "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<AzToolsFramework::EditorPythonEventsInterface>::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<AZStd::string> 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
|
||||
|
||||
+7
-86
@@ -8,63 +8,32 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/UserSettings/UserSettingsProvider.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
|
||||
#include <AzToolsFramework/API/AssetDatabaseBus.h>
|
||||
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
|
||||
#include <AzToolsFramework/Logger/TraceLogger.h>
|
||||
|
||||
#include <Atom/Document/ShaderManagementConsoleDocumentSystemRequestBus.h>
|
||||
#include <Atom/Window/ShaderManagementConsoleWindowNotificationBus.h>
|
||||
|
||||
#include <AzQtComponents/Application/AzQtApplication.h>
|
||||
#include <AtomToolsFramework/Application/AtomToolsApplication.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
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<AZ::Module*>& 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<AZStd::string> GetCriticalAssetFilters() const override;
|
||||
};
|
||||
} // namespace ShaderManagementConsole
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 <ctrl>.");
|
||||
}
|
||||
|
||||
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<AZStd::sys_time_t>((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<float>(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<float>(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;
|
||||
}
|
||||
|
||||
+6
@@ -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;
|
||||
|
||||
+1
@@ -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;
|
||||
|
||||
+2
-1
@@ -18,7 +18,7 @@ namespace AZ
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AreaLightComponentConfig, ComponentConfig>()
|
||||
->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)
|
||||
|
||||
+18
@@ -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
|
||||
{
|
||||
|
||||
+2
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
+9
@@ -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"
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+85
-82
@@ -11,121 +11,124 @@
|
||||
#include <Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Render
|
||||
{
|
||||
namespace Render
|
||||
SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible)
|
||||
: LightDelegateBase<PointLightFeatureProcessorInterface>(entityId, isVisible)
|
||||
, m_shapeBus(shapeBus)
|
||||
{
|
||||
SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible)
|
||||
: LightDelegateBase<PointLightFeatureProcessorInterface>(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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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/"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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> animGraph(GetImporter().LoadAnimGraph(fullPath, nullptr, loadFilter));
|
||||
AZStd::unique_ptr<AnimGraph> animGraph(GetImporter().LoadAnimGraph(fullPath, loadFilter));
|
||||
|
||||
if (!animGraph)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <MCore/Source/Attribute.h>
|
||||
#include <EMotionFX/Source/Parameter/BoolParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/ColorParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/FloatSpinnerParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/IntSliderParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/IntSpinnerParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/TagParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/RotationParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/StringParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/Vector2Parameter.h>
|
||||
#include <EMotionFX/Source/Parameter/Vector3GizmoParameter.h>
|
||||
#include <EMotionFX/Source/Parameter/Vector3Parameter.h>
|
||||
#include <EMotionFX/Source/Parameter/Vector4Parameter.h>
|
||||
|
||||
namespace EMotionFX
|
||||
{
|
||||
namespace FileFormat
|
||||
{
|
||||
AZ::TypeId GetParameterTypeIdForInterfaceType(uint32 interfaceType)
|
||||
{
|
||||
switch (interfaceType)
|
||||
{
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_FLOATSPINNER:
|
||||
return azrtti_typeid<EMotionFX::FloatSpinnerParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_FLOATSLIDER:
|
||||
return azrtti_typeid<EMotionFX::FloatSliderParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_INTSPINNER:
|
||||
return azrtti_typeid<EMotionFX::IntSpinnerParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_INTSLIDER:
|
||||
return azrtti_typeid<EMotionFX::IntSliderParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_CHECKBOX:
|
||||
return azrtti_typeid<EMotionFX::BoolParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR2:
|
||||
return azrtti_typeid<EMotionFX::Vector2Parameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3GIZMO:
|
||||
return azrtti_typeid<EMotionFX::Vector3GizmoParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR4:
|
||||
return azrtti_typeid<EMotionFX::Vector4Parameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_COLOR:
|
||||
return azrtti_typeid<EMotionFX::ColorParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_STRING:
|
||||
return azrtti_typeid<EMotionFX::StringParameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3:
|
||||
return azrtti_typeid<EMotionFX::Vector3Parameter>();
|
||||
case MCore::ATTRIBUTE_INTERFACETYPE_TAG:
|
||||
return azrtti_typeid<EMotionFX::TagParameter>();
|
||||
|
||||
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<EMotionFX::FloatSpinnerParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_FLOATSPINNER;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::FloatSliderParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_FLOATSLIDER;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::IntSpinnerParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_INTSPINNER;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::IntSliderParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_INTSLIDER;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::BoolParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_CHECKBOX;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::Vector2Parameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR2;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::Vector3GizmoParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3GIZMO;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::Vector4Parameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR4;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::ColorParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_COLOR;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::StringParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_STRING;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::Vector3Parameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_VECTOR3;
|
||||
}
|
||||
else if (parameterTypeId == azrtti_typeid<EMotionFX::TagParameter>())
|
||||
{
|
||||
return MCore::ATTRIBUTE_INTERFACETYPE_TAG;
|
||||
}
|
||||
return MCORE_INVALIDINDEX32;
|
||||
}
|
||||
}
|
||||
} // namespace EMotionFX
|
||||
@@ -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
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <MCore/Source/AttributeFactory.h>
|
||||
#include <MCore/Source/AzCoreConversions.h>
|
||||
#include <MCore/Source/Distance.h>
|
||||
#include <MCore/Source/File.h>
|
||||
#include <MCore/Source/ReflectionSerializer.h>
|
||||
#include <MCore/Source/StringConversions.h>
|
||||
#include <MCore/Source/MCoreSystem.h>
|
||||
@@ -53,7 +54,6 @@
|
||||
#include "../AnimGraphTransitionCondition.h"
|
||||
#include "../MCore/Source/Endian.h"
|
||||
#include "../NodeMap.h"
|
||||
#include "LegacyAnimGraphNodeParser.h"
|
||||
|
||||
#include <EMotionFX/Source/Importer/ActorFileFormat.h>
|
||||
#include <EMotionFX/Source/TwoStringEventData.h>
|
||||
@@ -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*>* sharedData)
|
||||
{
|
||||
// find the helper data
|
||||
SharedData* data = Importer::FindSharedData(sharedData, SharedHelperData::TYPE_ID);
|
||||
SharedHelperData* helperData = static_cast<SharedHelperData*>(data);
|
||||
|
||||
AZStd::vector<AZStd::string> 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*>* 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<AnimGraphNode*>& SharedHelperData::GetBlendNodes(MCore::Array<SharedData*>* sharedData)
|
||||
{
|
||||
// find the helper data
|
||||
SharedData* data = Importer::FindSharedData(sharedData, SharedHelperData::TYPE_ID);
|
||||
SharedHelperData* helperData = static_cast<SharedHelperData*>(data);
|
||||
return helperData->mBlendNodes;
|
||||
}
|
||||
|
||||
// Get the table of entry state indices to state machines IDs
|
||||
AZStd::map<AZ::u64, uint32>& SharedHelperData::GetEntryStateToStateMachineTable(MCore::Array<SharedData*>* sharedData)
|
||||
{
|
||||
// Find the helper data
|
||||
SharedData* data = Importer::FindSharedData(sharedData, SharedHelperData::TYPE_ID);
|
||||
SharedHelperData* helperData = static_cast<SharedHelperData*>(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<AnimGraphNode*>& 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<AnimGraphStateMachine>(), "ChunkProcessorAnimGraphStateTransitions::Process : Unexpected node type expected AnimGraphStateMachine. Found %u instead", azrtti_typeid(blendNodes[stateMachineIndex]));
|
||||
AnimGraphStateMachine* stateMachine = static_cast<AnimGraphStateMachine*>(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<AnimGraphStateTransition>())
|
||||
{
|
||||
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<AnimGraphStateTransition>(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<AnimGraphStateTransition>(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<AnimGraphNode*>& 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<uint16>(sourcePort), static_cast<uint16>(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<EMotionFX::Parameter> newParam(EMotionFX::ParameterFactory::Create(parameterTypeId));
|
||||
AZ_Assert(azrtti_istypeof<EMotionFX::ValueParameter>(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<EMotionFX::ValueParameter*>(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<AnimGraphNode*>& 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<float>(color128.GetR()), static_cast<float>(color128.GetG()), static_cast<float>(color128.GetB()), static_cast<float>(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<AZStd::pair<const EMotionFX::GroupParameter*, EMotionFX::ParameterVector> > 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<EMotionFX::Parameter> parameter(EMotionFX::ParameterFactory::Create(azrtti_typeid<EMotionFX::GroupParameter>()));
|
||||
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<EMotionFX::GroupParameter*>(parameter.release());
|
||||
|
||||
parametersByGroup.emplace_back(groupParameter, EMotionFX::ParameterVector());
|
||||
AZStd::vector<EMotionFX::Parameter*>& 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<EMotionFX::Parameter*>(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<const EMotionFX::GroupParameter*, EMotionFX::ParameterVector>& groupAndParameters : parametersByGroup)
|
||||
{
|
||||
const EMotionFX::GroupParameter* groupParameter = groupAndParameters.first;
|
||||
for (EMotionFX::Parameter* parameter : groupAndParameters.second)
|
||||
{
|
||||
animGraph->TakeParameterFromParent(parameter);
|
||||
animGraph->AddParameter(const_cast<EMotionFX::Parameter*>(parameter), groupParameter);
|
||||
}
|
||||
}
|
||||
|
||||
const EMotionFX::ValueParameterVector valueParametersAfterChange = animGraph->RecursivelyGetValueParameters();
|
||||
|
||||
AZStd::vector<EMotionFX::AnimGraphObject*> affectedObjects;
|
||||
animGraph->RecursiveCollectObjectsOfType(azrtti_typeid<EMotionFX::ObjectAffectedByParameterChanges>(), affectedObjects);
|
||||
|
||||
for (EMotionFX::AnimGraphObject* affectedObject : affectedObjects)
|
||||
{
|
||||
EMotionFX::ObjectAffectedByParameterChanges* affectedObjectByParameterChanges = azdynamic_cast<EMotionFX::ObjectAffectedByParameterChanges*>(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
|
||||
//----------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -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 <AzCore/std/containers/map.h>
|
||||
@@ -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<SharedData*>* sharedData, MCore::Endian::EEndianType endianType);
|
||||
|
||||
/**
|
||||
* Get the array of currently loaded anim graph nodes.
|
||||
* @param sharedData The array which holds the shared data objects.
|
||||
* @result The array of currently loaded anim graph nodes.
|
||||
*/
|
||||
static MCore::Array<AnimGraphNode*>& GetBlendNodes(MCore::Array<SharedData*>* sharedData);
|
||||
|
||||
/**
|
||||
* Get the table to look up a state machine that needs an entry state as they are created.
|
||||
* @param sharedData The array which holds the shared data objects.
|
||||
* @result The map whose keys are the indices of entry states and the values are the IDs of the state machines that need those as entry states.
|
||||
*/
|
||||
static AZStd::map<AZ::u64, uint32>& GetEntryStateToStateMachineTable(MCore::Array<SharedData*>* sharedData);
|
||||
|
||||
/**
|
||||
* Checks if the strings in the file are encoded using unicode or multi-byte based on the exporter date.
|
||||
* The first official EMotion FX version to use unicode was compiled on 26th November 2012.
|
||||
*/
|
||||
static bool GetIsUnicodeFile(const char* dateString, MCore::Array<SharedData*>* sharedData);
|
||||
|
||||
public:
|
||||
uint32 mFileHighVersion; /**< The high file version. For example 3 in case of v3.10. */
|
||||
uint32 mFileLowVersion; /**< The low file version. For example 10 in case of v3.10. */
|
||||
uint32 mStringStorageSize; /**< The size of the string buffer. */
|
||||
bool mIsUnicodeFile; /**< True in case strings in the file are saved using unicode character set, false in case they are saved using multi-byte. */
|
||||
char* mStringStorage; /**< The shared string buffer. */
|
||||
MCore::Array<AnimGraphNode*> mBlendNodes; /**< Array of read anim graph nodes. */
|
||||
AZStd::map<AZ::u64, uint32> m_entryNodeIndexToStateMachineIdLookupTable;
|
||||
protected:
|
||||
/**
|
||||
* The constructor.
|
||||
@@ -338,22 +304,6 @@ namespace EMotionFX
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorMotionMorphSubMotions, FileFormat::MOTION_CHUNK_MORPHSUBMOTIONS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorMotionData, FileFormat::MOTION_CHUNK_MOTIONDATA, 1)
|
||||
|
||||
// Anim graph file format chunk processors
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphStateTransitions, FileFormat::ANIMGRAPH_CHUNK_STATETRANSITIONS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphNodeConnections, FileFormat::ANIMGRAPH_CHUNK_NODECONNECTIONS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphParameters, FileFormat::ANIMGRAPH_CHUNK_PARAMETERS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphNodeGroups, FileFormat::ANIMGRAPH_CHUNK_NODEGROUPS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphGroupParameters, FileFormat::ANIMGRAPH_CHUNK_GROUPPARAMETERS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphGameControllerSettings, FileFormat::ANIMGRAPH_CHUNK_GAMECONTROLLERSETTINGS, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphNode, FileFormat::ANIMGRAPH_CHUNK_BLENDNODE, 1)
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorAnimGraphAdditionalInfo, FileFormat::ANIMGRAPH_CHUNK_ADDITIONALINFO, 1)
|
||||
|
||||
// motion set file format chunk processors
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorMotionSet, FileFormat::CHUNK_MOTIONSET, 1)
|
||||
|
||||
// node map file format chunk processors
|
||||
EMFX_CHUNKPROCESSOR(ChunkProcessorNodeMap, FileFormat::CHUNK_NODEMAP, 1)
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
} // namespace EMotionFX
|
||||
|
||||
@@ -156,85 +156,6 @@ namespace EMotionFX
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// check if we can process the given motion set file
|
||||
bool Importer::CheckIfIsValidMotionSetFile(MCore::File* f, MCore::Endian::EEndianType* outEndianType) const
|
||||
{
|
||||
MCORE_ASSERT(f->GetIsOpen());
|
||||
|
||||
// verify if we actually are dealing with a valid actor file
|
||||
FileFormat::MotionSet_Header header;
|
||||
if (f->Read(&header, sizeof(FileFormat::MotionSet_Header)) == 0)
|
||||
{
|
||||
MCore::LogError("Failed to read the motion set file header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the FOURCC
|
||||
if (header.mFourCC[0] != 'M' || header.mFourCC[1] != 'O' || header.mFourCC[2] != 'S' || header.mFourCC[3] != ' ')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the chunks
|
||||
switch (header.mEndianType)
|
||||
{
|
||||
case 0:
|
||||
*outEndianType = MCore::Endian::ENDIAN_LITTLE;
|
||||
break;
|
||||
case 1:
|
||||
*outEndianType = MCore::Endian::ENDIAN_BIG;
|
||||
break;
|
||||
default:
|
||||
MCore::LogError("Unsupported endian type used! (endian type = %d)", header.mEndianType);
|
||||
return false;
|
||||
}
|
||||
;
|
||||
|
||||
// yes, it is a valid motionset file!
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// check if we can process the given anim graph file
|
||||
bool Importer::CheckIfIsValidAnimGraphFile(MCore::File* f, MCore::Endian::EEndianType* outEndianType) const
|
||||
{
|
||||
MCORE_ASSERT(f->GetIsOpen());
|
||||
|
||||
// verify if we actually are dealing with a valid actor file
|
||||
FileFormat::AnimGraph_Header header;
|
||||
if (f->Read(&header, sizeof(FileFormat::AnimGraph_Header)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the FOURCC
|
||||
if (header.mFourCC[0] != 'A' || header.mFourCC[1] != 'N' || header.mFourCC[2] != 'G' || header.mFourCC[3] != 'R')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the chunks
|
||||
switch (header.mEndianType)
|
||||
{
|
||||
case 0:
|
||||
*outEndianType = MCore::Endian::ENDIAN_LITTLE;
|
||||
break;
|
||||
case 1:
|
||||
*outEndianType = MCore::Endian::ENDIAN_BIG;
|
||||
break;
|
||||
default:
|
||||
MCore::LogError("Unsupported endian type used! (endian type = %d)", header.mEndianType);
|
||||
return false;
|
||||
}
|
||||
;
|
||||
|
||||
// yes, it is a valid anim graph file!
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// check if we can process the given node map file
|
||||
bool Importer::CheckIfIsValidNodeMapFile(MCore::File* f, MCore::Endian::EEndianType* outEndianType) const
|
||||
{
|
||||
@@ -599,125 +520,32 @@ namespace EMotionFX
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
// try to load a motion set from disk
|
||||
MotionSet* Importer::LoadMotionSet(AZStd::string filename, MotionSetSettings* settings, const AZ::ObjectStream::FilterDescriptor& loadFilter)
|
||||
{
|
||||
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePathKeepCase, filename);
|
||||
|
||||
const bool isLegacyFile = Importer::CheckFileType(filename.c_str()) == Importer::EFileType::FILETYPE_MOTIONSET;
|
||||
if (!isLegacyFile)
|
||||
AZ::SerializeContext* context = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!context)
|
||||
{
|
||||
AZ::SerializeContext* context = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!context)
|
||||
{
|
||||
AZ_Error("EMotionFX", false, "Can't get serialize context from component application.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EMotionFX::MotionSet* motionSet = EMotionFX::MotionSet::LoadFromFile(filename, context, loadFilter);
|
||||
if (motionSet)
|
||||
{
|
||||
motionSet->SetFilename(filename.c_str());
|
||||
}
|
||||
return motionSet;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Legacy file type loading
|
||||
//////////////////////////////////////////
|
||||
|
||||
// check if we want to load the motion set even if a motion set with the given filename is already inside the motion manager
|
||||
if (settings == nullptr || settings->mForceLoading == false)
|
||||
{
|
||||
// search the motion set inside the motion manager and return it if it already got loaded
|
||||
MotionSet* motionSet = GetMotionManager().FindMotionSetByFileName(filename.c_str());
|
||||
if (motionSet)
|
||||
{
|
||||
MCore::LogInfo(" + Motion set '%s' already loaded, returning already loaded motion set from the MotionManager.", filename.c_str());
|
||||
return motionSet;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogInfo("- Trying to load motion set from file '%s'...", filename.c_str());
|
||||
}
|
||||
|
||||
// try to open the file from disk
|
||||
MCore::DiskFile f;
|
||||
if (f.Open(filename.c_str(), MCore::DiskFile::READ) == false)
|
||||
{
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogError(" + Failed to open the file for motion set.");
|
||||
}
|
||||
AZ_Error("EMotionFX", false, "Can't get serialize context from component application.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// retrieve the filesize
|
||||
const size_t fileSize = f.GetFileSize();
|
||||
|
||||
// create a temporary buffer for the file
|
||||
uint8* fileBuffer = (uint8*)MCore::Allocate(fileSize, EMFX_MEMCATEGORY_IMPORTER);
|
||||
|
||||
// read in the complete file
|
||||
f.Read(fileBuffer, fileSize);
|
||||
|
||||
// close the file again
|
||||
f.Close();
|
||||
|
||||
// create the motion set reading from memory
|
||||
MotionSet* result = LoadMotionSet(fileBuffer, fileSize, settings);
|
||||
if (result)
|
||||
EMotionFX::MotionSet* motionSet = EMotionFX::MotionSet::LoadFromFile(filename, context, loadFilter);
|
||||
if (motionSet)
|
||||
{
|
||||
result->SetFilename(filename.c_str());
|
||||
}
|
||||
|
||||
// delete the filebuffer again
|
||||
MCore::Free(fileBuffer);
|
||||
|
||||
// check if it worked :)
|
||||
if (result == nullptr)
|
||||
{
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogError(" + Failed to load motion set from file '%s'.", filename.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogInfo(" + Loading successfully finished.");
|
||||
}
|
||||
}
|
||||
|
||||
// return the result
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
MotionSet* Importer::LoadMotionSet(uint8* memoryStart, size_t lengthInBytes, MotionSetSettings* settings)
|
||||
{
|
||||
// Legacy file type loading.
|
||||
MCore::MemoryFile memFile;
|
||||
memFile.Open(memoryStart, lengthInBytes);
|
||||
|
||||
const bool isLegacyFile = Importer::CheckFileType(&memFile) == Importer::EFileType::FILETYPE_MOTIONSET;
|
||||
if (isLegacyFile)
|
||||
{
|
||||
// Open the memory file again as CheckFileType() is closing it at the end.
|
||||
memFile.Open(memoryStart, lengthInBytes);
|
||||
EMotionFX::MotionSet* motionSet = LoadMotionSet(&memFile, settings);
|
||||
motionSet->SetFilename(filename.c_str());
|
||||
if (settings)
|
||||
{
|
||||
motionSet->SetIsOwnedByRuntime(settings->m_isOwnedByRuntime);
|
||||
}
|
||||
return motionSet;
|
||||
}
|
||||
return motionSet;
|
||||
}
|
||||
|
||||
|
||||
MotionSet* Importer::LoadMotionSet(uint8* memoryStart, size_t lengthInBytes, MotionSetSettings* settings)
|
||||
{
|
||||
AZ::SerializeContext* context = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!context)
|
||||
@@ -734,58 +562,6 @@ namespace EMotionFX
|
||||
return motionSet;
|
||||
}
|
||||
|
||||
|
||||
// try to load a motion set from a file
|
||||
MotionSet* Importer::LoadMotionSet(MCore::File* f, MotionSetSettings* settings)
|
||||
{
|
||||
MCORE_ASSERT(f);
|
||||
MCORE_ASSERT(f->GetIsOpen());
|
||||
|
||||
// create the shared data
|
||||
MCore::Array<SharedData*> sharedData;
|
||||
sharedData.SetMemoryCategory(EMFX_MEMCATEGORY_IMPORTER);
|
||||
PrepareSharedData(sharedData);
|
||||
|
||||
// load the file header
|
||||
FileFormat::MotionSet_Header fileHeader;
|
||||
f->Read(&fileHeader, sizeof(FileFormat::MotionSet_Header));
|
||||
if (fileHeader.mFourCC[0] != 'M' || fileHeader.mFourCC[1] != 'O' || fileHeader.mFourCC[2] != 'S' || fileHeader.mFourCC[3] != ' ')
|
||||
{
|
||||
MCore::LogError("The motion set file is not a valid file.");
|
||||
f->Close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// get the endian type
|
||||
MCore::Endian::EEndianType endianType = (MCore::Endian::EEndianType)fileHeader.mEndianType;
|
||||
|
||||
// init the import parameters
|
||||
ImportParameters params;
|
||||
params.mSharedData = &sharedData;
|
||||
params.mEndianType = endianType;
|
||||
params.m_isOwnedByRuntime = settings ? settings->m_isOwnedByRuntime : false;
|
||||
|
||||
// read the chunks
|
||||
while (ProcessChunk(f, params))
|
||||
{
|
||||
}
|
||||
|
||||
// close the file and return a pointer to the actor we loaded
|
||||
f->Close();
|
||||
|
||||
// get rid of shared data
|
||||
ResetSharedData(sharedData);
|
||||
sharedData.Clear();
|
||||
|
||||
// check if the motion set got set
|
||||
if (params.mMotionSet == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return params.mMotionSet;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
// load a node map by filename
|
||||
@@ -1087,19 +863,6 @@ namespace EMotionFX
|
||||
RegisterChunkProcessor(aznew ChunkProcessorMotionMorphSubMotions());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorMotionData());
|
||||
|
||||
// AnimGraph file format
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphParameters());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphNodeGroups());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphNode());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphStateTransitions());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphNodeConnections());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphGroupParameters());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphGameControllerSettings());
|
||||
RegisterChunkProcessor(aznew ChunkProcessorAnimGraphAdditionalInfo());
|
||||
|
||||
// motion set file format
|
||||
RegisterChunkProcessor(aznew ChunkProcessorMotionSet());
|
||||
|
||||
// node map
|
||||
RegisterChunkProcessor(aznew ChunkProcessorNodeMap());
|
||||
}
|
||||
@@ -1232,6 +995,18 @@ namespace EMotionFX
|
||||
return FILETYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
AZStd::string fileExtension;
|
||||
AZ::StringFunc::Path::GetExtension(filename, fileExtension);
|
||||
|
||||
if (fileExtension == ".animgraph")
|
||||
{
|
||||
return FILETYPE_ANIMGRAPH;
|
||||
}
|
||||
if (fileExtension == ".motionset")
|
||||
{
|
||||
return FILETYPE_MOTIONSET;
|
||||
}
|
||||
|
||||
// try to open the file from disk
|
||||
MCore::MemoryFile memoryFile;
|
||||
memoryFile.Open();
|
||||
@@ -1272,14 +1047,6 @@ namespace EMotionFX
|
||||
return FILETYPE_MOTION;
|
||||
}
|
||||
|
||||
// check for motion set
|
||||
file->Seek(0);
|
||||
if (CheckIfIsValidMotionSetFile(file, &endianType))
|
||||
{
|
||||
file->Close();
|
||||
return FILETYPE_MOTIONSET;
|
||||
}
|
||||
|
||||
// check for node map
|
||||
file->Seek(0);
|
||||
if (CheckIfIsValidNodeMapFile(file, &endianType))
|
||||
@@ -1288,14 +1055,6 @@ namespace EMotionFX
|
||||
return FILETYPE_NODEMAP;
|
||||
}
|
||||
|
||||
// check for anim graph
|
||||
file->Seek(0);
|
||||
if (CheckIfIsValidAnimGraphFile(file, &endianType))
|
||||
{
|
||||
file->Close();
|
||||
return FILETYPE_ANIMGRAPH;
|
||||
}
|
||||
|
||||
// close the file again
|
||||
file->Close();
|
||||
|
||||
@@ -1304,124 +1063,30 @@ namespace EMotionFX
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
||||
|
||||
// load anim graph by filename
|
||||
AnimGraph* Importer::LoadAnimGraph(AZStd::string filename, AnimGraphSettings* settings, const AZ::ObjectStream::FilterDescriptor& loadFilter)
|
||||
AnimGraph* Importer::LoadAnimGraph(AZStd::string filename, const AZ::ObjectStream::FilterDescriptor& loadFilter)
|
||||
{
|
||||
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePathKeepCase, filename);
|
||||
|
||||
const bool isLegacyFile = Importer::CheckFileType(filename.c_str()) == Importer::EFileType::FILETYPE_ANIMGRAPH;
|
||||
if (!isLegacyFile)
|
||||
AZ::SerializeContext* context = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!context)
|
||||
{
|
||||
AZ::SerializeContext* context = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!context)
|
||||
{
|
||||
AZ_Error("EMotionFX", false, "Can't get serialize context from component application.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EMotionFX::AnimGraph* animGraph = EMotionFX::AnimGraph::LoadFromFile(filename, context, loadFilter);
|
||||
if (animGraph)
|
||||
{
|
||||
animGraph->SetFileName(filename.c_str());
|
||||
animGraph->RemoveInvalidConnections(); // Remove connections that have nullptr source node's, which happens when connections point to unknown nodes.
|
||||
}
|
||||
|
||||
return animGraph;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Legacy file type loading
|
||||
//////////////////////////////////////////
|
||||
|
||||
// check if we want to load the anim graph even if a anim graph with the given filename is already inside the anim graph manager
|
||||
if (settings == nullptr || settings->mForceLoading == false)
|
||||
{
|
||||
// search the anim graph inside the anim graph manager and return it if it already got loaded
|
||||
AnimGraph* animGraph = GetAnimGraphManager().FindAnimGraphByFileName(filename.c_str());
|
||||
if (animGraph)
|
||||
{
|
||||
MCore::LogInfo(" + Anim graph '%s' already loaded, returning already loaded anim graph from the AnimGraphManager.", filename.c_str());
|
||||
return animGraph;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogInfo("- Trying to load anim graph from file '%s'...", filename.c_str());
|
||||
}
|
||||
|
||||
// try to open the file from disk
|
||||
MCore::DiskFile f;
|
||||
if (!f.Open(filename.c_str(), MCore::DiskFile::READ))
|
||||
{
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogError(" + Failed to open the file for anim graph '%s', anim graph not loaded!", filename.c_str());
|
||||
}
|
||||
AZ_Error("EMotionFX", false, "Can't get serialize context from component application.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// retrieve the filesize
|
||||
const size_t fileSize = f.GetFileSize();
|
||||
|
||||
// create a temporary buffer for the file
|
||||
uint8* fileBuffer = (uint8*)MCore::Allocate(fileSize, EMFX_MEMCATEGORY_IMPORTER);
|
||||
|
||||
// read in the complete file
|
||||
f.Read(fileBuffer, fileSize);
|
||||
|
||||
// close the file again
|
||||
f.Close();
|
||||
|
||||
// create the actor reading from memory
|
||||
AnimGraph* result = LoadAnimGraph(fileBuffer, fileSize, settings);
|
||||
if (result)
|
||||
EMotionFX::AnimGraph* animGraph = EMotionFX::AnimGraph::LoadFromFile(filename, context, loadFilter);
|
||||
if (animGraph)
|
||||
{
|
||||
result->SetFileName(filename.c_str());
|
||||
result->RemoveInvalidConnections(); // Remove connections that have nullptr source node's, which happens when connections point to unknown nodes.
|
||||
animGraph->SetFileName(filename.c_str());
|
||||
animGraph->RemoveInvalidConnections(); // Remove connections that have nullptr source node's, which happens when connections point to unknown nodes.
|
||||
}
|
||||
|
||||
// delete the filebuffer again
|
||||
MCore::Free(fileBuffer);
|
||||
|
||||
// check if it worked
|
||||
if (result == nullptr)
|
||||
{
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogError(" + Failed to load anim graph from file '%s'", filename.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetLogging())
|
||||
{
|
||||
MCore::LogInfo(" + Loading successfully finished");
|
||||
}
|
||||
}
|
||||
|
||||
// return the result
|
||||
return result;
|
||||
return animGraph;
|
||||
}
|
||||
|
||||
|
||||
// load the anim graph from memory
|
||||
AnimGraph* Importer::LoadAnimGraph(uint8* memoryStart, size_t lengthInBytes, AnimGraphSettings* settings)
|
||||
AnimGraph* Importer::LoadAnimGraph(uint8* memoryStart, size_t lengthInBytes)
|
||||
{
|
||||
// Legacy file type loading.
|
||||
MCore::MemoryFile memFile;
|
||||
memFile.Open(memoryStart, lengthInBytes);
|
||||
|
||||
const bool isLegacyFile = Importer::CheckFileType(&memFile) == Importer::EFileType::FILETYPE_ANIMGRAPH;
|
||||
if (isLegacyFile)
|
||||
{
|
||||
// Open the memory file again as CheckFileType() is closing it at the end.
|
||||
memFile.Open(memoryStart, lengthInBytes);
|
||||
return LoadAnimGraph(&memFile, settings);
|
||||
}
|
||||
|
||||
AZ::SerializeContext* context = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!context)
|
||||
@@ -1434,123 +1099,6 @@ namespace EMotionFX
|
||||
return animGraph;
|
||||
}
|
||||
|
||||
|
||||
// load a anim graph from a file object
|
||||
AnimGraph* Importer::LoadAnimGraph(MCore::File* f, AnimGraphSettings* settings)
|
||||
{
|
||||
MCORE_ASSERT(f);
|
||||
MCORE_ASSERT(f->GetIsOpen());
|
||||
|
||||
// execute the pre-passes
|
||||
if (f->GetType() != MCore::MemoryFile::TYPE_ID)
|
||||
{
|
||||
MCore::LogError("Given file is not a memory file. Cannot process pre-passes.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// copy over the actor settings, or use defaults
|
||||
AnimGraphSettings animGraphSettings;
|
||||
if (settings)
|
||||
{
|
||||
animGraphSettings = *settings;
|
||||
}
|
||||
|
||||
// create the shared data
|
||||
MCore::Array<SharedData*> sharedData;
|
||||
sharedData.SetMemoryCategory(EMFX_MEMCATEGORY_IMPORTER);
|
||||
PrepareSharedData(sharedData);
|
||||
|
||||
//-----------------------------------------------
|
||||
|
||||
// load the file header
|
||||
FileFormat::AnimGraph_Header fileHeader;
|
||||
f->Read(&fileHeader, sizeof(FileFormat::AnimGraph_Header));
|
||||
if (fileHeader.mFourCC[0] != 'A' || fileHeader.mFourCC[1] != 'N' || fileHeader.mFourCC[2] != 'G' || fileHeader.mFourCC[3] != 'R')
|
||||
{
|
||||
MCore::LogError("The anim graph file is not a valid anim graph file.");
|
||||
f->Close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// get the endian type
|
||||
MCore::Endian::EEndianType endianType = (MCore::Endian::EEndianType)fileHeader.mEndianType;
|
||||
|
||||
// convert endian of the integer values
|
||||
MCore::Endian::ConvertUnsignedInt32(&fileHeader.mFileVersion, endianType);
|
||||
MCore::Endian::ConvertUnsignedInt32(&fileHeader.mNumNodes, endianType);
|
||||
MCore::Endian::ConvertUnsignedInt32(&fileHeader.mNumStateTransitions, endianType);
|
||||
MCore::Endian::ConvertUnsignedInt32(&fileHeader.mNumNodeConnections, endianType);
|
||||
MCore::Endian::ConvertUnsignedInt32(&fileHeader.mNumParameters, endianType);
|
||||
|
||||
// read the anim graph name, create it, and read the other remaining info strings
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType);
|
||||
AnimGraph* animGraph = aznew AnimGraph();
|
||||
|
||||
if (GetLogDetails())
|
||||
{
|
||||
MCore::LogDetailedInfo("Anim Graph:");
|
||||
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // copyright
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // description
|
||||
MCore::LogDetailedInfo(" + Company = %s", SharedHelperData::ReadString(f, &sharedData, endianType)); // company
|
||||
MCore::LogDetailedInfo(" + EMotion FX Version= %s", SharedHelperData::ReadString(f, &sharedData, endianType)); // emfx version
|
||||
MCore::LogDetailedInfo(" + EMStudio Build = %s", SharedHelperData::ReadString(f, &sharedData, endianType)); // emstudio build
|
||||
MCore::LogDetailedInfo(" + Num nodes = %d", fileHeader.mNumNodes);
|
||||
MCore::LogDetailedInfo(" + Num transitions = %d", fileHeader.mNumStateTransitions);
|
||||
MCore::LogDetailedInfo(" + Num connections = %d", fileHeader.mNumNodeConnections);
|
||||
MCore::LogDetailedInfo(" + Num parameters = %d", fileHeader.mNumParameters);
|
||||
MCore::LogDetailedInfo(" + File version = %d", fileHeader.mFileVersion);
|
||||
MCore::LogDetailedInfo(" + Endian type = %d", fileHeader.mEndianType);
|
||||
}
|
||||
else
|
||||
{
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // copyright
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // description
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // company
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // emfx version
|
||||
SharedHelperData::ReadString(f, &sharedData, endianType); // emstudio build
|
||||
}
|
||||
|
||||
// init the import parameters
|
||||
ImportParameters params;
|
||||
params.mSharedData = &sharedData;
|
||||
params.mEndianType = endianType;
|
||||
params.mAnimGraph = animGraph;
|
||||
params.mAnimGraphSettings = &animGraphSettings;
|
||||
|
||||
// pre-allocate the blend nodes array to prevent reallocs
|
||||
MCore::Array<AnimGraphNode*>& blendNodes = SharedHelperData::GetBlendNodes(params.mSharedData);
|
||||
blendNodes.Reserve(fileHeader.mNumNodes);
|
||||
|
||||
// process all chunks
|
||||
while (ProcessChunk(f, params))
|
||||
{
|
||||
}
|
||||
|
||||
// close the file and return a pointer to the actor we loaded
|
||||
f->Close();
|
||||
|
||||
// get rid of shared data
|
||||
ResetSharedData(sharedData);
|
||||
sharedData.Clear();
|
||||
|
||||
// recursively update attributes of all state machines and blend tree nodes
|
||||
if (animGraph->GetRootStateMachine())
|
||||
{
|
||||
animGraph->InitAfterLoading();
|
||||
animGraph->RemoveInvalidConnections(true); // Remove connections that have nullptr source node's, which happens when connections point to unknown nodes.
|
||||
}
|
||||
else
|
||||
{
|
||||
delete animGraph;
|
||||
animGraph = nullptr;
|
||||
}
|
||||
|
||||
// return the created actor
|
||||
return animGraph;
|
||||
}
|
||||
|
||||
|
||||
// extract the file information from an actor file
|
||||
bool Importer::ExtractActorFileInfo(FileInfo* outInfo, const char* filename) const
|
||||
{
|
||||
|
||||
@@ -108,18 +108,15 @@ namespace EMotionFX
|
||||
MCore::Array<uint32> mChunkIDsToIgnore; /**< Add the ID's of the chunks you wish to ignore. */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The motion set import options.
|
||||
* This can be used in combination with the LoadMotionSet method.
|
||||
*/
|
||||
struct EMFX_API MotionSetSettings
|
||||
{
|
||||
bool mForceLoading = false; /**< Set to true in case you want to load the motion set even if a motion set with the given filename is already inside the motion manager. */
|
||||
bool m_isOwnedByRuntime = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The node map import options.
|
||||
* This can be used in combination with the LoadNodeMap method.
|
||||
@@ -130,30 +127,15 @@ namespace EMotionFX
|
||||
bool mLoadNodes = true; /**< Add nodes to the map? (default=true) */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The anim graph import settings.
|
||||
*/
|
||||
struct EMFX_API AnimGraphSettings
|
||||
{
|
||||
bool mForceLoading = false; /**< Set to true in case you want to load the anim graph even if an anim graph with the given filename is already inside the anim graph manager. */
|
||||
bool mDisableNodeVisualization = true; /**< Force disabling of node visualization code execution inside the anim graph nodes? */
|
||||
};
|
||||
|
||||
|
||||
struct EMFX_API ImportParameters
|
||||
{
|
||||
Actor* mActor = nullptr;
|
||||
Motion* mMotion = nullptr;
|
||||
MotionSet* mMotionSet = nullptr;
|
||||
Importer::ActorSettings* mActorSettings = nullptr;
|
||||
Importer::MotionSettings* mMotionSettings = nullptr;
|
||||
MCore::Array<SharedData*>* mSharedData = nullptr;
|
||||
MCore::Endian::EEndianType mEndianType = MCore::Endian::ENDIAN_LITTLE;
|
||||
|
||||
AnimGraph* mAnimGraph = nullptr;
|
||||
Importer::AnimGraphSettings* mAnimGraphSettings = nullptr;
|
||||
|
||||
NodeMap* mNodeMap = nullptr;
|
||||
Importer::NodeMapSettings* mNodeMapSettings = nullptr;
|
||||
bool m_isOwnedByRuntime = false;
|
||||
@@ -250,43 +232,24 @@ namespace EMotionFX
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load a anim graph file from a given file object.
|
||||
* @param f The file object.
|
||||
* @param settings The importer settings, or nullptr to use default settings.
|
||||
* @result The anim graph object, or nullptr when failed.
|
||||
*/
|
||||
AnimGraph* LoadAnimGraph(MCore::File* f, AnimGraphSettings* settings = nullptr);
|
||||
|
||||
/**
|
||||
* Load a anim graph file by filename.
|
||||
* @param filename The filename to load from.
|
||||
* @param settings The anim graph importer settings, or nullptr to use default settings.
|
||||
* @param loadFilter The filter descriptor for loading anim graph from file
|
||||
* @result The anim graph object, or nullptr in case loading failed.
|
||||
*/
|
||||
AnimGraph* LoadAnimGraph(AZStd::string, AnimGraphSettings* settings = nullptr, const AZ::ObjectStream::FilterDescriptor& loadFilter = AZ::ObjectStream::FilterDescriptor(nullptr, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES));
|
||||
AnimGraph* LoadAnimGraph(AZStd::string, const AZ::ObjectStream::FilterDescriptor& loadFilter = AZ::ObjectStream::FilterDescriptor(nullptr, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES));
|
||||
|
||||
/**
|
||||
* Load a anim graph file from a memory location.
|
||||
* @param memoryStart The start address of the file in memory.
|
||||
* @param lengthInBytes The length of the file, in bytes.
|
||||
* @param settings The settings to use during loading, or nullptr when you want to use default settings, which would load everything.
|
||||
* @result The anim graph object, or nullptr in case loading failed.
|
||||
*/
|
||||
AnimGraph* LoadAnimGraph(uint8* memoryStart, size_t lengthInBytes, AnimGraphSettings* settings = nullptr);
|
||||
AnimGraph* LoadAnimGraph(uint8* memoryStart, size_t lengthInBytes);
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load a motion set from a given file.
|
||||
* A file does not have to be stored on disk, but can also be in memory or in an archive or on some network stream. Anything is possible.
|
||||
* @param f The file to load the motion set from (after load, the file will be closed).
|
||||
* @param settings The motion set importer settings, or nullptr to use default settings.
|
||||
* @result The motion set object, or nullptr in case loading failed.
|
||||
*/
|
||||
MotionSet* LoadMotionSet(MCore::File* f, MotionSetSettings* settings = nullptr);
|
||||
|
||||
/**
|
||||
* Loads a motion set from a file on disk.
|
||||
* @param filename The name of the file on disk.
|
||||
@@ -430,30 +393,6 @@ namespace EMotionFX
|
||||
*/
|
||||
bool CheckIfIsValidMotionFile(MCore::File* f, MCore::Endian::EEndianType* outEndianType) const;
|
||||
|
||||
/**
|
||||
* Verify if the given file is a valid motion set file that can be processed by the importer.
|
||||
* Please note that the specified must already been opened and must also be pointing to the location where the
|
||||
* XPM file header will be stored (the start of the file). The file will not be closed after this method!
|
||||
* The endian type of the file will be written inside the outEndianType parameter.
|
||||
* Also note that the file position (read position / cursor) will point after the header after this function has been executed.
|
||||
* @param f The file to perform the check on.
|
||||
* @param outEndianType The value that will contain the endian type used by the file.
|
||||
* @result Returns true when the file is a valid actor file that can be processed by the importer. Otherwise false is returned.
|
||||
*/
|
||||
bool CheckIfIsValidMotionSetFile(MCore::File* f, MCore::Endian::EEndianType* outEndianType) const;
|
||||
|
||||
/**
|
||||
* Verify if the given file is a valid anim graph file that can be processed by the importer.
|
||||
* Please note that the specified must already been opened and must also be pointing to the location where the
|
||||
* XPM file header will be stored (the start of the file). The file will not be closed after this method!
|
||||
* The endian type of the file will be written inside the outEndianType parameter.
|
||||
* Also note that the file position (read position / cursor) will point after the header after this function has been executed.
|
||||
* @param f The file to perform the check on.
|
||||
* @param outEndianType The value that will contain the endian type used by the file.
|
||||
* @result Returns true when the file is a valid actor file that can be processed by the importer. Otherwise false is returned.
|
||||
*/
|
||||
bool CheckIfIsValidAnimGraphFile(MCore::File* f, MCore::Endian::EEndianType* outEndianType) const;
|
||||
|
||||
/**
|
||||
* Verify if the given file is a valid node map file that can be processed by the importer.
|
||||
* Please note that the specified must already been opened and must also be pointing to the location where the
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,259 +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 <AzCore/std/string/string.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <MCore/Source/Endian.h>
|
||||
#include <MCore/Source/LogManager.h>
|
||||
#include "Importer.h"
|
||||
#include "AnimGraphFileFormat.h"
|
||||
#include "../AnimGraphObject.h"
|
||||
#include <MCore/Source/File.h>
|
||||
#include <EMotionFX/Source/AnimGraphObjectIds.h>
|
||||
#include "../AnimGraphNode.h"
|
||||
|
||||
|
||||
namespace EMotionFX
|
||||
{
|
||||
class AnimGraphNode;
|
||||
class AnimGraphStateMachine;
|
||||
class AnimGraphStateTransition;
|
||||
class AnimGraphTransitionCondition;
|
||||
class BlendTreeVector4ComposeNode;
|
||||
class BlendTreeVector3ComposeNode;
|
||||
class BlendTreeVector2ComposeNode;
|
||||
class BlendTreeMorphTargetNode;
|
||||
class BlendTreeFloatConstantNode;
|
||||
class BlendTreeLookAtNode;
|
||||
class BlendTreeTwoLinkIKNode;
|
||||
class BlendTreeFloatMath1Node;
|
||||
class AnimGraphStateTransition;
|
||||
class AnimGraphStateMachine;
|
||||
class BlendTreeRangeRemapperNode;
|
||||
class BlendTreeSmoothingNode;
|
||||
class BlendTreeVector3Math2Node;
|
||||
class BlendTreeVector3Math1Node;
|
||||
class BlendTreeFloatMath2Node;
|
||||
class BlendTreeBlend2LegacyNode;
|
||||
class AnimGraphVector2Condition;
|
||||
class AnimGraphTimeCondition;
|
||||
class AnimGraphTagCondition;
|
||||
class AnimGraphStateCondition;
|
||||
class AnimGraphPlayTimeCondition;
|
||||
class AnimGraphParameterCondition;
|
||||
class AnimGraphMotionCondition;
|
||||
class BlendTreeBlendNNode;
|
||||
class BlendTreeMaskLegacyNode;
|
||||
class BlendTreeTransformNode;
|
||||
class BlendTreeAccumTransformNode;
|
||||
class AnimGraphMotionNode;
|
||||
class BlendTreeVector4DecomposeNode;
|
||||
class BlendTreeVector3DecomposeNode;
|
||||
class BlendTreeVector2DecomposeNode;
|
||||
class BlendTree;
|
||||
class BlendTreePoseSwitchNode;
|
||||
class BlendSpace2DNode;
|
||||
class BlendSpace1DNode;
|
||||
class BlendTreeParameterNode;
|
||||
class AnimGraphExitNode;
|
||||
class AnimGraphEntryNode;
|
||||
class BlendTreeMirrorPoseNode;
|
||||
class BlendTreeDirectionToWeightNode;
|
||||
class AnimGraphBindPoseNode;
|
||||
class BlendTreeFinalNode;
|
||||
class AnimGraphNode;
|
||||
class BlendTreeMotionFrameNode;
|
||||
class BlendTreeFloatConditionNode;
|
||||
class BlendTreeFloatSwitchNode;
|
||||
class BlendTreeBoolLogicNode;
|
||||
|
||||
const AZ::TypeId GetNewTypeIdByOldNodeTypeId(uint32 oldNodeTypeId);
|
||||
|
||||
enum LegacyAttributeTypeId
|
||||
{
|
||||
ATTRIBUTE_FLOAT_TYPE_ID = 0x00000001,
|
||||
ATTRIBUTE_INT32_TYPE_ID = 0x00000002,
|
||||
ATTRIBUTE_BOOL_TYPE_ID = 0x00000004
|
||||
};
|
||||
|
||||
enum LegacyERotationOrder
|
||||
{
|
||||
ROTATIONORDER_ZYX = 0,
|
||||
ROTATIONORDER_ZXY = 1,
|
||||
ROTATIONORDER_YZX = 2,
|
||||
ROTATIONORDER_YXZ = 3,
|
||||
ROTATIONORDER_XYZ = 4,
|
||||
ROTATIONORDER_XZY = 5
|
||||
};
|
||||
|
||||
|
||||
class LegacyAttributeHeader
|
||||
{
|
||||
public:
|
||||
LegacyAttributeHeader() { }
|
||||
|
||||
AZ::u32 GetAttributeType() const
|
||||
{
|
||||
return m_attribType;
|
||||
}
|
||||
|
||||
AZ::u32 GetAttributeSize() const
|
||||
{
|
||||
return m_attributeSize;
|
||||
}
|
||||
|
||||
static bool Parse(MCore::File* stream, MCore::Endian::EEndianType endianType, LegacyAttributeHeader& attributeHeader);
|
||||
private:
|
||||
LegacyAttributeHeader(const LegacyAttributeHeader& src) :
|
||||
m_attribType(src.m_attribType)
|
||||
, m_attributeSize(src.m_attributeSize)
|
||||
, m_name(src.m_name)
|
||||
{ }
|
||||
|
||||
AZ::u32 m_attribType;
|
||||
AZ::u32 m_attributeSize;
|
||||
AZStd::string m_name;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class LegacyAttribute
|
||||
{
|
||||
public:
|
||||
bool Parse(MCore::File* stream, MCore::Endian::EEndianType endianType);
|
||||
const T& GetValue() const;
|
||||
private:
|
||||
T m_value;
|
||||
};
|
||||
|
||||
class LegacyAnimGraphNodeParser
|
||||
{
|
||||
public:
|
||||
static bool ParseAnimGraphNodeChunk(MCore::File* file,
|
||||
Importer::ImportParameters& importParams,
|
||||
const char* nodeName,
|
||||
FileFormat::AnimGraph_NodeHeader& nodeHeader,
|
||||
AnimGraphNode*& node);
|
||||
|
||||
static bool ParseTransitionConditionChunk(MCore::File* file,
|
||||
Importer::ImportParameters& importParams,
|
||||
const FileFormat::AnimGraph_NodeHeader& nodeHeader,
|
||||
AnimGraphTransitionCondition*& transitionCondition);
|
||||
|
||||
template <class T>
|
||||
static bool ParseLegacyAttributes(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
|
||||
static bool Forward(MCore::File* stream, size_t numBytes);
|
||||
|
||||
private:
|
||||
static bool InitializeNodeGeneralData(const char* nodeName, Importer::ImportParameters& importParams, FileFormat::AnimGraph_NodeHeader& nodeHeader, AnimGraphNode* node);
|
||||
static bool GetBlendSpaceNodeEvaluatorTypeId(uint32 legacyIndex, AZ::TypeId& value);
|
||||
static bool ConvertFloatAttributeValueToBool(float value);
|
||||
static bool TryGetFloatFromAttribute(MCore::File* stream, MCore::Endian::EEndianType endianType, const LegacyAttributeHeader& attributeHeader, float& outputValue);
|
||||
|
||||
template <class T>
|
||||
static bool ParseAnimGraphNode(MCore::File* file,
|
||||
Importer::ImportParameters& importParams,
|
||||
const char* nodeName,
|
||||
FileFormat::AnimGraph_NodeHeader& nodeHeader,
|
||||
AnimGraphNode*& node)
|
||||
{
|
||||
node = aznew T();
|
||||
node->SetAnimGraph(importParams.mAnimGraph);
|
||||
if (!InitializeNodeGeneralData(nodeName, importParams, nodeHeader, node))
|
||||
{
|
||||
MCore::LogError("Error on initializing node general data");
|
||||
return false;
|
||||
}
|
||||
if (!ParseLegacyAttributes<T>(file, nodeHeader.mNumAttributes, importParams.mEndianType, importParams, *node))
|
||||
{
|
||||
MCore::LogError("Unable to parse node legacy attributes");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static bool ParseAnimGraphTransitionCondition(MCore::File* file,
|
||||
Importer::ImportParameters& importParams,
|
||||
const FileFormat::AnimGraph_NodeHeader& header,
|
||||
AnimGraphTransitionCondition*& transitionCondition)
|
||||
{
|
||||
transitionCondition = aznew T();
|
||||
return ParseLegacyAttributes<T>(file, header.mNumAttributes, importParams.mEndianType, importParams, *transitionCondition);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, template<class> class LegacyAttribute>
|
||||
class LegacyAttributeArray
|
||||
{
|
||||
public:
|
||||
bool Parse(MCore::File* stream, MCore::Endian::EEndianType endianType);
|
||||
const AZStd::vector< LegacyAttribute<T> >& GetValue() const;
|
||||
private:
|
||||
bool PopulateAttributeDynamicArray(MCore::File* stream, MCore::Endian::EEndianType endianType);
|
||||
|
||||
AZStd::vector< LegacyAttribute<T> > m_attributes;
|
||||
// Used when reading version 2 attribute array
|
||||
uint32 m_elementTypeId;
|
||||
};
|
||||
|
||||
class LegacyAttributeSettingsParser
|
||||
{
|
||||
public:
|
||||
static bool Parse(MCore::File* stream, MCore::Endian::EEndianType endianType);
|
||||
};
|
||||
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector4ComposeNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector3ComposeNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector2ComposeNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeMorphTargetNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeFloatConstantNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeLookAtNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeTwoLinkIKNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeFloatMath1Node>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphStateTransition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphStateMachine>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeRangeRemapperNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeSmoothingNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector3Math2Node>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector3Math1Node>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeFloatMath2Node>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeBlend2LegacyNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphVector2Condition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphTimeCondition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphTagCondition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphStateCondition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphPlayTimeCondition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphParameterCondition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphMotionCondition>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeBlendNNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeMaskLegacyNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeTransformNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeAccumTransformNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphMotionNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector4DecomposeNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector3DecomposeNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeVector2DecomposeNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTree>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreePoseSwitchNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendSpace2DNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendSpace1DNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeParameterNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphExitNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphEntryNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeMirrorPoseNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeDirectionToWeightNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphBindPoseNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeFinalNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<AnimGraphNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeMotionFrameNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeFloatConditionNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeFloatSwitchNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
template<> bool LegacyAnimGraphNodeParser::ParseLegacyAttributes<BlendTreeBoolLogicNode>(MCore::File* stream, uint32 numAttributes, MCore::Endian::EEndianType endianType, Importer::ImportParameters& importParams, AnimGraphObject& animGraphObject);
|
||||
} // Namespace EMotionFX
|
||||
@@ -1,60 +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 the shared structs
|
||||
#include "SharedFileFormatStructs.h"
|
||||
|
||||
|
||||
namespace EMotionFX
|
||||
{
|
||||
namespace FileFormat
|
||||
{
|
||||
// the chunks
|
||||
enum
|
||||
{
|
||||
CHUNK_MOTIONSET = 500
|
||||
};
|
||||
|
||||
|
||||
struct MotionSet_Header
|
||||
{
|
||||
uint8 mFourCC[4]; // must be "MOS "
|
||||
uint8 mHiVersion; // high version (2 in case of v2.34)
|
||||
uint8 mLoVersion; // low version (34 in case of v2.34)
|
||||
uint8 mEndianType; // the endian in which the data is saved [0=little, 1=big]
|
||||
};
|
||||
|
||||
|
||||
struct MotionSetsChunk
|
||||
{
|
||||
uint32 mNumSets; // the number of motion sets
|
||||
|
||||
// followed by:
|
||||
// motionSets[mNumSets]
|
||||
};
|
||||
|
||||
|
||||
struct MotionSetChunk
|
||||
{
|
||||
uint32 mNumChildSets; // the number of child motion sets
|
||||
uint32 mNumMotionEntries; // the number of motion entries
|
||||
|
||||
// followed by:
|
||||
// string : the name of the parent set
|
||||
// string : the name of the motion set
|
||||
// string : the filename and path information (e.g. "Root/Motions/Human_Male/" // obsolete, this got removed and is now just an empty string
|
||||
// motionEntries[mNumMotionEntries]: motion entries
|
||||
// MotionEntry:
|
||||
// string : motion filename without path (e.g. "Walk.motion")
|
||||
// string : motion set string id (e.g. "WALK")
|
||||
};
|
||||
} // namespace FileFormat
|
||||
} // namespace EMotionFX
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <MCore/Source/Config.h>
|
||||
|
||||
namespace EMotionFX
|
||||
@@ -20,8 +21,6 @@ namespace EMotionFX
|
||||
SHARED_CHUNK_TIMESTAMP = 51
|
||||
};
|
||||
|
||||
|
||||
// a chunk
|
||||
struct FileChunk
|
||||
{
|
||||
uint32 mChunkID; // the chunk ID
|
||||
@@ -29,7 +28,6 @@ namespace EMotionFX
|
||||
uint32 mVersion; // the version of the chunk
|
||||
};
|
||||
|
||||
|
||||
// color [0..1] range
|
||||
struct FileColor
|
||||
{
|
||||
@@ -39,16 +37,12 @@ namespace EMotionFX
|
||||
float mA; // alpha
|
||||
};
|
||||
|
||||
|
||||
// a 2D vector
|
||||
struct FileVector2
|
||||
{
|
||||
float mX;
|
||||
float mY;
|
||||
};
|
||||
|
||||
|
||||
// a 3D vector
|
||||
struct FileVector3
|
||||
{
|
||||
float mX; // x+ = to the right
|
||||
@@ -56,7 +50,6 @@ namespace EMotionFX
|
||||
float mZ; // z+ = up
|
||||
};
|
||||
|
||||
|
||||
// a compressed 3D vector
|
||||
struct File16BitVector3
|
||||
{
|
||||
@@ -65,7 +58,6 @@ namespace EMotionFX
|
||||
uint16 mZ; // z+ = up
|
||||
};
|
||||
|
||||
|
||||
// a compressed 3D vector
|
||||
struct File8BitVector3
|
||||
{
|
||||
@@ -74,8 +66,6 @@ namespace EMotionFX
|
||||
uint8 mZ; // z+ = up
|
||||
};
|
||||
|
||||
|
||||
// a quaternion
|
||||
struct FileQuaternion
|
||||
{
|
||||
float mX;
|
||||
@@ -84,7 +74,6 @@ namespace EMotionFX
|
||||
float mW;
|
||||
};
|
||||
|
||||
|
||||
// the 16 bit component quaternion
|
||||
struct File16BitQuaternion
|
||||
{
|
||||
@@ -94,7 +83,6 @@ namespace EMotionFX
|
||||
int16 mW;
|
||||
};
|
||||
|
||||
|
||||
// a time stamp chunk
|
||||
struct FileTime
|
||||
{
|
||||
@@ -105,17 +93,5 @@ namespace EMotionFX
|
||||
int8 mMinutes;
|
||||
int8 mSeconds;
|
||||
};
|
||||
|
||||
|
||||
// attribute
|
||||
struct FileAttribute
|
||||
{
|
||||
uint32 mDataType;
|
||||
uint32 mNumBytes;
|
||||
uint32 mFlags;
|
||||
|
||||
// followed by:
|
||||
// uint8 mData[mNumBytes];
|
||||
};
|
||||
} // namespace FileFormat
|
||||
} // namespace EMotionFX
|
||||
} // namespace EMotionFX
|
||||
|
||||
@@ -364,16 +364,11 @@ set(FILES
|
||||
Source/TwoStringEventData.h
|
||||
Source/Importer/ChunkProcessors.cpp
|
||||
Source/Importer/ChunkProcessors.h
|
||||
Source/Importer/LegacyAnimGraphNodeParser.cpp
|
||||
Source/Importer/LegacyAnimGraphNodeParser.h
|
||||
Source/Importer/Importer.cpp
|
||||
Source/Importer/Importer.h
|
||||
Source/Importer/MotionSetFileFormat.h
|
||||
Source/Importer/NodeMapFileFormat.h
|
||||
Source/Importer/SharedFileFormatStructs.h
|
||||
Source/Importer/ActorFileFormat.h
|
||||
Source/Importer/AnimGraphFileFormat.cpp
|
||||
Source/Importer/AnimGraphFileFormat.h
|
||||
Source/Importer/MotionFileFormat.h
|
||||
Source/Parameter/BoolParameter.cpp
|
||||
Source/Parameter/BoolParameter.h
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
// include required headers
|
||||
#include "Attribute.h"
|
||||
#include "AttributeFactory.h"
|
||||
#include "AttributeString.h"
|
||||
@@ -14,20 +13,15 @@
|
||||
|
||||
namespace MCore
|
||||
{
|
||||
// constructor
|
||||
Attribute::Attribute(uint32 typeID)
|
||||
{
|
||||
mTypeID = typeID;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
Attribute::~Attribute()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// equal operator
|
||||
Attribute& Attribute::operator=(const Attribute& other)
|
||||
{
|
||||
if (&other != this)
|
||||
@@ -36,25 +30,4 @@ namespace MCore
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// read the attribute
|
||||
bool Attribute::Read(Stream* stream, Endian::EEndianType sourceEndianType)
|
||||
{
|
||||
// read the version
|
||||
uint8 version;
|
||||
if (stream->Read(&version, sizeof(uint8)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the data
|
||||
const bool result = ReadData(stream, sourceEndianType, version);
|
||||
if (result == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MCore
|
||||
} // namespace MCore
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// include the required headers
|
||||
#include "StandardHeaders.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "Endian.h"
|
||||
@@ -28,7 +27,6 @@ namespace MCore
|
||||
// forward declarations
|
||||
class AttributeSettings;
|
||||
|
||||
|
||||
// the attribute interface types
|
||||
enum : uint32
|
||||
{
|
||||
@@ -49,12 +47,6 @@ namespace MCore
|
||||
ATTRIBUTE_INTERFACETYPE_DEFAULT = 0xFFFFFFFF// use the default attribute type that the specific attribute class defines as default
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class MCORE_API Attribute
|
||||
{
|
||||
friend class AttributeFactory;
|
||||
@@ -70,10 +62,6 @@ namespace MCore
|
||||
virtual uint32 GetClassSize() const = 0;
|
||||
virtual uint32 GetDefaultInterfaceType() const = 0;
|
||||
|
||||
// These two members and ReadData can go away once we put the old-format parser
|
||||
bool Read(Stream* stream, MCore::Endian::EEndianType sourceEndianType);
|
||||
virtual uint32 GetDataSize() const = 0; // data only
|
||||
|
||||
Attribute& operator=(const Attribute& other);
|
||||
|
||||
virtual void NetworkSerialize(EMotionFX::Network::AnimGraphSnapshotChunkSerializer&) {};
|
||||
@@ -82,16 +70,5 @@ namespace MCore
|
||||
uint32 mTypeID; /**< The unique type ID of the attribute class. */
|
||||
|
||||
Attribute(uint32 typeID);
|
||||
|
||||
/**
|
||||
* Read the attribute info and data from a given stream.
|
||||
* Please note that the endian information of the actual data is not being converted. You have to handle that yourself.
|
||||
* The data endian conversion could be done with for example the static Attribute::ConvertDataEndian method.
|
||||
* @param stream The stream to read the info and data from.
|
||||
* @param endianType The endian type in which the data is stored in the stream.
|
||||
* @param version The version of the attribute.
|
||||
* @result Returns true when successful, or false when reading failed.
|
||||
*/
|
||||
virtual bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) = 0;
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -63,23 +63,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) {}
|
||||
~AttributeBool() {}
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(int8); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
MCORE_UNUSED(streamEndianType);
|
||||
int8 streamValue;
|
||||
if (stream->Read(&streamValue, sizeof(int8)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mValue = (streamValue == 0) ? false : true;
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -79,27 +79,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) { }
|
||||
~AttributeColor() {}
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(RGBAColor); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
// read the value
|
||||
RGBAColor streamValue;
|
||||
if (stream->Read(&streamValue, sizeof(RGBAColor)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert endian
|
||||
Endian::ConvertRGBAColor(&streamValue, streamEndianType);
|
||||
mValue = streamValue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -57,8 +57,6 @@ namespace MCore
|
||||
private:
|
||||
float mValue; /**< The float value. */
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(float); }
|
||||
|
||||
AttributeFloat()
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(0.0f) {}
|
||||
@@ -66,21 +64,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) {}
|
||||
~AttributeFloat() {}
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
float streamValue;
|
||||
if (stream->Read(&streamValue, sizeof(float)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Endian::ConvertFloat(&streamValue, streamEndianType);
|
||||
mValue = streamValue;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -64,23 +64,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) {}
|
||||
~AttributeInt32() {}
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(int32); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
int32 streamValue;
|
||||
if (stream->Read(&streamValue, sizeof(int32)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Endian::ConvertSignedInt32(&streamValue, streamEndianType);
|
||||
mValue = streamValue;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -66,18 +66,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(pointer) { }
|
||||
~AttributePointer() {}
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(void*); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(stream);
|
||||
MCORE_UNUSED(streamEndianType);
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
MCore::LogWarning("MCore::AttributePointer::ReadData() - Pointer attributes cannot be read from disk.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -82,26 +82,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) {}
|
||||
~AttributeQuaternion() { }
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(AZ::Quaternion); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
// read the value
|
||||
AZ::Quaternion streamValue;
|
||||
if (stream->Read(&streamValue, sizeof(AZ::Quaternion)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert endian
|
||||
Endian::ConvertQuaternion(&streamValue, streamEndianType);
|
||||
mValue = streamValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -72,40 +72,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) { }
|
||||
~AttributeString() { mValue.clear(); }
|
||||
|
||||
uint32 GetDataSize() const override
|
||||
{
|
||||
return sizeof(uint32) + static_cast<uint32>(mValue.size());
|
||||
}
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
// read the number of characters
|
||||
uint32 numCharacters;
|
||||
if (stream->Read(&numCharacters, sizeof(uint32)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert endian
|
||||
Endian::ConvertUnsignedInt32(&numCharacters, streamEndianType);
|
||||
if (numCharacters == 0)
|
||||
{
|
||||
mValue.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
mValue.resize(numCharacters);
|
||||
if (stream->Read(mValue.data(), numCharacters) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -78,27 +78,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) { }
|
||||
~AttributeVector2() { }
|
||||
|
||||
uint32 GetDataSize() const override { return sizeofVector2; }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
// read the value
|
||||
AZ::Vector2 streamValue;
|
||||
if (stream->Read(&streamValue, sizeofVector2) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert endian
|
||||
Endian::ConvertVector2(&streamValue, streamEndianType);
|
||||
mValue = streamValue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -79,27 +79,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) { }
|
||||
~AttributeVector3() { }
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(AZ::Vector3); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
// read the value
|
||||
AZ::PackedVector3f streamValue(0.0f);
|
||||
if (stream->Read(&streamValue, sizeof(AZ::PackedVector3f)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert endian
|
||||
mValue = AZ::Vector3(streamValue.GetX(), streamValue.GetY(), streamValue.GetZ());
|
||||
Endian::ConvertVector3(&mValue, streamEndianType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -75,27 +75,5 @@ namespace MCore
|
||||
: Attribute(TYPE_ID)
|
||||
, mValue(value) { }
|
||||
~AttributeVector4() { }
|
||||
|
||||
uint32 GetDataSize() const override { return sizeof(AZ::Vector4); }
|
||||
|
||||
// read from a stream
|
||||
bool ReadData(MCore::Stream* stream, MCore::Endian::EEndianType streamEndianType, uint8 version) override
|
||||
{
|
||||
MCORE_UNUSED(version);
|
||||
|
||||
// read the value
|
||||
AZ::Vector4 streamValue;
|
||||
if (stream->Read(&streamValue, sizeof(AZ::Vector4)) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert endian
|
||||
Endian::ConvertVector4(&streamValue, streamEndianType);
|
||||
mValue = streamValue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace MCore
|
||||
|
||||
@@ -53,8 +53,7 @@ namespace EMotionFX
|
||||
AnimGraphAsset* assetData = asset.GetAs<AnimGraphAsset>();
|
||||
assetData->m_emfxAnimGraph.reset(EMotionFX::GetImporter().LoadAnimGraph(
|
||||
assetData->m_emfxNativeData.data(),
|
||||
assetData->m_emfxNativeData.size(),
|
||||
nullptr));
|
||||
assetData->m_emfxNativeData.size()));
|
||||
|
||||
if (assetData->m_emfxAnimGraph)
|
||||
{
|
||||
|
||||
@@ -97,16 +97,6 @@ namespace EMotionFX
|
||||
ASSERT_EQ(productDependencies.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(EMotionFXBuilderTests, TestLegacyAnimGraphAsset_NoDependency_OutputNoProductDependencies)
|
||||
{
|
||||
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/LegacyAnimGraphExample.animgraph";
|
||||
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
|
||||
EMotionFXBuilder::AnimGraphBuilderWorker builderWorker;
|
||||
|
||||
ASSERT_TRUE(builderWorker.ParseProductDependencies(ResolvePath(fileName.c_str()), fileName, productDependencies));
|
||||
ASSERT_EQ(productDependencies.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_HasReferenceNode_OutputProductDependencies)
|
||||
{
|
||||
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset";
|
||||
@@ -150,14 +140,4 @@ namespace EMotionFX
|
||||
AZ_TEST_STOP_ASSERTTEST(2);
|
||||
ASSERT_EQ(productDependencies.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(EMotionFXBuilderTests, TestLegacyMotionSetAsset_ReferenceMotionAssets_OutputProductDependencies)
|
||||
{
|
||||
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/LegacyMotionSetExample.motionset";
|
||||
ProductPathDependencySet productDependencies;
|
||||
EMotionFXBuilder::MotionSetBuilderWorker builderWorker;
|
||||
|
||||
ASSERT_TRUE(builderWorker.ParseProductDependencies(ResolvePath(fileName.c_str()), fileName, productDependencies));
|
||||
ASSERT_EQ(productDependencies.size(), 25);
|
||||
}
|
||||
} // namespace EMotionFX
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:33ff8a4f75b5ad918a833113fb45a21e9075b66a9d0ed8563ee34d93ae905aaf
|
||||
size 50863
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8ed5c6c8376df150ebddc8b16714012991c1892a94d2b8ad943a7f96563771e0
|
||||
size 2518
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:33ff8a4f75b5ad918a833113fb45a21e9075b66a9d0ed8563ee34d93ae905aaf
|
||||
size 50863
|
||||
oid sha256:8825feb4f0946979f8a1258cdf20bb71e358b7597eca1aaed552c2b0a17b2bd9
|
||||
size 464733
|
||||
|
||||
@@ -191,7 +191,6 @@ namespace GraphCanvas
|
||||
|
||||
void GraphCanvasSystemComponent::Activate()
|
||||
{
|
||||
RegisterAssetHandler();
|
||||
RegisterTranslationBuilder();
|
||||
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
@@ -386,34 +385,6 @@ namespace GraphCanvas
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, collectAssetsCb, postEnumerateCb);
|
||||
}
|
||||
|
||||
void GraphCanvasSystemComponent::RegisterAssetHandler()
|
||||
{
|
||||
AZ::Data::AssetType assetType(azrtti_typeid<TranslationAsset>());
|
||||
if (AZ::Data::AssetManager::Instance().GetHandler(assetType))
|
||||
{
|
||||
return; // Asset Type already handled
|
||||
}
|
||||
|
||||
auto* catalogBus = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
|
||||
if (catalogBus)
|
||||
{
|
||||
// Register asset types the asset DB should query our catalog for.
|
||||
catalogBus->AddAssetType(assetType);
|
||||
|
||||
// Build the catalog (scan).
|
||||
catalogBus->AddExtension(".names");
|
||||
}
|
||||
|
||||
m_assetHandler = AZStd::make_unique<TranslationAssetHandler>();
|
||||
AZ::Data::AssetManager::Instance().RegisterHandler(m_assetHandler.get(), assetType);
|
||||
|
||||
// Use AssetCatalog service to register ScriptEvent asset type and extension
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddAssetType, assetType);
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::EnableCatalogForAsset, assetType);
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension, TranslationAsset::GetFileFilter());
|
||||
|
||||
}
|
||||
|
||||
void GraphCanvasSystemComponent::UnregisterAssetHandler()
|
||||
{
|
||||
if (m_assetHandler)
|
||||
|
||||
@@ -82,8 +82,6 @@ namespace GraphCanvas
|
||||
AZStd::unique_ptr<TranslationAssetHandler> m_assetHandler;
|
||||
|
||||
void RegisterTranslationBuilder();
|
||||
|
||||
void RegisterAssetHandler();
|
||||
void UnregisterAssetHandler();
|
||||
TranslationAssetWorker m_translationAssetWorker;
|
||||
AZStd::vector<AZ::Data::AssetId> m_translationAssets;
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace LmbrCentral
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<PolygonPrismShapeComponentRequestBus>("PolygonPrismShapeComponentRequestBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Shape")
|
||||
->Attribute(AZ::Script::Attributes::Module, "shape")
|
||||
->Event("GetPolygonPrism", &PolygonPrismShapeComponentRequestBus::Events::GetPolygonPrism)
|
||||
|
||||
@@ -23,36 +23,30 @@ namespace Multiplayer
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile)
|
||||
{
|
||||
incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::Activate()
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::Deactivate()
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
ImGui::ImGuiUpdateListenerBus::Handler::BusDisconnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IMGUI_ENABLED
|
||||
void MultiplayerDebugSystemComponent::OnImGuiMainMenuUpdate()
|
||||
{
|
||||
@@ -63,7 +57,6 @@ namespace Multiplayer
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond)
|
||||
{
|
||||
uint64_t summedCalls = 0;
|
||||
@@ -80,8 +73,9 @@ namespace Multiplayer
|
||||
|
||||
bool DrawMetricsRow(const char* name, bool expandable, uint64_t totalCalls, uint64_t totalBytes, float callsPerSecond, float bytesPerSecond)
|
||||
{
|
||||
const ImGuiTreeNodeFlags flags = expandable ? ImGuiTreeNodeFlags_SpanFullWidth
|
||||
: (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
const ImGuiTreeNodeFlags flags = expandable
|
||||
? ImGuiTreeNodeFlags_SpanFullWidth
|
||||
: (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
const bool open = ImGui::TreeNodeEx(name, flags);
|
||||
@@ -95,14 +89,12 @@ namespace Multiplayer
|
||||
ImGui::Text("%11.2f", bytesPerSecond);
|
||||
return open;
|
||||
}
|
||||
|
||||
bool DrawSummaryRow(const char* name, const MultiplayerStats& stats)
|
||||
{
|
||||
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
|
||||
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics();
|
||||
const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics();
|
||||
const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics();
|
||||
|
||||
const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
|
||||
const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -111,17 +103,14 @@ namespace Multiplayer
|
||||
AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
|
||||
|
||||
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
|
||||
}
|
||||
|
||||
bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId)
|
||||
{
|
||||
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
|
||||
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
|
||||
const MultiplayerStats::Metric rpcsSent = stats.CalculateComponentRpcsSentMetrics(netComponentId);
|
||||
const MultiplayerStats::Metric rpcsRecv = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
|
||||
|
||||
const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
|
||||
const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -130,10 +119,8 @@ namespace Multiplayer
|
||||
AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
|
||||
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
|
||||
|
||||
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
|
||||
}
|
||||
|
||||
void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
|
||||
{
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
@@ -158,7 +145,6 @@ namespace Multiplayer
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -180,7 +166,6 @@ namespace Multiplayer
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsSentMetrics(netComponentId);
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -202,7 +187,6 @@ namespace Multiplayer
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
|
||||
float callsPerSecond = 0.0f;
|
||||
@@ -226,45 +210,218 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::OnImGuiUpdate()
|
||||
void DrawNetworkingStats()
|
||||
{
|
||||
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
const ImGuiTableFlags flags = ImGuiTableFlags_BordersV
|
||||
| ImGuiTableFlags_BordersOuterH
|
||||
| ImGuiTableFlags_Resizable
|
||||
| ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_NoBordersInBody;
|
||||
|
||||
const ImGuiTreeNodeFlags nodeFlags = (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
|
||||
AzNetworking::INetworking* networking = AZ::Interface<AzNetworking::INetworking>::Get();
|
||||
|
||||
ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(networking->GetTcpListenThreadUpdateTime()));
|
||||
ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(networking->GetUdpReaderThreadUpdateTime()));
|
||||
ImGui::NewLine();
|
||||
|
||||
for (auto& networkInterface : networking->GetNetworkInterfaces())
|
||||
{
|
||||
if (ImGui::CollapsingHeader(networkInterface.second->GetName().GetCStr()))
|
||||
{
|
||||
const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp";
|
||||
const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer";
|
||||
const uint32_t port = aznumeric_cast<uint32_t>(networkInterface.second->GetPort());
|
||||
ImGui::Text("%sNetworkInterface open to %s on port %u", protocol, trustZone, port);
|
||||
|
||||
if (ImGui::BeginTable("", 2, flags))
|
||||
{
|
||||
const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics();
|
||||
ImGui::TableSetupColumn("Stat", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total time spent updating (ms)");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_updateTimeMs));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total number of connections");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_connectionCount));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total send time (ms)");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_sendTimeMs));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent packets");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::s64>(metrics.m_sendPackets));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent bytes after compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytes));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent bytes before compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytesUncompressed));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total sent compressed packets without benefit");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_sendCompressedPacketsNoGain));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total gain from packet compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_sendBytesCompressedDelta));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total packets resent");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_resentPackets));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total receive time (ms)");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%lld", aznumeric_cast<AZ::s64>(metrics.m_recvTimeMs));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total received packets");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_recvPackets));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total received bytes after compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytes));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total received bytes before compression");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytesUncompressed));
|
||||
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
||||
ImGui::Text("Total packets discarded due to load");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", aznumeric_cast<AZ::u64>(metrics.m_discardedPackets));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("", 7, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("RemoteAddr", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Conn. Id", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 6.0f);
|
||||
ImGui::TableSetupColumn("Send (Bps)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 10.0f);
|
||||
ImGui::TableSetupColumn("Recv (Bps)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 10.0f);
|
||||
ImGui::TableSetupColumn("RTT (ms)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 8.0f);
|
||||
ImGui::TableSetupColumn("% Lost", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 8.0f);
|
||||
ImGui::TableSetupColumn("Debug Settings", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 32.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto displayConnectionRow = [](AzNetworking::IConnection& connection)
|
||||
{
|
||||
ImGui::PushID(&connection);
|
||||
|
||||
const AzNetworking::ConnectionMetrics& metrics = connection.GetMetrics();
|
||||
const AzNetworking::IpAddress::IpString remoteAddr = connection.GetRemoteAddress().GetString();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TreeNodeEx(remoteAddr.c_str(), nodeFlags);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%5llu", aznumeric_cast<AZ::u64>(connection.GetConnectionId()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%9.2f", metrics.m_sendDatarate.GetBytesPerSecond());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%9.2f", metrics.m_recvDatarate.GetBytesPerSecond());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%7.2f", metrics.m_connectionRtt.GetRoundTripTimeSeconds() * 1000.0f);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%7.2f", metrics.m_sendDatarate.GetLossRatePercent());
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
{
|
||||
AzNetworking::ConnectionQuality& quality = connection.GetConnectionQuality();
|
||||
int32_t latency = aznumeric_cast<int32_t>(quality.m_latencyMs);
|
||||
int32_t variance = aznumeric_cast<int32_t>(quality.m_varianceMs);
|
||||
ImGui::SliderInt("Loss %", &quality.m_lossPercentage, 0, 100);
|
||||
if (ImGui::SliderInt("Latency(ms)", &latency, 0, 3000))
|
||||
{
|
||||
quality.m_latencyMs = AZ::TimeMs{ latency };
|
||||
}
|
||||
if (ImGui::SliderInt("Jitter(ms)", &variance, 0, 1000))
|
||||
{
|
||||
quality.m_varianceMs = AZ::TimeMs{ variance };
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
};
|
||||
networkInterface.second->GetConnectionSet().VisitConnections(displayConnectionRow);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::NewLine();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void DrawMultiplayerStats()
|
||||
{
|
||||
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
|
||||
ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
|
||||
ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
|
||||
ImGui::Text("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
|
||||
ImGui::Text("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
|
||||
ImGui::NewLine();
|
||||
|
||||
static ImGuiTableFlags flags = ImGuiTableFlags_BordersV
|
||||
| ImGuiTableFlags_BordersOuterH
|
||||
| ImGuiTableFlags_Resizable
|
||||
| ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_NoBordersInBody;
|
||||
|
||||
if (ImGui::BeginTable("", 5, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
if (DrawSummaryRow("Totals", stats))
|
||||
{
|
||||
for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index)
|
||||
{
|
||||
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
|
||||
using StringLabel = AZStd::fixed_string<128>;
|
||||
const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId);
|
||||
const StringLabel componentName = componentRegistry->GetComponentName(netComponentId);
|
||||
const StringLabel label = gemName + "::" + componentName;
|
||||
if (DrawComponentRow(label.c_str(), stats, netComponentId))
|
||||
{
|
||||
DrawComponentDetails(stats, netComponentId);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
ImGui::NewLine();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MultiplayerDebugSystemComponent::OnImGuiUpdate()
|
||||
{
|
||||
if (m_displayNetworkingStats)
|
||||
{
|
||||
if (ImGui::Begin("Networking Stats", &m_displayNetworkingStats, ImGuiWindowFlags_None))
|
||||
{
|
||||
AzNetworking::INetworking* networking = AZ::Interface<AzNetworking::INetworking>::Get();
|
||||
|
||||
ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(networking->GetTcpListenThreadUpdateTime()));
|
||||
ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount());
|
||||
ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(networking->GetUdpReaderThreadUpdateTime()));
|
||||
|
||||
for (auto& networkInterface : networking->GetNetworkInterfaces())
|
||||
{
|
||||
const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp";
|
||||
const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer";
|
||||
const uint32_t port = aznumeric_cast<uint32_t>(networkInterface.second->GetPort());
|
||||
ImGui::Text("%sNetworkInterface: %s - open to %s on port %u", protocol, networkInterface.second->GetName().GetCStr(), trustZone, port);
|
||||
|
||||
const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics();
|
||||
ImGui::Text(" - Total time spent updating in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_updateTimeMs));
|
||||
ImGui::Text(" - Total number of connections: %llu", aznumeric_cast<AZ::u64>(metrics.m_connectionCount));
|
||||
ImGui::Text(" - Total send time in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_sendTimeMs));
|
||||
ImGui::Text(" - Total sent packets: %llu", aznumeric_cast<AZ::s64>(metrics.m_sendPackets));
|
||||
ImGui::Text(" - Total sent bytes after compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytes));
|
||||
ImGui::Text(" - Total sent bytes before compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytesUncompressed));
|
||||
ImGui::Text(" - Total sent compressed packets without benefit: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendCompressedPacketsNoGain));
|
||||
ImGui::Text(" - Total gain from packet compression: %lld", aznumeric_cast<AZ::s64>(metrics.m_sendBytesCompressedDelta));
|
||||
ImGui::Text(" - Total packets resent: %llu", aznumeric_cast<AZ::u64>(metrics.m_resentPackets));
|
||||
ImGui::Text(" - Total receive time in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_recvTimeMs));
|
||||
ImGui::Text(" - Total received packets: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvPackets));
|
||||
ImGui::Text(" - Total received bytes after compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytes));
|
||||
ImGui::Text(" - Total received bytes before compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytesUncompressed));
|
||||
ImGui::Text(" - Total packets discarded due to load: %llu", aznumeric_cast<AZ::u64>(metrics.m_discardedPackets));
|
||||
}
|
||||
DrawNetworkingStats();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,50 +429,7 @@ namespace Multiplayer
|
||||
{
|
||||
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None))
|
||||
{
|
||||
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
|
||||
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
|
||||
ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
|
||||
ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
|
||||
ImGui::Text("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
|
||||
ImGui::Text("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
|
||||
ImGui::NewLine();
|
||||
|
||||
static ImGuiTableFlags flags = ImGuiTableFlags_BordersV
|
||||
| ImGuiTableFlags_BordersOuterH
|
||||
| ImGuiTableFlags_Resizable
|
||||
| ImGuiTableFlags_RowBg
|
||||
| ImGuiTableFlags_NoBordersInBody;
|
||||
|
||||
if (ImGui::BeginTable("", 5, flags))
|
||||
{
|
||||
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
if (DrawSummaryRow("Totals", stats))
|
||||
{
|
||||
for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index)
|
||||
{
|
||||
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
|
||||
using StringLabel = AZStd::fixed_string<128>;
|
||||
const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId);
|
||||
const StringLabel componentName = componentRegistry->GetComponentName(netComponentId);
|
||||
const StringLabel label = gemName + "::" + componentName;
|
||||
if (DrawComponentRow(label.c_str(), stats, netComponentId))
|
||||
{
|
||||
DrawComponentDetails(stats, netComponentId);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
DrawMultiplayerStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,19 +69,24 @@ namespace Multiplayer
|
||||
{
|
||||
using namespace AzNetworking;
|
||||
|
||||
AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serverpassword, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Optional server password");
|
||||
AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port");
|
||||
AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The address of the remote server or host to connect to");
|
||||
AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic");
|
||||
AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_gamerules, "norules", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "GameRules server works with");
|
||||
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
|
||||
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
|
||||
AZ_CVAR(bool, sv_isTransient, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether a dedicated server shuts down if all existing connections disconnect.");
|
||||
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
|
||||
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
|
||||
AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The default spawnable to use when a new player connects");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default spawnable to use when a new player connects");
|
||||
AZ_CVAR(float, cl_renderTickBlendBase, 0.15f, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"The base used for blending between network updates, 0.1 will be quite linear, 0.2 or 0.3 will "
|
||||
"slow down quicker and may be better suited to connections with highly variable latency");
|
||||
|
||||
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
@@ -546,7 +551,7 @@ namespace Multiplayer
|
||||
if ((GetAgentType() == MultiplayerAgentType::Client) && (packet.GetHostFrameId() > m_lastReplicatedHostFrameId))
|
||||
{
|
||||
// Update client to latest server time
|
||||
m_renderBlendFactor = 0.0f;
|
||||
m_tickFactor = 0.0f;
|
||||
m_lastReplicatedHostTimeMs = packet.GetHostTimeMs();
|
||||
m_lastReplicatedHostFrameId = packet.GetHostFrameId();
|
||||
m_networkTime.AlterTime(m_lastReplicatedHostFrameId, m_lastReplicatedHostTimeMs, AzNetworking::InvalidConnectionId);
|
||||
@@ -854,10 +859,18 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds)
|
||||
{
|
||||
m_tickFactor += deltaTime / serverRateSeconds;
|
||||
// Linear close to the origin, but asymptote at y = 1
|
||||
const float targetAdjustBlend = AZStd::clamp(deltaTime / serverRateSeconds, 0.0f, 1.0f);
|
||||
m_renderBlendFactor = 1.0f - (std::pow(0.2f, m_renderBlendFactor + targetAdjustBlend));
|
||||
AZLOG(NET_Blending, "Computed blend factor of %0.2f using a frametime of %0.2f and a serverTickRate of %0.2f", m_renderBlendFactor, deltaTime, serverRateSeconds);
|
||||
const float renderBlendFactor = AZStd::clamp(1.0f - (std::pow(cl_renderTickBlendBase, m_tickFactor)), 0.0f, 1.0f);
|
||||
AZLOG
|
||||
(
|
||||
NET_Blending,
|
||||
"Computed blend factor of %0.3f using a tick factor of %0.3f, a frametime of %0.3f and a serverTickRate of %0.3f",
|
||||
renderBlendFactor,
|
||||
m_tickFactor,
|
||||
deltaTime,
|
||||
serverRateSeconds
|
||||
);
|
||||
|
||||
if (Camera::ActiveCameraRequestBus::HasHandlers())
|
||||
{
|
||||
@@ -900,7 +913,7 @@ namespace Multiplayer
|
||||
|
||||
for (NetBindComponent* netBindComponent : gatheredEntities)
|
||||
{
|
||||
netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor);
|
||||
netBindComponent->NotifyPreRender(deltaTime, renderBlendFactor);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -912,7 +925,7 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor);
|
||||
netBindComponent->NotifyPreRender(deltaTime, renderBlendFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace Multiplayer
|
||||
HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0);
|
||||
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
float m_renderBlendFactor = 0.0f;
|
||||
float m_tickFactor = 0.0f;
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
MultiplayerEditorConnection m_editorConnectionListener;
|
||||
|
||||
@@ -578,7 +578,7 @@ namespace NvCloth
|
||||
const AZ::Vector3& renderTangent = renderTangents[renderVertexIndex];
|
||||
destTangentsBuffer[index].Set(
|
||||
renderTangent,
|
||||
1.0f);
|
||||
-1.0f); // Shader function ConstructTBN inverts w to change bitangent sign, but the bitangents passed are already corrected, so passing -1.0 to counteract.
|
||||
}
|
||||
|
||||
if (destBitangentsBuffer)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NvCloth
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const float Tolerance = 0.0001f;
|
||||
const float Tolerance = 1e-7f;
|
||||
}
|
||||
|
||||
bool TangentSpaceHelper::CalculateNormals(
|
||||
@@ -34,7 +34,8 @@ namespace NvCloth
|
||||
const size_t vertexCount = vertices.size();
|
||||
|
||||
// Reset results
|
||||
outNormals.resize(vertexCount, AZ::Vector3::CreateZero());
|
||||
outNormals.resize(vertexCount);
|
||||
AZStd::fill(outNormals.begin(), outNormals.end(), AZ::Vector3::CreateZero());
|
||||
|
||||
// calculate the normals per triangle
|
||||
for (size_t i = 0; i < triangleCount; ++i)
|
||||
@@ -115,8 +116,10 @@ namespace NvCloth
|
||||
const size_t vertexCount = vertices.size();
|
||||
|
||||
// Reset results
|
||||
outTangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
||||
outBitangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
||||
outTangents.resize(vertexCount);
|
||||
outBitangents.resize(vertexCount);
|
||||
AZStd::fill(outTangents.begin(), outTangents.end(), AZ::Vector3::CreateZero());
|
||||
AZStd::fill(outBitangents.begin(), outBitangents.end(), AZ::Vector3::CreateZero());
|
||||
|
||||
// calculate the base vectors per triangle
|
||||
for (size_t i = 0; i < triangleCount; ++i)
|
||||
@@ -193,9 +196,12 @@ namespace NvCloth
|
||||
const size_t vertexCount = vertices.size();
|
||||
|
||||
// Reset results
|
||||
outTangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
||||
outBitangents.resize(vertexCount, AZ::Vector3::CreateZero());
|
||||
outNormals.resize(vertexCount, AZ::Vector3::CreateZero());
|
||||
outTangents.resize(vertexCount);
|
||||
outBitangents.resize(vertexCount);
|
||||
outNormals.resize(vertexCount);
|
||||
AZStd::fill(outTangents.begin(), outTangents.end(), AZ::Vector3::CreateZero());
|
||||
AZStd::fill(outBitangents.begin(), outBitangents.end(), AZ::Vector3::CreateZero());
|
||||
AZStd::fill(outNormals.begin(), outNormals.end(), AZ::Vector3::CreateZero());
|
||||
|
||||
// calculate the base vectors per triangle
|
||||
for (size_t i = 0; i < triangleCount; ++i)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user