Merge branch 'development' of https://github.com/o3de/o3de into carlitosan/development

monroegm-disable-blank-issue-2
chcurran 5 years ago
commit d300f4b033

@ -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)

@ -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

@ -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>

@ -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&lt;*,*&gt;">
<DisplayString Condition="(data_.f.flags &amp; 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 &amp; kTypeMask) == kStringType">{(const char*)((size_t)data_.s.str &amp; 0x0000FFFFFFFFFFFF)}</DisplayString>
<DisplayString Condition="(data_.f.flags &amp; kNumberIntFlag) == kNumberIntFlag">{data_.n.i.i}</DisplayString>
<DisplayString Condition="(data_.f.flags &amp; kNumberUintFlag) == kNumberUintFlag">{data_.n.u.u}</DisplayString>
<DisplayString Condition="(data_.f.flags &amp; kNumberInt64Flag) == kNumberInt64Flag">{data_.n.i64}</DisplayString>
<DisplayString Condition="(data_.f.flags &amp; kNumberUint64Flag) == kNumberUint64Flag">{data_.n.u64}</DisplayString>
<DisplayString Condition="(data_.f.flags &amp; 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&lt;$T1,$T2&gt;*)(((size_t)data_.o.members) &amp; 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&lt;$T1,$T2&gt;*)(((size_t)data_.a.elements) &amp; 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

@ -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;
}

@ -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(&paramInfo, sizeof(FileFormat::AnimGraph_ParameterInfo));
// convert endian
MCore::Endian::ConvertUnsignedInt32(&paramInfo.mNumComboValues, importParams.mEndianType);
MCore::Endian::ConvertUnsignedInt32(&paramInfo.mInterfaceType, importParams.mEndianType);
MCore::Endian::ConvertUnsignedInt32(&paramInfo.mAttributeType, importParams.mEndianType);
MCore::Endian::ConvertUnsignedInt16(&paramInfo.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(&parameterIndex, sizeof(uint32));
MCore::Endian::ConvertUnsignedInt32(&parameterIndex, 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(&paramInfoChunk, 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_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)
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (!context)
{
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())
motionSet->SetFilename(filename.c_str());
if (settings)
{
MCore::LogInfo(" + Loading successfully finished.");
motionSet->SetIsOwnedByRuntime(settings->m_isOwnedByRuntime);
}
}
// return the result
return result;
return motionSet;
}
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);
if (settings)
{
motionSet->SetIsOwnedByRuntime(settings->m_isOwnedByRuntime);
}
return motionSet;
}
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_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))
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (!context)
{
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)
{
result->SetFileName(filename.c_str());
result->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
EMotionFX::AnimGraph* animGraph = EMotionFX::AnimGraph::LoadFromFile(filename, context, loadFilter);
if (animGraph)
{
if (GetLogging())
{
MCore::LogInfo(" + Loading successfully finished");
}
animGraph->SetFileName(filename.c_str());
animGraph->RemoveInvalidConnections(); // Remove connections that have nullptr source node's, which happens when connections point to unknown nodes.
}
// 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

@ -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

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:33ff8a4f75b5ad918a833113fb45a21e9075b66a9d0ed8563ee34d93ae905aaf
size 50863

@ -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

@ -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)

@ -125,6 +125,9 @@ namespace UnitTest
const AZStd::vector<AZ::Vector4>& motionConstraints = clothConstraints->GetMotionConstraints();
EXPECT_TRUE(motionConstraints.size() == SimulationParticles.size());
EXPECT_THAT(motionConstraints[0].GetAsVector3(), IsCloseTolerance(SimulationParticles[0].GetAsVector3(), Tolerance));
EXPECT_THAT(motionConstraints[1].GetAsVector3(), IsCloseTolerance(SimulationParticles[1].GetAsVector3(), Tolerance));
EXPECT_THAT(motionConstraints[2].GetAsVector3(), IsCloseTolerance(SimulationParticles[2].GetAsVector3(), Tolerance));
EXPECT_NEAR(motionConstraints[0].GetW(), 6.0f, Tolerance);
EXPECT_NEAR(motionConstraints[1].GetW(), 0.0f, Tolerance);
EXPECT_NEAR(motionConstraints[2].GetW(), 0.0f, Tolerance);
@ -278,6 +281,9 @@ namespace UnitTest
const AZStd::vector<AZ::Vector4>& separationConstraints = clothConstraints->GetSeparationConstraints();
EXPECT_TRUE(motionConstraints.size() == newParticles.size());
EXPECT_THAT(motionConstraints[0].GetAsVector3(), IsCloseTolerance(newParticles[0].GetAsVector3(), Tolerance));
EXPECT_THAT(motionConstraints[1].GetAsVector3(), IsCloseTolerance(newParticles[1].GetAsVector3(), Tolerance));
EXPECT_THAT(motionConstraints[2].GetAsVector3(), IsCloseTolerance(newParticles[2].GetAsVector3(), Tolerance));
EXPECT_NEAR(motionConstraints[0].GetW(), 3.0f, Tolerance);
EXPECT_NEAR(motionConstraints[1].GetW(), 1.5f, Tolerance);
EXPECT_NEAR(motionConstraints[2].GetW(), 0.0f, Tolerance);
@ -286,8 +292,8 @@ namespace UnitTest
EXPECT_NEAR(separationConstraints[0].GetW(), 3.0f, Tolerance);
EXPECT_NEAR(separationConstraints[1].GetW(), 1.5f, Tolerance);
EXPECT_NEAR(separationConstraints[2].GetW(), 0.3f, Tolerance);
EXPECT_THAT(separationConstraints[0].GetAsVector3(), IsCloseTolerance(AZ::Vector3(-3.03902f, 2.80752f, 3.80752f), Tolerance));
EXPECT_THAT(separationConstraints[1].GetAsVector3(), IsCloseTolerance(AZ::Vector3(-1.41659f, 0.651243f, -0.348757f), Tolerance));
EXPECT_THAT(separationConstraints[2].GetAsVector3(), IsCloseTolerance(AZ::Vector3(6.15313f, -0.876132f, 0.123868f), Tolerance));
EXPECT_THAT(separationConstraints[0].GetAsVector3(), IsCloseTolerance(AZ::Vector3(0.0f, 3.53553f, 4.53553f), Tolerance));
EXPECT_THAT(separationConstraints[1].GetAsVector3(), IsCloseTolerance(AZ::Vector3(0.0f, 2.06066f, 1.06066f), Tolerance));
EXPECT_THAT(separationConstraints[2].GetAsVector3(), IsCloseTolerance(AZ::Vector3(1.0f, -3.74767f, -2.74767f), Tolerance));
}
} // namespace UnitTest

@ -41,11 +41,13 @@ git clone https://github.com/o3de/o3de.git
#### Optional
* Wwise - 2021.1.1.7601 minimum: [https://www.audiokinetic.com/download/](https://www.audiokinetic.com/download/)
* Wwise version 2021.1.1.7601 minimum: [https://www.audiokinetic.com/download/](https://www.audiokinetic.com/download/)
* Note: This requires registration and installation of a client application to download
* Make sure to select the SDK(C++) component during installation of Wwise
* You will also need to set an environment variable: `set LY_WWISE_INSTALL_PATH=<path to Wwise version>`
* For example: `set LY_WWISE_INSTALL_PATH="C:\Program Files (x86)\Audiokinetic\Wwise 2021.1.1.7601"`
* Note: It is generally okay to use a more recent version of Wwise, but some SDK updates will require code changes
* Make sure to select the `SDK(C++)` component during installation of Wwise
* CMake can find the Wwise install location in two ways:
* The `LY_WWISE_INSTALL_PATH` CMake cache variable -- this is checked first
* The `WWISEROOT` environment variable which is set when installing Wwise SDK
### Quick Start Build Steps
@ -54,8 +56,7 @@ git clone https://github.com/o3de/o3de.git
1. Install the following redistributables to the following:
- Visual Studio and VC++ redistributable can be installed to any location
- CMake can be installed to any location, as long as it's available in the system path
- (Optional) Wwise can be installed anywhere, but you will need to set an environment variable for CMake to detect it: `set LY_WWISE_INSTALL_PATH=<path to Wwise>`
1. Configure the source into a solution using this command line, replacing <your build path> and <3rdParty cache path> to a path you've created:
```
cmake -B <your build path> -S <your source path> -G "Visual Studio 16" -DLY_3RDPARTY_PATH=<3rdParty cache path> -DLY_UNITY_BUILD=ON -DLY_PROJECTS=AutomatedTesting

@ -6,25 +6,16 @@
#
#
# The current supported version of Wwise
set(WWISE_VERSION 2021.1.1.7601)
# Wwise Install Path
# Initialize to the default 3rdParty path
set(LY_WWISE_INSTALL_PATH "" CACHE PATH "Path to Wwise version ${WWISE_VERSION} installation.")
set(LY_WWISE_INSTALL_PATH "" CACHE PATH "Path to Wwise installation.")
# Check for a known file in the SDK path to verify the path
function(is_valid_sdk sdk_path is_valid)
set(${is_valid} FALSE PARENT_SCOPE)
if(EXISTS ${sdk_path})
set(sdk_version_file ${sdk_path}/SDK/include/AK/AkWwiseSDKVersion.h)
if(EXISTS ${sdk_version_file})
string(FIND ${sdk_path} ${WWISE_VERSION} index)
if(NOT index EQUAL -1)
set(${is_valid} TRUE PARENT_SCOPE)
else()
# The install path doesn't contain the WWISE_VERSION string.
# The path could still be correct, but it would require parsing the AkWwiseSDKVersion.h to verify.
endif()
set(${is_valid} TRUE PARENT_SCOPE)
endif()
endif()
endfunction()
@ -32,19 +23,17 @@ endfunction()
# Paths that will be checked, in order:
# - CMake cache variable
# - WWISEROOT Environment Variable
# - Standard 3rdParty path
set(WWISE_SDK_PATHS
"${LY_WWISE_INSTALL_PATH}"
"$ENV{WWISEROOT}"
"${LY_3RDPARTY_PATH}/Wwise/${WWISE_VERSION}"
)
set(found_sdk FALSE)
foreach(test_path ${WWISE_SDK_PATHS})
is_valid_sdk(${test_path} found_sdk)
foreach(candidate_path ${WWISE_SDK_PATHS})
is_valid_sdk(${candidate_path} found_sdk)
if(found_sdk)
# Update the Wwise Install Path cache variable
set(LY_WWISE_INSTALL_PATH "${test_path}")
# Update the Wwise Install Path variable internally
set(LY_WWISE_INSTALL_PATH "${candidate_path}")
break()
endif()
endforeach()
@ -112,15 +101,14 @@ set(WWISE_COMPILE_DEFINITIONS
)
# The default install path might look different than the standard 3rdParty format (${LY_3RDPARTY_PATH}/<Name>/<Version>).
# Use these to get the parent path and folder name before adding the external 3p target.
get_filename_component(WWISE_3P_ROOT ${LY_WWISE_INSTALL_PATH} DIRECTORY)
get_filename_component(WWISE_INSTALL_ROOT ${LY_WWISE_INSTALL_PATH} DIRECTORY)
get_filename_component(WWISE_FOLDER ${LY_WWISE_INSTALL_PATH} NAME)
ly_add_external_target(
NAME Wwise
VERSION "${WWISE_FOLDER}"
3RDPARTY_ROOT_DIRECTORY "${WWISE_3P_ROOT}"
3RDPARTY_ROOT_DIRECTORY "${WWISE_INSTALL_ROOT}"
INCLUDE_DIRECTORIES SDK/include
COMPILE_DEFINITIONS ${WWISE_COMPILE_DEFINITIONS}
)

@ -40,7 +40,6 @@ function(ly_add_autogen)
)
set_target_properties(${ly_add_autogen_NAME} PROPERTIES AUTOGEN_INPUT_FILES "${AZCG_INPUTFILES}")
set_target_properties(${ly_add_autogen_NAME} PROPERTIES AUTOGEN_OUTPUT_FILES "${AUTOGEN_OUTPUTS}")
set_target_properties(${ly_add_autogen_NAME} PROPERTIES VS_USER_PROPS "${LY_ROOT_FOLDER}/Code/Framework/AzAutoGen/AzAutoGen.props")
target_sources(${ly_add_autogen_NAME} PRIVATE ${AUTOGEN_OUTPUTS})
endif()

@ -10,7 +10,10 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
<PropertyGroup>
<UseMultiToolTask>true</UseMultiToolTask>
<EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>
<!-- Add a mapping of configurations to those known by vcpkg to prevent warnings/messages during the build -->
@VCPKG_CONFIGURATION_MAPPING@
<!-- Disable vcpkg to prevent include and linking paths from being added by the vcpkg integration -->
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>

@ -28,7 +28,7 @@ def add_args(parser, subparsers) -> None:
o3de_package_dir = (script_dir / 'o3de').resolve()
# add the scripts/o3de directory to the front of the sys.path
sys.path.insert(0, str(o3de_package_dir))
from o3de import engine_template, global_project, register, print_registration, get_registration, \
from o3de import engine_properties, engine_template, gem_properties, global_project, register, print_registration, get_registration, \
enable_gem, disable_gem, project_properties, sha256
# Remove the temporarily added path
sys.path = sys.path[1:]
@ -52,9 +52,15 @@ def add_args(parser, subparsers) -> None:
# remove a gem from a project
disable_gem.add_args(subparsers)
# modify project properties
# modify engine properties
engine_properties.add_args(subparsers)
# modify project properties
project_properties.add_args(subparsers)
# modify gem properties
gem_properties.add_args(subparsers)
# sha256
sha256.add_args(subparsers)

@ -0,0 +1,82 @@
#
# 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 argparse
import json
import os
import pathlib
import sys
import logging
from o3de import manifest, utils
logger = logging.getLogger()
logging.basicConfig()
def edit_engine_props(engine_path: pathlib.Path = None,
engine_name: str = None,
new_name: str = None,
new_version: str = None) -> int:
if not engine_path and not engine_name:
logger.error(f'Either a engine path or a engine name must be supplied to lookup engine.json')
return 1
if not engine_path:
engine_path = manifest.get_registered(engine_name=engine_name)
if not engine_path:
logger.error(f'Error unable locate engine path: No engine with name {engine_name} is registered in any manifest')
return 1
engine_json_data = manifest.get_engine_json_data(engine_path=engine_path)
if not engine_json_data:
return 1
if new_name:
if not utils.validate_identifier(new_name):
logger.error(f'Engine name must be fewer than 64 characters, contain only alphanumeric, "_" or "-"'
f' characters, and start with a letter. {new_name}')
return 1
engine_json_data['engine_name'] = new_name
if new_version:
engine_json_data['O3DEVersion'] = new_version
return 0 if manifest.save_o3de_manifest(engine_json_data, pathlib.Path(engine_path) / 'engine.json') else 1
def _edit_engine_props(args: argparse) -> int:
return edit_engine_props(args.engine_path,
args.engine_name,
args.engine_new_name,
args.engine_version)
def add_parser_args(parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-ep', '--engine-path', type=pathlib.Path, required=False,
help='The path to the engine.')
group.add_argument('-en', '--engine-name', type=str, required=False,
help='The name of the engine.')
group = parser.add_argument_group('properties', 'arguments for modifying individual engine properties.')
group.add_argument('-enn', '--engine-new-name', type=str, required=False,
help='Sets the name for the engine.')
group.add_argument('-ev', '--engine-version', type=str, required=False,
help='Sets the version for the engine.')
parser.set_defaults(func=_edit_engine_props)
def add_args(subparsers) -> None:
enable_engine_props_subparser = subparsers.add_parser('edit-engine-properties')
add_parser_args(enable_engine_props_subparser)
def main():
the_parser = argparse.ArgumentParser()
add_parser_args(the_parser)
the_args = the_parser.parse_args()
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
sys.exit(ret)
if __name__ == "__main__":
main()

@ -0,0 +1,163 @@
#
# 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 argparse
import json
import os
import pathlib
import sys
import logging
from o3de import manifest, utils
logger = logging.getLogger()
logging.basicConfig()
def update_values_in_key_list(existing_values: list, new_values: list or str, remove_values: list or str,
replace_values: list or str):
"""
Updates values within a list by first appending values in the new_values list, removing values in the remove_values
list and then replacing values in the replace_values list
:param existing_values list with existing values to modify
:param new_values list with values to add to the existing value list
:param remove_values list with values to remove from the existing value list
:param replace_values list with values to replace in the existing value list
returns updated existing value list
"""
if new_values:
new_values = new_values.split() if isinstance(new_values, str) else new_values
existing_values.extend(new_values)
if remove_values:
remove_values = remove_values.split() if isinstance(remove_values, str) else remove_values
existing_values = list(filter(lambda value: value not in remove_values, existing_values))
if replace_values:
replace_values = replace_values.split() if isinstance(replace_values, str) else replace_values
existing_values = replace_values
return existing_values
def edit_gem_props(gem_path: pathlib.Path = None,
gem_name: str = None,
new_name: str = None,
new_display: str = None,
new_origin: str = None,
new_type: str = None,
new_summary: str = None,
new_icon: str = None,
new_requirements: str = None,
new_tags: list or str = None,
remove_tags: list or str = None,
replace_tags: list or str = None,
) -> int:
if not gem_path and not gem_name:
logger.error(f'Either a gem path or a gem name must be supplied to lookup gem.json')
return 1
if not gem_path:
gem_path = manifest.get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Error unable locate gem path: No gem with name {gem_name} is registered in any manifest')
return 1
gem_json_data = manifest.get_gem_json_data(gem_path=gem_path)
if not gem_json_data:
return 1
update_key_dict = {}
if new_name:
if not utils.validate_identifier(new_name):
logger.error(f'Engine name must be fewer than 64 characters, contain only alphanumeric, "_" or "-"'
f' characters, and start with a letter. {new_name}')
return 1
update_key_dict['gem_name'] = new_name
if new_display:
update_key_dict['display_name'] = new_display
if new_origin:
update_key_dict['origin'] = new_origin
if new_type:
update_key_dict['type'] = new_type
if new_summary:
update_key_dict['summary'] = new_summary
if new_icon:
update_key_dict['icon_path'] = new_icon
if new_requirements:
update_key_dict['icon_requirements'] = new_requirements
update_key_dict['user_tags'] = update_values_in_key_list(gem_json_data.get('user_tags', []), new_tags,
remove_tags, replace_tags)
gem_json_data.update(update_key_dict)
return 0 if manifest.save_o3de_manifest(gem_json_data, pathlib.Path(gem_path) / 'gem.json') else 1
def _edit_gem_props(args: argparse) -> int:
return edit_gem_props(args.gem_path,
args.gem_name,
args.gem_new_name,
args.gem_display,
args.gem_origin,
args.gem_type,
args.gem_summary,
args.gem_icon,
args.gem_requirements,
args.add_tags,
args.remove_tags,
args.replace_tags)
def add_parser_args(parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False,
help='The path to the gem.')
group.add_argument('-gn', '--gem-name', type=str, required=False,
help='The name of the gem.')
group = parser.add_argument_group('properties', 'arguments for modifying individual gem properties.')
group.add_argument('-gnn', '--gem-new-name', type=str, required=False,
help='Sets the name for the gem.')
group.add_argument('-gd', '--gem-display', type=str, required=False,
help='Sets the gem display name.')
group.add_argument('-go', '--gem-origin', type=str, required=False,
help='Sets description for gem origin.')
group.add_argument('-gt', '--gem-type', type=str, required=False, choices=['Code', 'Tool', 'Asset'],
help='Sets the gem type. Can only be one of the selected choices')
group.add_argument('-gs', '--gem-summary', type=str, required=False,
help='Sets the summary description of the gem.')
group.add_argument('-gi', '--gem-icon', type=str, required=False,
help='Sets the path to the projects icon resource.')
group.add_argument('-gr', '--gem-requirements', type=str, required=False,
help='Sets the description of the requirements needed to use the gem')
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('-at', '--add-tags', type=str, nargs='*', required=False,
help='Adds tag(s) to user_tags property. Can be specified multiple times')
group.add_argument('-dt', '--remove-tags', type=str, nargs='*', required=False,
help='Removes tag(s) from the user_tags property. Can be specified multiple times')
group.add_argument('-rt', '--replace-tags', type=str, nargs='*', required=False,
help='Replace tag(s) in user_tags property. Can be specified multiple times')
parser.set_defaults(func=_edit_gem_props)
def add_args(subparsers) -> None:
enable_gem_props_subparser = subparsers.add_parser('edit-gem-properties')
add_parser_args(enable_gem_props_subparser)
def main():
the_parser = argparse.ArgumentParser()
add_parser_args(the_parser)
the_args = the_parser.parse_args()
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
sys.exit(ret)
if __name__ == "__main__":
main()

@ -26,7 +26,7 @@ def get_project_props(name: str = None, path: pathlib.Path = None) -> dict:
return None
return proj_json
def edit_project_props(proj_path: pathlib.Path,
def edit_project_props(proj_path: pathlib.Path = None,
proj_name: str = None,
new_name: str = None,
new_origin: str = None,
@ -71,8 +71,8 @@ def edit_project_props(proj_path: pathlib.Path,
tag_list = replace_tags.split() if isinstance(replace_tags, str) else replace_tags
proj_json['user_tags'] = tag_list
manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path) / 'project.json')
return 0
return 0 if manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path) / 'project.json') else 1
def _edit_project_props(args: argparse) -> int:
return edit_project_props(args.project_path,

@ -39,6 +39,13 @@ ly_add_pytest(
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_engine_properties
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_engine_properties.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_project_properties
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_project_properties.py
@ -46,6 +53,13 @@ ly_add_pytest(
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_gem_properties
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_gem_properties.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_template
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_engine_template.py

@ -0,0 +1,72 @@
#
# 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 json
import pytest
import pathlib
from unittest.mock import patch
from o3de import engine_properties
TEST_ENGINE_JSON_PAYLOAD = '''
{
"engine_name": "o3de",
"restricted_name": "o3de",
"FileVersion": 1,
"O3DEVersion": "0.0.0.0",
"O3DECopyrightYear": 2021,
"O3DEBuildNumber": 0,
"external_subdirectories": [
"Gems/TestGem2"
],
"projects": [
],
"templates": [
"Templates/MinimalProject"
]
}
'''
@pytest.fixture(scope='class')
def init_engine_json_data(request):
class EngineJsonData:
def __init__(self):
self.data = json.loads(TEST_ENGINE_JSON_PAYLOAD)
request.cls.engine_json = EngineJsonData()
@pytest.mark.usefixtures('init_engine_json_data')
class TestEditEngineProperties:
@pytest.mark.parametrize("engine_path, engine_name, engine_new_name, engine_version, expected_result", [
pytest.param(pathlib.PurePath('D:/o3de'), None, 'o3de-install', '1.0.0.0', 0),
pytest.param(None, 'o3de-install', 'o3de-sdk', '1.0.0.1', 0),
pytest.param(None, 'o3de-sdk', None, '2.0.0.0', 0),
]
)
def test_edit_engine_properties(self, engine_path, engine_name, engine_new_name, engine_version, expected_result):
def get_engine_json_data(engine_path: pathlib.Path) -> dict:
return self.engine_json.data
def get_engine_path(engine_name: str) -> pathlib.Path:
return pathlib.Path('D:/o3de')
def save_o3de_manifest(new_engine_data: dict, engine_path: pathlib.Path) -> bool:
self.engine_json.data = new_engine_data
return True
with patch('o3de.manifest.get_engine_json_data', side_effect=get_engine_json_data) as get_engine_json_data_patch, \
patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch, \
patch('o3de.manifest.get_registered', side_effect=get_engine_path) as get_registered_patch:
result = engine_properties.edit_engine_props(engine_path, engine_name, engine_new_name, engine_version)
assert result == expected_result
if engine_new_name:
assert self.engine_json.data.get('engine_name', '') == engine_new_name
if engine_version:
assert self.engine_json.data.get('O3DEVersion', '') == engine_version

@ -0,0 +1,99 @@
#
# 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 json
import pytest
import pathlib
from unittest.mock import patch
from o3de import gem_properties
TEST_GEM_JSON_PAYLOAD = '''
{
"gem_name": "TestGem",
"display_name": "TestGem",
"license": "What license TestGem uses goes here: i.e. https://opensource.org/licenses/MIT",
"origin": "The primary repo for TestGem goes here: i.e. http://www.mydomain.com",
"type": "Code",
"summary": "A short description of TestGem.",
"canonical_tags": [
"Gem"
],
"user_tags": [
"TestGem"
],
"icon_path": "preview.png",
"requirements": ""
}
'''
@pytest.fixture(scope='class')
def init_gem_json_data(request):
class GemJsonData:
def __init__(self):
self.data = json.loads(TEST_GEM_JSON_PAYLOAD)
request.cls.gem_json = GemJsonData()
@pytest.mark.usefixtures('init_gem_json_data')
class TestEditGemProperties:
@pytest.mark.parametrize("gem_path, gem_name, gem_new_name, gem_display, gem_origin,\
gem_type, gem_summary, gem_icon, gem_requirements,\
add_tags, remove_tags, replace_tags, expected_tags, expected_result", [
pytest.param(pathlib.PurePath('D:/TestProject'),
None, 'TestGem2', 'New Gem Name', 'O3DE', 'Code', 'Gem that exercises Default Gem Template',
'preview.png', '',
['Physics', 'Rendering', 'Scripting'], None, None, ['TestGem', 'Physics', 'Rendering', 'Scripting'],
0),
pytest.param(None,
'TestGem2', None, 'New Gem Name', 'O3DE', 'Asset', 'Gem that exercises Default Gem Template',
'preview.png', '', None, ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0),
pytest.param(None,
'TestGem2', None, 'New Gem Name', 'O3DE', 'Tool', 'Gem that exercises Default Gem Template',
'preview.png', '', None, None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0)
]
)
def test_edit_gem_properties(self, gem_path, gem_name, gem_new_name, gem_display, gem_origin,
gem_type, gem_summary, gem_icon, gem_requirements,
add_tags, remove_tags, replace_tags,
expected_tags, expected_result):
def get_gem_json_data(gem_path: pathlib.Path) -> dict:
return self.gem_json.data
def get_gem_path(gem_name: str) -> pathlib.Path:
return pathlib.Path('D:/TestProject')
def save_o3de_manifest(new_gem_data: dict, gem_path: pathlib.Path) -> bool:
self.gem_json.data = new_gem_data
return True
with patch('o3de.manifest.get_gem_json_data', side_effect=get_gem_json_data) as get_gem_json_data_patch, \
patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch, \
patch('o3de.manifest.get_registered', side_effect=get_gem_path) as get_registered_patch:
result = gem_properties.edit_gem_props(gem_path, gem_name, gem_new_name, gem_display, gem_origin,
gem_type, gem_summary, gem_icon, gem_requirements,
add_tags, remove_tags, replace_tags)
assert result == expected_result
if gem_new_name:
assert self.gem_json.data.get('gem_name', '') == gem_new_name
if gem_display:
assert self.gem_json.data.get('display_name', '') == gem_display
if gem_origin:
assert self.gem_json.data.get('origin', '') == gem_origin
if gem_type:
assert self.gem_json.data.get('type', '') == gem_type
if gem_summary:
assert self.gem_json.data.get('summary', '') == gem_summary
if gem_icon:
assert self.gem_json.data.get('icon_path', '') == gem_icon
if gem_requirements:
assert self.gem_json.data.get('requirments', '') == gem_requirements
assert set(self.gem_json.data.get('user_tags', [])) == set(expected_tags)

@ -58,8 +58,9 @@ class TestEditProjectProperties:
return None
return self.project_json.data
def save_o3de_manifest(new_proj_data: dict, project_path) -> None:
def save_o3de_manifest(new_proj_data: dict, project_path) -> bool:
self.project_json.data = new_proj_data
return True
with patch('o3de.manifest.get_project_json_data', side_effect=get_project_json_data) as get_project_json_data_patch, \
patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch:

Loading…
Cancel
Save