Merge branch 'development' into Atom/guthadam/thumbnail_and_preview_refactor

This commit is contained in:
Guthrie Adams
2021-10-07 21:07:24 -05:00
121 changed files with 2254 additions and 1992 deletions
@@ -42,5 +42,8 @@ class TestAutomation(EditorTestSuite):
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
@@ -0,0 +1,194 @@
"""
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
"""
class Tests:
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
creation_redo = (
"REDO Entity creation success",
"REDO Entity creation failed")
reflection_probe_creation = (
"Reflection Probe Entity successfully created",
"Reflection Probe Entity failed to be created")
reflection_probe_component = (
"Entity has a Reflection Probe component",
"Entity failed to find Reflection Probe component")
reflection_probe_disabled = (
"Reflection Probe component disabled",
"Reflection Probe component was not disabled.")
reflection_map_generated = (
"Reflection Probe cubemap generated",
"Reflection Probe cubemap not generated")
box_shape_component = (
"Entity has a Box Shape component",
"Entity did not have a Box Shape component")
reflection_probe_enabled = (
"Reflection Probe component enabled",
"Reflection Probe component was not enabled.")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
is_visible = (
"Entity is visible",
"Entity was not visible")
is_hidden = (
"Entity is hidden",
"Entity was not hidden")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
deletion_undo = (
"UNDO deletion success",
"UNDO deletion failed")
deletion_redo = (
"REDO deletion success",
"REDO deletion failed")
def AtomEditorComponents_ReflectionProbe_AddedToEntity():
"""
Summary:
Tests the Reflection Probe component can be added to an entity and has the expected functionality.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Create a Reflection Probe entity with no components.
2) Add a Reflection Probe component to Reflection Probe entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
5) Verify Reflection Probe component not enabled.
6) Add Shape component since it is required by the Reflection Probe component.
7) Verify Reflection Probe component is enabled.
8) Enter/Exit game mode.
9) Test IsHidden.
10) Test IsVisible.
11) Verify cubemap generation
12) Delete Reflection Probe entity.
13) UNDO deletion.
14) REDO deletion.
15) Look for errors.
:return: None
"""
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.render as render
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper as helper
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
helper.init_idle()
helper.open_level("", "Base")
# Test steps begin.
# 1. Create a Reflection Probe entity with no components.
reflection_probe_name = "Reflection Probe"
reflection_probe_entity = EditorEntity.create_editor_entity_at(
math.Vector3(512.0, 512.0, 34.0), reflection_probe_name)
Report.critical_result(Tests.reflection_probe_creation, reflection_probe_entity.exists())
# 2. Add a Reflection Probe component to Reflection Probe entity.
reflection_probe_component = reflection_probe_entity.add_component(reflection_probe_name)
Report.critical_result(
Tests.reflection_probe_component,
reflection_probe_entity.has_component(reflection_probe_name))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
general.undo()
# -> UNDO naming entity.
general.undo()
# -> UNDO selecting entity.
general.undo()
# -> UNDO entity creation.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo, not reflection_probe_entity.exists())
# 4. REDO the entity creation and component addition.
# -> REDO entity creation.
general.redo()
# -> REDO selecting entity.
general.redo()
# -> REDO naming entity.
general.redo()
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, reflection_probe_entity.exists())
# 5. Verify Reflection Probe component not enabled.
Report.result(Tests.reflection_probe_disabled, not reflection_probe_component.is_enabled())
# 6. Add Box Shape component since it is required by the Reflection Probe component.
box_shape = "Box Shape"
reflection_probe_entity.add_component(box_shape)
Report.result(Tests.box_shape_component, reflection_probe_entity.has_component(box_shape))
# 7. Verify Reflection Probe component is enabled.
Report.result(Tests.reflection_probe_enabled, reflection_probe_component.is_enabled())
# 8. Enter/Exit game mode.
helper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
helper.exit_game_mode(Tests.exit_game_mode)
# 9. Test IsHidden.
reflection_probe_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, reflection_probe_entity.is_hidden() is True)
# 10. Test IsVisible.
reflection_probe_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, reflection_probe_entity.is_visible() is True)
# 11. Verify cubemap generation
render.EditorReflectionProbeBus(azlmbr.bus.Event, "BakeReflectionProbe", reflection_probe_entity.id)
Report.result(
Tests.reflection_map_generated,
helper.wait_for_condition(
lambda: reflection_probe_component.get_component_property_value("Cubemap|Baked Cubemap Path") != "",
20.0))
# 12. Delete Reflection Probe entity.
reflection_probe_entity.delete()
Report.result(Tests.entity_deleted, not reflection_probe_entity.exists())
# 13. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, reflection_probe_entity.exists())
# 14. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not reflection_probe_entity.exists())
# 15. Look for errors or asserts.
helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponents_ReflectionProbe_AddedToEntity)
@@ -18,6 +18,14 @@
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Threading/ThreadUtils.h>
AZ_CVAR(float, cl_jobThreadsConcurrencyRatio, 0.6f, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system multiplier on the number of hw threads the machine creates at initialization");
AZ_CVAR(uint32_t, cl_jobThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system number of hardware threads that are reserved for O3DE system threads");
AZ_CVAR(uint32_t, cl_jobThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system minimum number of worker threads to create after scaling the number of hw threads");
namespace AZ
{
//=========================================================================
@@ -46,9 +54,10 @@ namespace AZ
JobManagerThreadDesc threadDesc;
int numberOfWorkerThreads = m_numberOfWorkerThreads;
if (numberOfWorkerThreads <= 0)
if (numberOfWorkerThreads <= 0) // spawn default number of threads
{
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), AZStd::thread::hardware_concurrency());
uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved);
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), scaledHardwareThreads);
#if (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
numberOfWorkerThreads = AZ::GetMin(numberOfWorkerThreads, AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS);
#endif // (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
@@ -36,7 +36,7 @@ namespace AZ
*/
int m_stackSize;
JobManagerThreadDesc(int cpuId = -1, int priority = -100000, int stackSize = -1)
JobManagerThreadDesc(int cpuId = -1, int priority = 0, int stackSize = -1)
: m_cpuId(cpuId)
, m_priority(priority)
, m_stackSize(stackSize)
@@ -308,11 +308,11 @@ namespace AZ
void TaskExecutor::SetInstance(TaskExecutor* executor)
{
if (!executor)
if (!executor) // allow unsetting the executor
{
s_executor.Reset();
}
else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities)
else if (!s_executor) // ignore any extra executors after the first (this happens during unit tests)
{
s_executor = AZ::Environment::CreateVariable<TaskExecutor*>(s_executorName, executor);
}
@@ -11,9 +11,15 @@
#include <AzCore/Task/TaskGraphSystemComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Threading/ThreadUtils.h>
// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system.
AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)");
AZ_CVAR(float, cl_taskGraphThreadsConcurrencyRatio, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph calculate the number of worker threads to spawn by scaling the number of hw threads, value is clamped between 0.0f and 1.0f");
AZ_CVAR(uint32_t, cl_taskGraphThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph number of hardware threads that are reserved for O3DE system threads. Value is clamped between 0 and the number of logical cores in the system");
AZ_CVAR(uint32_t, cl_taskGraphThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph minimum number of worker threads to create after scaling the number of hw threads");
static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService");
namespace AZ
@@ -24,8 +30,8 @@ namespace AZ
if (Interface<TaskGraphActiveInterface>::Get() == nullptr)
{
Interface<TaskGraphActiveInterface>::Register(this);
m_taskExecutor = aznew TaskExecutor();
Interface<TaskGraphActiveInterface>::Register(this); // small window that another thread can try to use taskgraph between this line and the set instance.
m_taskExecutor = aznew TaskExecutor(Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved));
TaskExecutor::SetInstance(m_taskExecutor);
}
}
@@ -0,0 +1,25 @@
/*
* 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 <AzCore/Threading/ThreadUtils.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
namespace AZ::Threading
{
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads)
{
const uint32_t maxHardwareThreads = AZStd::thread::hardware_concurrency();
const uint32_t numReservedThreads = AZ::GetMin<uint32_t>(reservedNumThreads, maxHardwareThreads); // protect against num reserved being bigger than the number of hw threads
const uint32_t maxWorkerThreads = maxHardwareThreads - numReservedThreads;
const float requestedWorkerThreads = AZ::GetClamp<float>(workerThreadsRatio, 0.0f, 1.0f) * static_cast<float>(maxWorkerThreads);
const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads);
const uint32_t numWorkerThreads = AZ::GetMax<uint32_t>(minNumWorkerThreads, requestedWorkerThreadsRounded);
return numWorkerThreads;
}
};
@@ -0,0 +1,22 @@
/*
* 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/base.h>
namespace AZ::Threading
{
//! Calculates the number of worker threads a system should use based on the number of hardware threads a device has.
//! result = max (minNumWorkerThreads, workerThreadsRatio * (num_hardware_threads - reservedNumThreads))
//! @param workerThreadsRatio scale applied to the calculated maximum number of threads available after reserved threads have been accounted for. Clamped between 0 and 1.
//! @param minNumWorkerThreads minimum value that will be returned. Value is unclamped and can be more than num_hardware_threads.
//! @param reservedNumThreads number of hardware threads to reserve for O3DE system threads. Value clamped to num_hardware_threads.
//! @return number of worker threads for the calling system to allocate
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads);
};
@@ -639,6 +639,8 @@ set(FILES
Threading/ThreadSafeDeque.inl
Threading/ThreadSafeObject.h
Threading/ThreadSafeObject.inl
Threading/ThreadUtils.h
Threading/ThreadUtils.cpp
Time/ITime.h
Time/TimeSystemComponent.cpp
Time/TimeSystemComponent.h
@@ -59,6 +59,10 @@ namespace AZStd
{
priority = desc->m_priority;
}
else
{
priority = SCHED_OTHER;
}
if (desc->m_name)
{
name = desc->m_name;
@@ -25,7 +25,7 @@ namespace UnitTest
R"X(Executing RC.EXE: '"E:\lyengine\dev\windows\bin\profile\rc.exe" "E:/Directory/File.tga")X",
R"X(Executing RC.EXE with working directory : '')X",
R"X(ResourceCompiler 64 - bit DEBUG)X",
R"X(Platform support : PC, PowerVR, etc2Comp)X",
R"X(Platform support : PC, PowerVR)X",
R"X(Version 1.1.8.6 Nov 5 2018 13 : 28 : 28)X"
};
@@ -74,7 +74,12 @@ namespace AWSMetrics
{
behaviorContext->EBus<AWSMetricsRequestBus>("AWSMetricsRequestBus", "Generate and submit metrics to the metrics analytics pipeline")
->Attribute(AZ::Script::Attributes::Category, "AWSMetrics")
->Event("SubmitMetrics", &AWSMetricsRequestBus::Events::SubmitMetrics)
->Event(
"SubmitMetrics", &AWSMetricsRequestBus::Events::SubmitMetrics,
{ { { "Metrics Attributes list", "The list of metrics attributes to submit." },
{ "Event priority", "Priority of the event. Defaults to 0, which is highest priority." },
{ "Event source override", "Event source used to override the default, 'AWSMetricGem'." },
{ "Buffer metrics", "Whether to buffer metrics and send them in a batch." } } })
->Event("FlushMetrics", &AWSMetricsRequestBus::Events::FlushMetrics)
;
@@ -63,13 +63,10 @@ ly_add_target(
3rdParty::Qt::Widgets
3rdParty::Qt::Gui
3rdParty::astc-encoder
3rdParty::etc2comp
3rdParty::PVRTexTool
3rdParty::squish-ccr
3rdParty::tiff
3rdParty::ISPCTexComp
3rdParty::ilmbase
Legacy::CryCommon
AZ::AzFramework
AZ::AzToolsFramework
AZ::AzQtComponents
@@ -39,16 +39,7 @@ namespace ImageProcessingAtom
ePixelFormat_ASTC_10x8,
ePixelFormat_ASTC_10x10,
ePixelFormat_ASTC_12x10,
ePixelFormat_ASTC_12x12,
//Formats supported by PowerVR GPU. Mainly for ios devices.
ePixelFormat_PVRTC2, //2bpp
ePixelFormat_PVRTC4, //4bpp
//formats for opengl and opengles 3.0 (android devices)
ePixelFormat_EAC_R11, //one channel unsigned data
ePixelFormat_EAC_RG11, //two channel unsigned data
ePixelFormat_ETC2, //Compresses RGB888 data, it taks 4x4 groups of pixel data and compresses each into a 64-bit
ePixelFormat_ETC2a1, //Compresses RGB888A1 data, it taks 4x4 groups of pixel data and compresses each into a 64-bit
ePixelFormat_ETC2a, //Compresses RGBA8888 data with full alpha support
ePixelFormat_ASTC_12x12,
// Standardized Compressed DXGI Formats (DX10+)
// Data in these compressed formats is hardware decodable on all DX10 chips, and manageable with the DX10-API.
@@ -88,7 +79,6 @@ namespace ImageProcessingAtom
};
bool IsASTCFormat(EPixelFormat fmt);
bool IsETCFormat(EPixelFormat fmt);
} // namespace ImageProcessingAtom
namespace AZ
@@ -109,13 +109,6 @@ namespace ImageProcessingAtom
->Value("ASTC_10x10", EPixelFormat::ePixelFormat_ASTC_10x10)
->Value("ASTC_12x10", EPixelFormat::ePixelFormat_ASTC_12x10)
->Value("ASTC_12x12", EPixelFormat::ePixelFormat_ASTC_12x12)
->Value("PVRTC2", EPixelFormat::ePixelFormat_PVRTC2)
->Value("PVRTC4", EPixelFormat::ePixelFormat_PVRTC4)
->Value("EAC_R11", EPixelFormat::ePixelFormat_EAC_R11)
->Value("EAC_RG11", EPixelFormat::ePixelFormat_EAC_RG11)
->Value("ETC2", EPixelFormat::ePixelFormat_ETC2)
->Value("ETC2a1", EPixelFormat::ePixelFormat_ETC2a1)
->Value("ETC2a", EPixelFormat::ePixelFormat_ETC2a)
->Value("BC1", EPixelFormat::ePixelFormat_BC1)
->Value("BC1a", EPixelFormat::ePixelFormat_BC1a)
->Value("BC3", EPixelFormat::ePixelFormat_BC3)
@@ -10,8 +10,6 @@
#include <AzCore/PlatformIncl.h>
#include <Compressors/ASTCCompressor.h>
#include <Compressors/CTSquisher.h>
#include <Compressors/PVRTC.h>
#include <Compressors/ETC2.h>
#include <Compressors/ISPCTextureCompressor.h>
namespace ImageProcessingAtom
@@ -42,26 +40,6 @@ namespace ImageProcessingAtom
}
}
// Both ETC2Compressor and PVRTCCompressor can process ETC formats
// According to Mobile team, Etc2Com is faster than PVRTexLib, so we check with ETC2Compressor before PVRTCCompressor
// Note: with the test I have done, I found out it cost similar time for both Etc2Com and PVRTexLib to compress
// a 2048x2048 test texture to EAC_R11 and EAC_RG11. It was around 7 minutes for EAC_R11 and 14 minutes for EAC_RG11
if (ETC2Compressor::IsCompressedPixelFormatSupported(fmt))
{
if (isCompressing || (!isCompressing && ETC2Compressor::DoesSupportDecompress(fmt)))
{
return ICompressorPtr(new ETC2Compressor());
}
}
if (PVRTCCompressor::IsCompressedPixelFormatSupported(fmt))
{
if (isCompressing || (!isCompressing && PVRTCCompressor::DoesSupportDecompress(fmt)))
{
return ICompressorPtr(new PVRTCCompressor());
}
}
return nullptr;
}
@@ -1,233 +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 <Atom/ImageProcessing/ImageObject.h>
#include <Processing/PixelFormatInfo.h>
#include <Processing/ImageFlags.h>
#include <Converters/PixelOperation.h>
#include <Compressors/ETC2.h>
#include <EtcConfig.h>
#include <Etc.h>
#include <EtcImage.h>
#include <EtcColorFloatRGBA.h>
namespace ImageProcessingAtom
{
//limited to 1 thread because AP requires so. We may change to n when AP allocate n thread to a job in the furture
static const int MAX_COMP_JOBS = 1;
static const int MIN_COMP_JOBS = 1;
static const float ETC_LOW_EFFORT_LEVEL = 25.0f;
static const float ETC_MED_EFFORT_LEVEL = 40.0f;
static const float ETC_HIGH_EFFORT_LEVEL = 80.0f;
//Grab the Etc2Comp specific pixel format enum
static Etc::Image::Format FindEtc2PixelFormat(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_EAC_RG11:
return Etc::Image::Format::RG11;
case ePixelFormat_EAC_R11:
return Etc::Image::Format::R11;
case ePixelFormat_ETC2:
return Etc::Image::Format::RGB8;
case ePixelFormat_ETC2a1:
return Etc::Image::Format::RGB8A1;
case ePixelFormat_ETC2a:
return Etc::Image::Format::RGBA8;
default:
return Etc::Image::Format::FORMATS;
}
}
//Get the errmetric required for the compression
static Etc::ErrorMetric FindErrMetric(Etc::Image::Format fmt)
{
switch (fmt)
{
case Etc::Image::Format::RG11:
return Etc::ErrorMetric::NORMALXYZ;
case Etc::Image::Format::R11:
return Etc::ErrorMetric::NUMERIC;
case Etc::Image::Format::RGB8:
return Etc::ErrorMetric::RGBX;
case Etc::Image::Format::RGBA8:
case Etc::Image::Format::RGB8A1:
return Etc::ErrorMetric::RGBA;
default:
return Etc::ErrorMetric::ERROR_METRICS;
}
}
//Convert to sRGB format
static Etc::Image::Format FindGammaEtc2PixelFormat(Etc::Image::Format fmt)
{
switch (fmt)
{
case Etc::Image::Format::RGB8:
return Etc::Image::Format::SRGB8;
case Etc::Image::Format::RGBA8:
return Etc::Image::Format::SRGBA8;
case Etc::Image::Format::RGB8A1:
return Etc::Image::Format::SRGB8A1;
default:
return Etc::Image::Format::FORMATS;
}
}
bool ETC2Compressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
return (FindEtc2PixelFormat(fmt) != Etc::Image::Format::FORMATS);
}
bool ETC2Compressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
{
//for uncompress format
if (fmt == ePixelFormat_R8G8B8A8)
{
return true;
}
return false;
}
EPixelFormat ETC2Compressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, [[maybe_unused]] EPixelFormat uncompressedfmt) const
{
return ePixelFormat_R8G8B8A8;
}
bool ETC2Compressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
{
return false;
}
ColorSpace ETC2Compressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const
{
return ColorSpace::autoSelect;
}
const char* ETC2Compressor::GetName() const
{
return "ETC2Compressor";
}
IImageObjectPtr ETC2Compressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst,
const CompressOption* compressOption) const
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be uncompressed and dst format need to compressed.
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
//determinate compression quality
ICompressor::EQuality quality = ICompressor::eQuality_Normal;
//get setting from compression option
if (compressOption)
{
quality = compressOption->compressQuality;
}
float qualityEffort = 0.0f;
switch (quality)
{
case eQuality_Preview:
case eQuality_Fast:
{
qualityEffort = ETC_LOW_EFFORT_LEVEL;
break;
}
case eQuality_Normal:
{
qualityEffort = ETC_MED_EFFORT_LEVEL;
break;
}
default:
{
qualityEffort = ETC_HIGH_EFFORT_LEVEL;
}
}
Etc::Image::Format dstEtc2Format = FindEtc2PixelFormat(fmtDst);
if (srcImage->GetImageFlags() & EIF_SRGBRead)
{
dstEtc2Format = FindGammaEtc2PixelFormat(dstEtc2Format);
}
//use to read pixel data from src image
IPixelOperationPtr pixelOp = CreatePixelOperation(fmtSrc);
//get count of bytes per pixel for images
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc)->bitsPerBlock / 8;
const AZ::u32 mipCount = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < mipCount; ++mip)
{
const AZ::u32 width = srcImage->GetWidth(mip);
const AZ::u32 height = srcImage->GetHeight(mip);
// Prepare source data
AZ::u8* srcMem;
AZ::u32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
const AZ::u32 pixelCount = srcImage->GetPixelCount(mip);
Etc::ColorFloatRGBA* rgbaPixels = new Etc::ColorFloatRGBA[pixelCount];
Etc::ColorFloatRGBA* rgbaPixelPtr = rgbaPixels;
float r, g, b, a;
for (AZ::u32 pixelIdx = 0; pixelIdx < pixelCount; pixelIdx++, srcMem += pixelBytes, rgbaPixelPtr++)
{
pixelOp->GetRGBA(srcMem, r, g, b, a);
rgbaPixelPtr->fA = a;
rgbaPixelPtr->fR = r;
rgbaPixelPtr->fG = g;
rgbaPixelPtr->fB = b;
}
//Call into etc2Comp lib to compress. https://medium.com/@duhroach/building-a-blazing-fast-etc2-compressor-307f3e9aad99
Etc::ErrorMetric errMetric = FindErrMetric(dstEtc2Format);
unsigned char* paucEncodingBits;
unsigned int uiEncodingBitsBytes;
unsigned int uiExtendedWidth;
unsigned int uiExtendedHeight;
int iEncodingTime_ms;
Etc::Encode(reinterpret_cast<float*>(rgbaPixels),
width, height,
dstEtc2Format,
errMetric,
qualityEffort,
MIN_COMP_JOBS,
MAX_COMP_JOBS,
&paucEncodingBits, &uiEncodingBitsBytes,
&uiExtendedWidth, &uiExtendedHeight,
&iEncodingTime_ms);
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
memcpy(dstMem, paucEncodingBits, uiEncodingBitsBytes);
delete[] rgbaPixels;
}
return dstImage;
}
IImageObjectPtr ETC2Compressor::DecompressImage(IImageObjectPtr srcImage, [[maybe_unused]] EPixelFormat fmtDst) const
{
//etc2Comp doesn't support decompression
//Since PVRTexLib support ETC formats too. It may take over the decompression.
return nullptr;
}
} // namespace ImageProcessingAtom
@@ -1,31 +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 <Compressors/Compressor.h>
namespace ImageProcessingAtom
{
class ETC2Compressor
: public ICompressor
{
public:
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
static bool DoesSupportDecompress(EPixelFormat fmtDst);
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override;
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override;
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override;
ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final;
const char* GetName() const final;
};
} // namespace ImageProcessingAtom
@@ -1,338 +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 <ImageProcessing_Traits_Platform.h>
#include <AzCore/PlatformIncl.h>
#include <Atom/ImageProcessing/ImageObject.h>
#include <Processing/ImageFlags.h>
#include <Processing/PixelFormatInfo.h>
#include <Compressors/PVRTC.h>
#include <PVRTexture.h>
#include <PVRTextureUtilities.h>
namespace ImageProcessingAtom
{
// Note: PVRTexLib supports ETC formats, PVRTC formats and BC formats
// We haven't tested the performace to compress BC formats compare to CTSquisher
// For PVRTC formats, we only added PVRTC 1 support for now
// The compression for ePVRTPF_EAC_R11 and ePVRTPF_EAC_RG11 are very slow. It takes 7 and 14 minutes for a 2048x2048 texture.
EPVRTPixelFormat FindPvrPixelFormat(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_PVRTC2:
return ePVRTPF_PVRTCI_2bpp_RGBA;
case ePixelFormat_PVRTC4:
return ePVRTPF_PVRTCI_4bpp_RGBA;
case ePixelFormat_EAC_R11:
return ePVRTPF_EAC_R11;
case ePixelFormat_EAC_RG11:
return ePVRTPF_EAC_RG11;
case ePixelFormat_ETC2:
return ePVRTPF_ETC2_RGB;
case ePixelFormat_ETC2a1:
return ePVRTPF_ETC2_RGB_A1;
case ePixelFormat_ETC2a:
return ePVRTPF_ETC2_RGBA;
default:
return ePVRTPF_NumCompressedPFs;
}
}
bool PVRTCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
return (FindPvrPixelFormat(fmt) != ePVRTPF_NumCompressedPFs);
}
bool PVRTCCompressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
{
//for uncompress format
if (fmt == ePixelFormat_R8G8B8A8)
{
return true;
}
return false;
}
EPixelFormat PVRTCCompressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, [[maybe_unused]] EPixelFormat uncompressedfmt) const
{
return ePixelFormat_R8G8B8A8;
}
ColorSpace PVRTCCompressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const
{
return ColorSpace::autoSelect;
}
const char* PVRTCCompressor::GetName() const
{
return "PVRTCCompressor";
}
bool PVRTCCompressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
{
return true;
}
IImageObjectPtr PVRTCCompressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst,
const CompressOption* compressOption) const
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be uncompressed and dst format need to compressed.
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
//determinate compression quality
pvrtexture::ECompressorQuality internalQuality = pvrtexture::eETCFast;
ICompressor::EQuality quality = ICompressor::eQuality_Normal;
AZ::Vector3 uniformWeights = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
AZ::Vector3 weights = uniformWeights;
bool isUniform = true;
//get setting from compression option
if (compressOption)
{
quality = compressOption->compressQuality;
weights = compressOption->rgbWeight;
isUniform = (weights == uniformWeights);
}
if (IsETCFormat(fmtDst))
{
if ((quality <= eQuality_Normal) && isUniform)
{
internalQuality = pvrtexture::eETCFast;
}
else if (quality <= eQuality_Normal)
{
internalQuality = pvrtexture::eETCNormal;
}
else if (isUniform)
{
internalQuality = pvrtexture::eETCSlow;
}
else
{
internalQuality = pvrtexture::eETCSlow;
}
}
else
{
if (quality == eQuality_Preview)
{
internalQuality = pvrtexture::ePVRTCFastest;
}
else if (quality == eQuality_Fast)
{
internalQuality = pvrtexture::ePVRTCFast;
}
else if (quality == eQuality_Normal)
{
internalQuality = pvrtexture::ePVRTCNormal;
}
else
{
internalQuality = pvrtexture::ePVRTCHigh;
}
}
// setup color space
EPVRTColourSpace cspace = ePVRTCSpacelRGB;
if (srcImage->GetImageFlags() & EIF_SRGBRead)
{
cspace = ePVRTCSpacesRGB;
}
//setup src texture for compression
const pvrtexture::PixelType srcPixelType('r', 'g', 'b', 'a', 8, 8, 8, 8);
const AZ::u32 dstMips = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < dstMips; ++mip)
{
const AZ::u32 width = srcImage->GetWidth(mip);
const AZ::u32 height = srcImage->GetHeight(mip);
// Prepare source data
AZ::u8* srcMem;
uint32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
const pvrtexture::CPVRTextureHeader srcHeader(
srcPixelType.PixelTypeID, // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
1, // uint32 u32NumMipMaps=1,
1, // uint32 u32NumArrayMembers=1,
1, // uint32 u32NumFaces=1,
cspace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB,
ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm,
false); // bool bPreMultiplied=false);
pvrtexture::CPVRTexture compressTexture(srcHeader, srcMem);
//compressing
bool isSuccess = false;
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
try
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
{
isSuccess = pvrtexture::Transcode(
compressTexture,
pvrtexture::PixelType(FindPvrPixelFormat(fmtDst)),
ePVRTVarTypeUnsignedByteNorm,
cspace,
internalQuality);
}
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
catch (...)
{
AZ_Error("Image Processing", false, "Unknown exception in PVRTexLib");
return nullptr;
}
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
if (!isSuccess)
{
AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib.");
return nullptr;
}
// Getting compressed data
const void* const compressedData = compressTexture.getDataPtr();
if (!compressedData)
{
AZ_Error("Image Processing", false, "Failed to obtain compressed image data by using PVRTexLib");
return nullptr;
}
const AZ::u32 compressedDataSize = compressTexture.getDataSize();
if (dstImage->GetMipBufSize(mip) != compressedDataSize)
{
AZ_Error("Image Processing", false, "Compressed image data size mismatch while using PVRTexLib");
return nullptr;
}
//save compressed data to dst image
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
memcpy(dstMem, compressedData, compressedDataSize);
}
return dstImage;
}
IImageObjectPtr PVRTCCompressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat(); //compressed
if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
EPVRTColourSpace colorSpace = ePVRTCSpacelRGB;
if (srcImage->GetImageFlags() & EIF_SRGBRead)
{
colorSpace = ePVRTCSpacesRGB;
}
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
const AZ::u32 mipCount = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < mipCount; ++mip)
{
const AZ::u32 width = srcImage->GetWidth(mip);
const AZ::u32 height = srcImage->GetHeight(mip);
// Preparing source compressed data
const pvrtexture::CPVRTextureHeader compressedHeader(
FindPvrPixelFormat(fmtSrc), // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
1, // uint32 u32NumMipMaps=1,
1, // uint32 u32NumArrayMembers=1,
1, // uint32 u32NumFaces=1,
colorSpace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB,
ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm,
false); // bool bPreMultiplied=false);
const AZ::u32 compressedDataSize = compressedHeader.getDataSize();
if (srcImage->GetMipBufSize(mip) != compressedDataSize)
{
AZ_Error("Image Processing", false, "Decompressed image data size mismatch while using PVRTexLib");
return nullptr;
}
AZ::u8* srcMem;
AZ::u32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
pvrtexture::CPVRTexture cTexture(compressedHeader, srcMem);
// Decompress
bool bOk = false;
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
try
{
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
bOk = pvrtexture::Transcode(
cTexture,
pvrtexture::PVRStandard8PixelType,
ePVRTVarTypeUnsignedByteNorm,
colorSpace,
pvrtexture::ePVRTCHigh);
#if AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
}
catch (...)
{
AZ_Error("Image Processing", false, "Unknown exception in PVRTexLib when decompressing");
return nullptr;
}
#endif // AZ_TRAIT_IMAGEPROCESSING_SUPPORT_TRY_CATCH
if (!bOk)
{
AZ_Error("Image Processing", false, "Failed to decompress an image by using PVRTexLib");
return nullptr;
}
// Getting decompressed data
const void* const pDecompressedData = cTexture.getDataPtr();
if (!pDecompressedData)
{
AZ_Error("Image Processing", false, "Failed to obtain decompressed image data by using PVRTexLib");
return nullptr;
}
const AZ::u32 decompressedDataSize = cTexture.getDataSize();
if (dstImage->GetMipBufSize(mip) != decompressedDataSize)
{
AZ_Error("Image Processing", false, "Decompressed image data size mismatch while using PVRTexLib");
return nullptr;
}
//save decompressed image to dst image
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
memcpy(dstMem, pDecompressedData, decompressedDataSize);
}
return dstImage;
}
} //namespace ImageProcessingAtom
@@ -1,30 +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 <Compressors/Compressor.h>
namespace ImageProcessingAtom
{
class PVRTCCompressor
: public ICompressor
{
public:
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
static bool DoesSupportDecompress(EPixelFormat fmtDst);
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override;
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override;
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override;
ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final;
const char* GetName() const final;
};
} // namespace ImageProcessingAtom
@@ -82,7 +82,6 @@ namespace ImageProcessingAtomEditor
presetInfoText += "\n";
presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False");
presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False");
presetInfoText += QString("Is Power Of 2: %1\n").arg(presetSettings->m_isPowerOf2 ? "True" : "False");
presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False");
presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip);
presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals);
@@ -24,201 +24,6 @@ namespace ImageProcessingAtom
{
namespace DdsLoader
{
IImageObject* CreateImageFromHeaderLegacy(DDS_HEADER_LEGACY& header, DDS_HEADER_DXT10& exthead)
{
EPixelFormat eFormat = ePixelFormat_Unknown;
AZ::u32 dwWidth, dwMips, dwHeight;
AZ::u32 imageFlags = header.dwReserved1;
AZ::Color colMinARGB, colMaxARGB;
dwWidth = header.dwWidth;
dwHeight = header.dwHeight;
dwMips = 1;
if (header.dwHeaderFlags & DDS_HEADER_FLAGS_MIPMAP)
{
dwMips = header.dwMipMapCount;
}
if ((header.dwSurfaceFlags & DDS_SURFACE_FLAGS_CUBEMAP) && (header.dwCubemapFlags & DDS_CUBEMAP_ALLFACES))
{
AZ_Assert(header.dwReserved1 & EIF_Cubemap, "Image flag should have cubemap flag");
dwHeight *= 6;
}
colMinARGB = AZ::Color(header.cMinColor[0], header.cMinColor[1], header.cMinColor[2], header.cMinColor[3]);
colMaxARGB = AZ::Color(header.cMaxColor[0], header.cMaxColor[1], header.cMaxColor[2], header.cMaxColor[3]);
//get pixel format
{
// DX10 formats
if (header.ddspf.dwFourCC == FOURCC_DX10)
{
AZ::u32 dxgiFormat = exthead.dxgiFormat;
//remove the SRGB from dxgi format and add sRGB to image flag
if (dxgiFormat == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
{
dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
}
else if (dxgiFormat == DXGI_FORMAT_BC1_UNORM_SRGB)
{
dxgiFormat = DXGI_FORMAT_BC1_UNORM;
}
else if (dxgiFormat == DXGI_FORMAT_BC2_UNORM_SRGB)
{
dxgiFormat = DXGI_FORMAT_BC2_UNORM;
}
else if (dxgiFormat == DXGI_FORMAT_BC3_UNORM_SRGB)
{
dxgiFormat = DXGI_FORMAT_BC3_UNORM;
}
else if (dxgiFormat == DXGI_FORMAT_BC7_UNORM_SRGB)
{
dxgiFormat = DXGI_FORMAT_BC7_UNORM;
}
//add rgb flag if the dxgiformat was changed (which means it was sRGB format) above
if (dxgiFormat != exthead.dxgiFormat)
{
AZ_Assert(imageFlags & EIF_SRGBRead, "Image flags should have SRGBRead flag");
imageFlags |= EIF_SRGBRead;
}
//check all the pixel formats and find matching one
if (dxgiFormat != DXGI_FORMAT_UNKNOWN)
{
int i = 0;
for (; i < ePixelFormat_Count; i++)
{
const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo((EPixelFormat)i);
if (static_cast<AZ::u32>(info->d3d10Format) == dxgiFormat)
{
eFormat = (EPixelFormat)i;
break;
}
}
if (i == ePixelFormat_Count)
{
AZ_Error("Image Processing", false, "Unhandled d3d10 format: %d", dxgiFormat);
return nullptr;
}
}
}
else
{
//for non-dx10 formats, use fourCC to find out its pixel formats
//go through all pixel formats and find a match with the fourcc
for (AZ::u32 formatIdx = 0; formatIdx < ePixelFormat_Count; formatIdx++)
{
const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo((EPixelFormat)formatIdx);
if (header.ddspf.dwFourCC == info->fourCC)
{
eFormat = (EPixelFormat)formatIdx;
break;
}
}
//legacy formats. This section is only used for load dds files converted by RC.exe
//our save to dds file function won't use any of these fourcc
if (eFormat == ePixelFormat_Unknown)
{
if (header.ddspf.dwFourCC == FOURCC_DXT1)
{
eFormat = ePixelFormat_BC1;
}
else if (header.ddspf.dwFourCC == FOURCC_DXT5)
{
eFormat = ePixelFormat_BC3;
}
else if (header.ddspf.dwFourCC == FOURCC_3DCP)
{
eFormat = ePixelFormat_BC4;
}
else if (header.ddspf.dwFourCC == FOURCC_3DC)
{
eFormat = ePixelFormat_BC5;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_R32F)
{
eFormat = ePixelFormat_R32F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_G32R32F)
{
eFormat = ePixelFormat_R32G32F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_A32B32G32R32F)
{
eFormat = ePixelFormat_R32G32B32A32F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_R16F)
{
eFormat = ePixelFormat_R16F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_G16R16F)
{
eFormat = ePixelFormat_R16G16F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16F)
{
eFormat = ePixelFormat_R16G16B16A16F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16)
{
eFormat = ePixelFormat_R16G16B16A16;
}
else if ((header.ddspf.dwFlags == DDS_RGBA || header.ddspf.dwFlags == DDS_RGB)
&& header.ddspf.dwRGBBitCount == 32)
{
if (header.ddspf.dwRBitMask == 0x00ff0000)
{
eFormat = ePixelFormat_B8G8R8A8;
}
else
{
eFormat = ePixelFormat_R8G8B8A8;
}
}
else if (header.ddspf.dwFlags == DDS_LUMINANCEA && header.ddspf.dwRGBBitCount == 8)
{
eFormat = ePixelFormat_R8G8;
}
else if (header.ddspf.dwFlags == DDS_LUMINANCE && header.ddspf.dwRGBBitCount == 8)
{
eFormat = ePixelFormat_A8;
}
else if ((header.ddspf.dwFlags == DDS_A || header.ddspf.dwFlags == DDS_A_ONLY || header.ddspf.dwFlags == (DDS_A | DDS_A_ONLY)) && header.ddspf.dwRGBBitCount == 8)
{
eFormat = ePixelFormat_A8;
}
}
}
}
if (eFormat == ePixelFormat_Unknown)
{
AZ_Error("Image Processing", false, "Unhandled dds pixel format fourCC: %d, flags: %d",
header.ddspf.dwFourCC, header.ddspf.dwFlags);
return nullptr;
}
IImageObject* newImage = IImageObject::CreateImage(dwWidth, dwHeight, dwMips, eFormat);
if (dwMips != newImage->GetMipCount())
{
AZ_Error("Image Processing", false, "Mipcount from image data doesn't match image size and pixelformat");
delete newImage;
return nullptr;
}
//set properties
newImage->SetImageFlags(imageFlags);
newImage->SetAverageBrightness(header.fAvgBrightness);
newImage->SetColorRange(colMinARGB, colMaxARGB);
newImage->SetNumPersistentMips(header.bNumPersistentMips);
return newImage;
}
bool IsExtensionSupported(const char* extension)
{
QString ext = QString(extension).toLower();
@@ -226,215 +31,6 @@ namespace ImageProcessingAtom
return ext == "dds";
}
IImageObject* LoadImageFromFileLegacy(const AZStd::string& filename)
{
AZ::IO::SystemFile file;
file.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY);
AZ::IO::SystemFileStream fileLoadStream(&file, true);
if (!fileLoadStream.IsOpen())
{
AZ_Warning("Image Processing", false, "%s: failed to open file %s", __FUNCTION__, filename.c_str());
return nullptr;
}
AZStd::string ext = "";
AzFramework::StringFunc::Path::GetExtension(filename.c_str(), ext, false);
bool isAlphaImage = (ext == "a");
IImageObject* imageObj = LoadImageFromFileStreamLegacy(fileLoadStream);
//load mips from seperated files if it's splitted
if (imageObj && imageObj->HasImageFlags(EIF_Splitted))
{
AZStd::string baseName;
if (isAlphaImage)
{
baseName = filename.substr(0, filename.size() - 2);
}
else
{
baseName = filename;
}
AZ::u32 externalMipCount = 0;
if (imageObj->GetNumPersistentMips() < imageObj->GetMipCount())
{
externalMipCount = imageObj->GetMipCount() - imageObj->GetNumPersistentMips();
}
//load other mips from files with number extensions
for (AZ::u32 mipIdx = 1; mipIdx <= externalMipCount; mipIdx++)
{
AZ::u32 mip = externalMipCount - mipIdx;
AZStd::string mipFileName = AZStd::string::format("%s.%d%s", baseName.c_str(), mipIdx, isAlphaImage ? "a" : "");
AZ::IO::SystemFile mipFile;
mipFile.Open(mipFileName.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY);
AZ::IO::SystemFileStream mipFileLoadStream(&mipFile, true);
if (!mipFileLoadStream.IsOpen())
{
AZ_Warning("Image Processing", false, "%s: failed to open mip file %s", __FUNCTION__, mipFileName.c_str());
break;
}
AZ::u32 pitch;
AZ::u8* mem;
imageObj->GetImagePointer(mip, mem, pitch);
AZ::u32 bufSize = imageObj->GetMipBufSize(mip);
mipFileLoadStream.Read(bufSize, mem);
}
}
return imageObj;
}
IImageObject* LoadImageFromFileStreamLegacy(AZ::IO::SystemFileStream& fileLoadStream)
{
if (fileLoadStream.GetLength() - fileLoadStream.GetCurPos() < sizeof(DDS_FILE_DESC_LEGACY))
{
AZ_Error("Image Processing", false, "%s: Trying to load a none-DDS file", __FUNCTION__);
return nullptr;
}
DDS_FILE_DESC_LEGACY desc;
DDS_HEADER_DXT10 exthead;
AZ::IO::SizeType startPos = fileLoadStream.GetCurPos();
fileLoadStream.Read(sizeof(desc.dwMagic), &desc.dwMagic);
if (desc.dwMagic != FOURCC_DDS)
{
desc.dwMagic = FOURCC_DDS;
//the old cry .a file doesn't have "DDS " in the beginning of the file.
//so reset to previous position
fileLoadStream.Seek(startPos, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
fileLoadStream.Read(sizeof(desc.header), &desc.header);
if (!desc.IsValid())
{
AZ_Error("Image Processing", false, "%s: Trying to load a none-DDS file", __FUNCTION__);
return nullptr;
}
if (desc.header.IsDX10Ext())
{
fileLoadStream.Read(sizeof(exthead), &exthead);
}
IImageObject* outImage = CreateImageFromHeaderLegacy(desc.header, exthead);
if (outImage == nullptr)
{
return nullptr;
}
//load mip data
AZ::u32 mipStart = 0;
//There are at least three lowest mips are in the file if it was splitted. This is to load splitted dds file exported by legacy rc.exe
int numPersistentMips = outImage->GetNumPersistentMips();
if (numPersistentMips == 0 && outImage->HasImageFlags(EIF_Splitted))
{
outImage->SetNumPersistentMips(3);
}
if (outImage->HasImageFlags(EIF_Splitted)
&& outImage->GetMipCount() > outImage->GetNumPersistentMips())
{
mipStart = outImage->GetMipCount() - outImage->GetNumPersistentMips();
}
AZ::u32 faces = 1;
if (outImage->HasImageFlags(EIF_Cubemap))
{
faces = 6;
}
for (AZ::u32 face = 0; face < faces; face++)
{
for (AZ::u32 mip = mipStart; mip < outImage->GetMipCount(); ++mip)
{
AZ::u32 pitch;
AZ::u8* mem;
outImage->GetImagePointer(mip, mem, pitch);
AZ::u32 faceBufSize = outImage->GetMipBufSize(mip) / faces;
fileLoadStream.Read(faceBufSize, mem + faceBufSize * face);
}
}
return outImage;
}
IImageObject* LoadAttachedImageFromDdsFileLegacy(const AZStd::string& filename, IImageObjectPtr originImage)
{
if (originImage == nullptr)
{
return nullptr;
}
AZ_Assert(originImage->HasImageFlags(EIF_AttachedAlpha),
"this function should only be called for origin image loaded from same file with attached alpha flag");
AZ::IO::SystemFile file;
file.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY);
AZ::IO::SystemFileStream fileLoadStream(&file, true);
if (!fileLoadStream.IsOpen())
{
AZ_Warning("Image Processing", false, "%s: failed to open file %s", __FUNCTION__, filename.c_str());
return nullptr;
}
DDS_FILE_DESC_LEGACY desc;
DDS_HEADER_DXT10 exthead;
fileLoadStream.Read(sizeof(desc), &desc);
if (desc.dwMagic != FOURCC_DDS)
{
AZ_Error("Image Processing", false, "%s:Trying to load a none-DDS file", __FUNCTION__);
return nullptr;
}
if (desc.header.IsDX10Ext())
{
fileLoadStream.Read(sizeof(exthead), &exthead);
}
//skip size for originImage's mip data
for (AZ::u32 mip = 0; mip < originImage->GetMipCount(); ++mip)
{
AZ::u32 bufSize = originImage->GetMipBufSize(mip);
fileLoadStream.Seek(bufSize, AZ::IO::GenericStream::ST_SEEK_CUR);
}
IImageObject* alphaImage = nullptr;
AZ::u32 marker = 0;
fileLoadStream.Read(4, &marker);
if (marker == FOURCC_CExt) // marker for the start of O3DE Extended data
{
fileLoadStream.Read(4, &marker);
if (FOURCC_AttC == marker) // Attached Channel chunk
{
AZ::u32 size = 0;
fileLoadStream.Read(4, &size);
alphaImage = LoadImageFromFileStreamLegacy(fileLoadStream);
fileLoadStream.Read(4, &marker);
}
if (FOURCC_CEnd == marker) // marker for the end of O3DE Extended data
{
fileLoadStream.Read(4, &marker);
}
}
return alphaImage;
}
// Create an image object from standard dds header
IImageObject* CreateImageFromHeader(DDS_HEADER& header, DDS_HEADER_DXT10& exthead)
{
@@ -526,45 +122,14 @@ namespace ImageProcessingAtom
{
format = ePixelFormat_BC1;
}
else if (header.ddspf.dwFourCC == FOURCC_DXT5)
else if (header.ddspf.dwFourCC == FOURCC_DXT5 || header.ddspf.dwFourCC == FOURCC_DXT4)
{
format = ePixelFormat_BC3;
}
else if (header.ddspf.dwFourCC == FOURCC_3DCP)
else
{
format = ePixelFormat_BC4;
}
else if (header.ddspf.dwFourCC == FOURCC_3DC)
{
format = ePixelFormat_BC5;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_R32F)
{
format = ePixelFormat_R32F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_G32R32F)
{
format = ePixelFormat_R32G32F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_A32B32G32R32F)
{
format = ePixelFormat_R32G32B32A32F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_R16F)
{
format = ePixelFormat_R16F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_G16R16F)
{
format = ePixelFormat_R16G16F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16F)
{
format = ePixelFormat_R16G16B16A16F;
}
else if (header.ddspf.dwFourCC == DDS_FOURCC_A16B16G16R16)
{
format = ePixelFormat_R16G16B16A16;
AZ_Error("Image Processing", false, "unsupported fourCC format: 0x%x", header.ddspf.dwFourCC);
return nullptr;
}
}
else
@@ -44,11 +44,6 @@ namespace ImageProcessingAtom
{
bool IsExtensionSupported(const char* extension);
IImageObject* LoadImageFromFile(const AZStd::string& filename);
// These functions are for loading legacy O3DE dds files
IImageObject* LoadImageFromFileLegacy(const AZStd::string& filename);
IImageObject* LoadImageFromFileStreamLegacy(AZ::IO::SystemFileStream& fileLoadStream);
IImageObject* LoadAttachedImageFromDdsFileLegacy(const AZStd::string& filename, IImageObjectPtr originImage);
};// namespace DdsLoader
// Load .exr files to an image object
@@ -5,8 +5,3 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
set(LY_COMPILE_OPTIONS
PRIVATE
-fexceptions #ImageLoader/ExrLoader.cpp and PVRTC.cpp uses exceptions
)
@@ -237,14 +237,8 @@ namespace ImageProcessingAtom
const static AZ::u32 FOURCC_CEnd = IMAGE_BUIDER_MAKEFOURCC('C', 'E', 'n', 'd'); // O3DE extension end
const static AZ::u32 FOURCC_AttC = IMAGE_BUIDER_MAKEFOURCC('A', 't', 't', 'C'); // Chunk Attached Channel
//Fourcc for pixel formats which aren't supported by dx10, such as astc formats, etc formats, pvrtc formats
//Fourcc for pixel formats which aren't supported by dx10, such as astc formats
//They are used for dwFourCC of dds header's DDS_PIXELFORMAT to identify non-dx10 pixel formats
const static AZ::u32 FOURCC_EAC_R11 = IMAGE_BUIDER_MAKEFOURCC('E', 'A', 'R', ' ');
const static AZ::u32 FOURCC_EAC_RG11 = IMAGE_BUIDER_MAKEFOURCC('E', 'A', 'R', 'G');
const static AZ::u32 FOURCC_ETC2 = IMAGE_BUIDER_MAKEFOURCC('E', 'T', '2', ' ');
const static AZ::u32 FOURCC_ETC2A = IMAGE_BUIDER_MAKEFOURCC('E', 'T', '2', 'A');
const static AZ::u32 FOURCC_PVRTC2 = IMAGE_BUIDER_MAKEFOURCC('P', 'V', 'R', '2');
const static AZ::u32 FOURCC_PVRTC4 = IMAGE_BUIDER_MAKEFOURCC('P', 'V', 'R', '4');
const static AZ::u32 FOURCC_ASTC_4x4 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '4', '4');
const static AZ::u32 FOURCC_ASTC_5x4 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '5', '4');
const static AZ::u32 FOURCC_ASTC_5x5 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', '5', '5');
@@ -260,10 +254,10 @@ namespace ImageProcessingAtom
const static AZ::u32 FOURCC_ASTC_12x10 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'C', 'A');
const static AZ::u32 FOURCC_ASTC_12x12 = IMAGE_BUIDER_MAKEFOURCC('A', 'S', 'C', 'C');
//legacy formats names. they are only used for load rc.exe's dds formats
//legacy formats names. they are only used for load old dds formats.
const static AZ::u32 FOURCC_DXT1 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '1');
const static AZ::u32 FOURCC_DXT2 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '2');
const static AZ::u32 FOURCC_DXT3 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '3');
const static AZ::u32 FOURCC_DXT4 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '4');
const static AZ::u32 FOURCC_DXT5 = IMAGE_BUIDER_MAKEFOURCC('D', 'X', 'T', '5');
const static AZ::u32 FOURCC_3DCP = IMAGE_BUIDER_MAKEFOURCC('A', 'T', 'I', '1');
const static AZ::u32 FOURCC_3DC = IMAGE_BUIDER_MAKEFOURCC('A', 'T', 'I', '2');
}
@@ -52,19 +52,6 @@ namespace ImageProcessingAtom
return false;
}
bool IsETCFormat(EPixelFormat fmt)
{
if (fmt == ePixelFormat_ETC2
|| fmt == ePixelFormat_ETC2a
|| fmt == ePixelFormat_ETC2a1
|| fmt == ePixelFormat_EAC_R11
|| fmt == ePixelFormat_EAC_RG11)
{
return true;
}
return false;
}
PixelFormatInfo::PixelFormatInfo(
uint32_t a_bitsPerPixel,
uint32_t a_Channels,
@@ -96,7 +83,6 @@ namespace ImageProcessingAtom
, fourCC(a_fourCC)
, eSampleType(a_eSampleType)
, szName(a_szName)
, szLegacyName(a_szName)
, szDescription(a_szDescription)
, bCompressed(a_bCompressed)
, bSelectable(a_bSelectable)
@@ -122,15 +108,6 @@ namespace ImageProcessingAtom
CPixelFormats::CPixelFormats()
{
InitPixelFormats();
m_removedLegacyFormats["DXT1"] = ePixelFormat_BC1;
m_removedLegacyFormats["DXT1a"] = ePixelFormat_BC1a;
m_removedLegacyFormats["DXT3"] = ePixelFormat_BC3;
m_removedLegacyFormats["DXT3t"] = ePixelFormat_BC3t;
m_removedLegacyFormats["DXT5"] = ePixelFormat_BC3;
m_removedLegacyFormats["DXT5t"] = ePixelFormat_BC3t;
m_removedLegacyFormats["3DCp"] = ePixelFormat_BC4;
m_removedLegacyFormats["3DC"] = ePixelFormat_BC5;
}
void CPixelFormats::InitPixelFormat(EPixelFormat format, const PixelFormatInfo& formatInfo)
@@ -176,13 +153,6 @@ namespace ImageProcessingAtom
InitPixelFormat(ePixelFormat_ASTC_10x10, PixelFormatInfo(0, 4, true, "?", 16, 16, 10, 10, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_10x10, ESampleType::eSampleType_Compressed, "ASTC_10x10", "ASTC 10x10 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_12x10, PixelFormatInfo(0, 4, true, "?", 16, 16, 12, 10, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_12x10, ESampleType::eSampleType_Compressed, "ASTC_12x10", "ASTC 12x10 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ASTC_12x12, PixelFormatInfo(0, 4, true, "?", 16, 16, 12, 12, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ASTC_12x12, ESampleType::eSampleType_Compressed, "ASTC_12x12", "ASTC 12x12 compressed texture format", true, false));
InitPixelFormat(ePixelFormat_PVRTC2, PixelFormatInfo(2, 4, true, "2", 16, 16, 8, 4, 64, true, DXGI_FORMAT_UNKNOWN, FOURCC_PVRTC2, ESampleType::eSampleType_Compressed, "PVRTC2", "POWERVR 2 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_PVRTC4, PixelFormatInfo(4, 4, true, "2", 8, 8, 4, 4, 64, true, DXGI_FORMAT_UNKNOWN, FOURCC_PVRTC4, ESampleType::eSampleType_Compressed, "PVRTC4", "POWERVR 4 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_EAC_R11, PixelFormatInfo(4, 1, true, "4", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_EAC_R11, ESampleType::eSampleType_Compressed, "EAC_R11", "EAC 4 bpp single channel texture format", true, false));
InitPixelFormat(ePixelFormat_EAC_RG11, PixelFormatInfo(8, 2, false, "0", 4, 4, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_EAC_RG11, ESampleType::eSampleType_Compressed, "EAC_RG11", "EAC 8 bpp dual channel texture format", true, false));
InitPixelFormat(ePixelFormat_ETC2, PixelFormatInfo(4, 3, false, "0", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2, ESampleType::eSampleType_Compressed, "ETC2", "ETC2 RGB 4 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ETC2a, PixelFormatInfo(8, 4, true, "4", 4, 4, 4, 4, 128, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2A, ESampleType::eSampleType_Compressed, "ETC2a", "ETC2 RGBA 8 bpp compressed texture format", true, false));
InitPixelFormat(ePixelFormat_ETC2a1, PixelFormatInfo(4, 4, true, "1", 4, 4, 4, 4, 64, false, DXGI_FORMAT_UNKNOWN, FOURCC_ETC2A, ESampleType::eSampleType_Compressed, "ETC2a1", "ETC2 RGBA1 8 bpp compressed texture format", true, false));
// Standardized Compressed DXGI Formats (DX10+)
// Data in these compressed formats is hardware decodable on all DX10 chips, and manageable with the DX10-API.
@@ -216,17 +186,6 @@ namespace ImageProcessingAtom
InitPixelFormat(ePixelFormat_R32, PixelFormatInfo(32, 1, false, "0", 1, 1, 1, 1, 32, false, DXGI_FORMAT_FORCE_UINT, FOURCC_DX10, ESampleType::eSampleType_Uint32, "R32", "32-bit red only", false, false));
//Set legacy name it can be used for convertion
m_pixelFormatInfo[ePixelFormat_R8G8B8A8].szLegacyName = "A8R8G8B8";
m_pixelFormatInfo[ePixelFormat_R8G8B8X8].szLegacyName = "X8R8G8B8";
m_pixelFormatInfo[ePixelFormat_R8G8].szLegacyName = "G8R8";
m_pixelFormatInfo[ePixelFormat_R16G16B16A16].szLegacyName = "A16B16G16R16";
m_pixelFormatInfo[ePixelFormat_R16G16].szLegacyName = "G16R16";
m_pixelFormatInfo[ePixelFormat_R32G32B32A32F].szLegacyName = "A32B32G32R32F";
m_pixelFormatInfo[ePixelFormat_R32G32F].szLegacyName = "G32R32F";
m_pixelFormatInfo[ePixelFormat_R16G16B16A16F].szLegacyName = "A16B16G16R16F";
m_pixelFormatInfo[ePixelFormat_R16G16F].szLegacyName = "G16R16F";
//validate all pixel formats are proper initialized
for (int i = 0; i < ePixelFormat_Count; ++i)
{
@@ -249,23 +208,6 @@ namespace ImageProcessingAtom
return ePixelFormat_Unknown;
}
EPixelFormat CPixelFormats::FindPixelFormatByLegacyName(const char* name)
{
if (m_removedLegacyFormats.find(name) != m_removedLegacyFormats.end())
{
return m_removedLegacyFormats[name];
}
for (int i = 0; i < ePixelFormat_Count; ++i)
{
if (azstricmp(m_pixelFormatInfo[i].szLegacyName, name) == 0)
{
return (EPixelFormat)i;
}
}
return ePixelFormat_Unknown;
}
const PixelFormatInfo* CPixelFormats::GetPixelFormatInfo(EPixelFormat format)
{
AZ_Assert((format >= 0) && (format < ePixelFormat_Count), "Unsupport pixel format: %d", format);
@@ -119,7 +119,6 @@ namespace ImageProcessingAtom
bool bSquarePow2; // whether the pixel format requires image size be square and power of 2.
DXGI_FORMAT d3d10Format; // the mapping d3d10 pixel format
ESampleType eSampleType; // the data type used to present pixel
const char* szLegacyName; // name used for cryEngine
const char* szName; // name for showing in editors
const char* szDescription; // description for showing in editors
bool bCompressed; // if it's a compressed format
@@ -173,10 +172,6 @@ namespace ImageProcessingAtom
bool IsFormatSigned(EPixelFormat fmt);
bool IsFormatFloatingPoint(EPixelFormat fmt, bool bFullPrecision);
//find the pixel format for name used by Cry's RC.ini
//returns ePixelFormat_Unknown if the name was not found in registed format list
EPixelFormat FindPixelFormatByLegacyName(const char* name);
//find pixel format by its name
EPixelFormat FindPixelFormatByName(const char* name);
@@ -208,9 +203,6 @@ namespace ImageProcessingAtom
//pixel format name to pixel format enum
AZStd::map<AZStd::string, EPixelFormat> m_pixelFormatNameMap;
// some formats from cryEngine were removed. using this name-pixelFormat mapping to look for new format
AZStd::map<AZStd::string, EPixelFormat> m_removedLegacyFormats;
};
template <class TInteger>
@@ -102,32 +102,6 @@ namespace ImageProcessingAtom
case AZ::RHI::Format::ASTC_12x12_UNORM:
return ePixelFormat_ASTC_12x12;
case AZ::RHI::Format::PVRTC2_UNORM_SRGB:
isSRGB = true;
case AZ::RHI::Format::PVRTC2_UNORM:
return ePixelFormat_PVRTC2;
case AZ::RHI::Format::PVRTC4_UNORM_SRGB:
isSRGB = true;
case AZ::RHI::Format::PVRTC4_UNORM:
return ePixelFormat_PVRTC4;
case AZ::RHI::Format::EAC_R11_UNORM:
return ePixelFormat_EAC_R11;
case AZ::RHI::Format::EAC_RG11_UNORM:
return ePixelFormat_EAC_RG11;
case AZ::RHI::Format::ETC2_UNORM_SRGB:
isSRGB = true;
case AZ::RHI::Format::ETC2_UNORM:
return ePixelFormat_ETC2;
case AZ::RHI::Format::ETC2A_UNORM_SRGB:
isSRGB = true;
case AZ::RHI::Format::ETC2A_UNORM:
return ePixelFormat_ETC2a;
case AZ::RHI::Format::ETC2A1_UNORM_SRGB:
isSRGB = true;
case AZ::RHI::Format::ETC2A1_UNORM:
return ePixelFormat_ETC2a1;
case AZ::RHI::Format::BC1_UNORM_SRGB:
isSRGB = true;
case AZ::RHI::Format::BC1_UNORM:
@@ -225,22 +199,6 @@ namespace ImageProcessingAtom
case ePixelFormat_ASTC_12x12:
return isSrgb ? RHI::Format::ASTC_12x12_UNORM_SRGB : RHI::Format::ASTC_12x12_UNORM;
case ePixelFormat_PVRTC2:
return isSrgb ? RHI::Format::PVRTC2_UNORM_SRGB : RHI::Format::PVRTC2_UNORM;
case ePixelFormat_PVRTC4:
return isSrgb ? RHI::Format::PVRTC4_UNORM_SRGB : RHI::Format::PVRTC4_UNORM;
case ePixelFormat_EAC_R11:
return RHI::Format::EAC_R11_UNORM;
case ePixelFormat_EAC_RG11:
return RHI::Format::EAC_RG11_UNORM;
case ePixelFormat_ETC2:
return isSrgb ? RHI::Format::ETC2_UNORM_SRGB : RHI::Format::ETC2_UNORM;
case ePixelFormat_ETC2a:
return isSrgb ? RHI::Format::ETC2A_UNORM_SRGB : RHI::Format::ETC2A_UNORM;
case ePixelFormat_ETC2a1:
return isSrgb ? RHI::Format::ETC2A1_UNORM_SRGB : RHI::Format::ETC2A1_UNORM;
case ePixelFormat_BC1:
case ePixelFormat_BC1a:
return isSrgb ? RHI::Format::BC1_UNORM_SRGB : RHI::Format::BC1_UNORM;
@@ -426,60 +426,6 @@ namespace UnitTest
return isDifferent;
}
bool CompareDDSImage(const QString& imagePath1, const QString& imagePath2, QString& output)
{
IImageObjectPtr image1, alphaImage1, image2, alphaImage2;
image1 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(imagePath1.toUtf8().constData()));
if (image1 && image1->HasImageFlags(EIF_AttachedAlpha))
{
if (image1->HasImageFlags(EIF_Splitted))
{
alphaImage1 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(QString(imagePath1 + ".a").toUtf8().constData()));
}
else
{
alphaImage1 = IImageObjectPtr(DdsLoader::LoadAttachedImageFromDdsFileLegacy(imagePath1.toUtf8().constData(), image1));
}
}
image2 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(imagePath2.toUtf8().constData()));
if (image2 && image2->HasImageFlags(EIF_AttachedAlpha))
{
if (image2->HasImageFlags(EIF_Splitted))
{
alphaImage2 = IImageObjectPtr(DdsLoader::LoadImageFromFileLegacy(QString(imagePath2 + ".a").toUtf8().constData()));
}
else
{
alphaImage2 = IImageObjectPtr(DdsLoader::LoadAttachedImageFromDdsFileLegacy(imagePath2.toUtf8().constData(), image2));
}
}
if (!image1 && !image2)
{
output += "Cannot load both image file! ";
return false;
}
bool isDifferent = false;
isDifferent = GetComparisonResult(image1, image2, output);
QFileInfo fi(imagePath1);
AZStd::string imageName = fi.baseName().toUtf8().constData();
SaveImageToFile(image1, imageName + "_new");
SaveImageToFile(image2, imageName + "_old");
if (alphaImage1 || alphaImage2)
{
isDifferent |= GetComparisonResult(alphaImage1, alphaImage2, output);
}
return isDifferent;
}
};
// test CPixelFormats related functions
@@ -487,34 +433,6 @@ namespace UnitTest
{
CPixelFormats& pixelFormats = CPixelFormats::GetInstance();
//verify names which was used for legacy rc.ini
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC7t") == ePixelFormat_BC7t);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("ETC2A") == ePixelFormat_ETC2a);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("PVRTC4") == ePixelFormat_PVRTC4);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC1") == ePixelFormat_BC1);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("ETC2") == ePixelFormat_ETC2);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC1a") == ePixelFormat_BC1a);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC3") == ePixelFormat_BC3);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC7") == ePixelFormat_BC7);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC5s") == ePixelFormat_BC5s);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("EAC_RG11") == ePixelFormat_EAC_RG11);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC4") == ePixelFormat_BC4);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("EAC_R11") == ePixelFormat_EAC_R11);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("A8R8G8B8") == ePixelFormat_R8G8B8A8);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("BC6UH") == ePixelFormat_BC6UH);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("R9G9B9E5") == ePixelFormat_R9G9B9E5);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("X8R8G8B8") == ePixelFormat_R8G8B8X8);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("A16B16G16R16F") == ePixelFormat_R16G16B16A16F);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G8R8") == ePixelFormat_R8G8);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G16R16") == ePixelFormat_R16G16);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("G16R16F") == ePixelFormat_R16G16F);
//some legacy format need to be mapping to new format.
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("DXT1") == ePixelFormat_BC1);
ASSERT_TRUE(pixelFormats.FindPixelFormatByLegacyName("DXT5") == ePixelFormat_BC3);
//calculate mipmap count. no cubemap support at this moment
//for all the non-compressed textures, if there minimum required texture size is 1x1
for (uint32 i = 0; i < ePixelFormat_Count; i++)
{
@@ -543,13 +461,6 @@ namespace UnitTest
}
//check function IsImageSizeValid && EvaluateImageDataSize function
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 2, 1, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 4, 4, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 16, 16, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 16, 32, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 34, 34, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_PVRTC4, 256, 256, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 2, 1, false) == false);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 16, 16, false) == true);
ASSERT_TRUE(pixelFormats.IsImageSizeValid(ePixelFormat_BC1, 16, 32, false) == true);
@@ -834,9 +745,7 @@ namespace UnitTest
if (formatInfo->bCompressed)
{
// skip ASTC formats which are tested in TestConvertASTCCompressor
if (!IsASTCFormat(pixelFormat)
&& pixelFormat != ePixelFormat_PVRTC2 && pixelFormat != ePixelFormat_PVRTC4
&& !IsETCFormat(pixelFormat)) // skip ETC since it's very slow
if (!IsASTCFormat(pixelFormat))
{
compressedFormats.push_back(pixelFormat);
}
@@ -1112,55 +1021,6 @@ namespace UnitTest
}
}
TEST_F(ImageProcessingTest, DISABLED_CompareOutputImage)
{
AZStd::string curretTextureFolder = "../TestAssets/TextureAssets/assets_new/textures";
AZStd::string oldTextureFolder = "../TestAssets/TextureAssets/assets_old/textures";
bool outputOnlyDifferent = false;
QDirIterator it(curretTextureFolder.c_str(), QStringList() << "*.dds", QDir::Files, QDirIterator::Subdirectories);
QFile f("../texture_comparison_output.csv");
f.open(QIODevice::ReadWrite | QIODevice::Truncate);
// Write a header for csv file
f.write("Texture Name, Path, Mip new/old, MipDiff, Format new/old, Flag new/old, MemSize new/old, MemDiff, Error, AlphaMip new/old, AlphaMipDiff, AlphaFormat new/old, AlphaFlag new/old, AlphaMemSize new/old, AlphaMemDiff, AlphaError\r\n");
int i = 0;
while (it.hasNext())
{
i++;
it.next();
QString fileName = it.fileName();
QString newFilePath = it.filePath();
QString sharedPath = QString(newFilePath).remove(curretTextureFolder.c_str());
QString oldFilePath = QString(oldTextureFolder.c_str()) + sharedPath;
QString output;
if (QFile::exists(oldFilePath))
{
bool isDifferent = CompareDDSImage(newFilePath, oldFilePath, output);
if (outputOnlyDifferent && !isDifferent)
{
continue;
}
else
{
f.write(fileName.toUtf8().constData());
f.write(",");
f.write(sharedPath.toUtf8().constData());
f.write(output.toUtf8().constData());
}
}
else
{
f.write(fileName.toUtf8().constData());
f.write(",");
f.write(sharedPath.toUtf8().constData());
output += ",No old file for comparison!";
f.write(output.toUtf8().constData());
}
f.write("\r\n");
}
f.close();
}
TEST_F(ImageProcessingTest, TextureSettingReflect_SerializingModernDataInAndOut_WritesAndParsesFileAccurately)
{
AZStd::string filepath = "test.xml";
@@ -120,10 +120,6 @@ set(FILES
Source/Compressors/Compressor.cpp
Source/Compressors/CTSquisher.h
Source/Compressors/CTSquisher.cpp
Source/Compressors/PVRTC.cpp
Source/Compressors/PVRTC.h
Source/Compressors/ETC2.cpp
Source/Compressors/ETC2.h
Source/Compressors/CryTextureSquisher/ColorBlockRGBA4x4f.cpp
Source/Compressors/CryTextureSquisher/ColorBlockRGBA4x4s.cpp
Source/Compressors/CryTextureSquisher/ColorBlockRGBA4x4c.cpp
@@ -86,6 +86,13 @@ namespace AZ
PhysicalDeviceDriverValidator::ValidationResult PhysicalDeviceDriverValidator::ValidateDriverVersion(const PhysicalDeviceDescriptor& descriptor) const
{
// [GFX TODO] Add driver info for other platforms besides Windows. Currently, avoid spamming warnings.
// ATOM-14967 [RHI][Metal] - Address driver version validator for Mac
if (m_driverInfo.size() == 0)
{
return ValidationResult::MissingInfo;
}
auto iter = m_driverInfo.find(descriptor.m_vendorId);
if (iter == m_driverInfo.end())
@@ -0,0 +1,23 @@
//
// 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
//
//
//
{
"O3DE":
{
"Atom":
{
"RHI":
{
"PhysicalDeviceDriverInfo":
{
}
}
}
}
}
@@ -0,0 +1,23 @@
//
// 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
//
//
//
{
"O3DE":
{
"Atom":
{
"RHI":
{
"PhysicalDeviceDriverInfo":
{
}
}
}
}
}
@@ -0,0 +1,23 @@
//
// 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
//
//
//
{
"O3DE":
{
"Atom":
{
"RHI":
{
"PhysicalDeviceDriverInfo":
{
}
}
}
}
}
@@ -0,0 +1,23 @@
//
// 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
//
//
//
{
"O3DE":
{
"Atom":
{
"RHI":
{
"PhysicalDeviceDriverInfo":
{
}
}
}
}
}
@@ -54,11 +54,21 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
INCLUDE_DIRECTORIES
PRIVATE
Source
Tools
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
Gem::EMotionFX_Atom.Static
Gem::EMotionFX.Editor.Static
Gem::AtomToolsFramework.Static
Gem::AtomToolsFramework.Editor
Gem::Atom_Component_DebugCamera.Static
Gem::Atom_Feature_Common.Static
Gem::AtomLyIntegration_CommonFeatures.Static
RUNTIME_DEPENDENCIES
Gem::EMotionFX.Editor
COMPILE_DEFINITIONS
PUBLIC
EMOTIONFXATOM_EDITOR
)
endif()
@@ -11,6 +11,9 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Module/Module.h>
#ifdef EMOTIONFXATOM_EDITOR
#include <Editor/EditorSystemComponent.h>
#endif
namespace AZ
{
@@ -28,7 +31,10 @@ namespace AZ
: Module()
{
m_descriptors.insert(m_descriptors.end(), {
ActorSystemComponent::CreateDescriptor()
ActorSystemComponent::CreateDescriptor(),
#ifdef EMOTIONFXATOM_EDITOR
EMotionFXAtom::EditorSystemComponent::CreateDescriptor(),
#endif
});
}
@@ -39,6 +45,9 @@ namespace AZ
{
return ComponentTypeList{
azrtti_typeid<ActorSystemComponent>(),
#ifdef EMOTIONFXATOM_EDITOR
azrtti_typeid<EMotionFXAtom::EditorSystemComponent>(),
#endif
};
}
};
@@ -0,0 +1,39 @@
/*
* 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 <Editor/EditorSystemComponent.h>
#include <EMStudio/AtomRenderPlugin.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
namespace AZ::EMotionFXAtom
{
void EditorSystemComponent::Reflect(ReflectContext* context)
{
if (SerializeContext* serialize = azrtti_cast<SerializeContext*>(context))
{
serialize->Class<EditorSystemComponent, Component>()->Version(0);
}
}
void EditorSystemComponent::Activate()
{
EMotionFX::Integration::SystemNotificationBus::Handler::BusConnect();
}
void EditorSystemComponent::Deactivate()
{
EMotionFX::Integration::SystemNotificationBus::Handler::BusDisconnect();
}
void EditorSystemComponent::OnRegisterPlugin()
{
EMStudio::PluginManager* pluginManager = EMStudio::EMStudioManager::GetInstance()->GetPluginManager();
pluginManager->RegisterPlugin(aznew EMStudio::AtomRenderPlugin());
}
} // namespace AZ::EMotionFXAtom
@@ -0,0 +1,32 @@
/*
* 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/Component/Component.h>
#include <Integration/AnimationBus.h>
namespace AZ::EMotionFXAtom
{
class EditorSystemComponent
: public Component
, private EMotionFX::Integration::SystemNotificationBus::Handler
{
public:
AZ_COMPONENT(EditorSystemComponent, "{1FAEC046-255D-4664-8F12-D16503C34431}");
static void Reflect(ReflectContext* context);
protected:
// AZ::Component overrides
void Activate() override;
void Deactivate() override;
// SystemNotificationBus::OnRegisterPlugin
void OnRegisterPlugin() override;
};
} // namespace AZ::EMotionFXAtom
@@ -0,0 +1,293 @@
/*
* 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 <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Integration/ActorComponentBus.h>
#include <Integration/Components/ActorComponent.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
#include <Atom/Feature/DisplayMapper/DisplayMapperFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
#include <Atom/Component/DebugCamera/CameraComponent.h>
#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Grid/GridComponentConfig.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/PostFxLayerComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.h>
#include <EMStudio/AnimViewportRenderer.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/CommandSystem/Source/CommandManager.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
namespace EMStudio
{
static constexpr float DepthNear = 0.01f;
AnimViewportRenderer::AnimViewportRenderer(AZ::RPI::ViewportContextPtr viewportContext)
: m_windowContext(viewportContext->GetWindowContext())
{
// Create a new entity context
m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
m_entityContext->InitContext();
// Create the scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "Unable to retrieve scene system.");
AZ::Outcome<AZStd::shared_ptr<AzFramework::Scene>, AZStd::string> createSceneOutcome = sceneSystem->CreateScene("AnimViewport");
AZ_Assert(createSceneOutcome, "%s", createSceneOutcome.GetError().data());
m_frameworkScene = createSceneOutcome.TakeValue();
m_frameworkScene->SetSubsystem<AzFramework::EntityContext::SceneStorageType>(m_entityContext.get());
// Create and register a scene with all available feature processors
AZ::RPI::SceneDescriptor sceneDesc;
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
m_scene->EnableAllFeatureProcessors();
// Link our RPI::Scene to the AzFramework::Scene
m_frameworkScene->SetSubsystem(m_scene);
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZStd::string defaultPipelineAssetPath = "passes/MainRenderPipeline.azasset";
AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(
defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *m_windowContext.get());
pipelineAsset.Release();
m_scene->AddRenderPipeline(m_renderPipeline);
m_renderPipeline->SetDefaultView(viewportContext->GetDefaultView());
// Currently the scene has to be activated after render pipeline was added so some feature processors (i.e. imgui) can be
// initialized properly with pipeline's pass information.
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
AzFramework::EntityContextId entityContextId = m_entityContext->GetContextId();
// Get the FeatureProcessors
m_meshFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::MeshFeatureProcessorInterface>();
// Configure tone mapper
AzFramework::EntityContextRequestBus::EventResult(
m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity");
AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity.");
m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId);
m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId);
m_postProcessEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_postProcessEntity->Activate();
// Init directional light processor
m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DirectionalLightFeatureProcessorInterface>();
// Init display mapper processor
m_displayMapperFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::DisplayMapperFeatureProcessorInterface>();
// Init Skybox
m_skyboxFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
m_skyboxFeatureProcessor->Enable(true);
m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap);
// Create IBL
AzFramework::EntityContextRequestBus::EventResult(
m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity");
AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity.");
m_iblEntity->CreateComponent(AZ::Render::ImageBasedLightComponentTypeId);
m_iblEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_iblEntity->Activate();
// Load light preset
AZ::Data::Asset<AZ::RPI::AnyAsset> lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(
"lightingpresets/default.lightingpreset.azasset", AZ::RPI::AssetUtils::TraceLevel::Warning);
const AZ::Render::LightingPreset* preset = lightingPresetAsset->GetDataAs<AZ::Render::LightingPreset>();
SetLightingPreset(preset);
// Create grid
AzFramework::EntityContextRequestBus::EventResult(
m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid");
AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity.");
AZ::Render::GridComponentConfig gridConfig;
gridConfig.m_gridSize = 4.0f;
gridConfig.m_axisColor = AZ::Color(0.5f, 0.5f, 0.5f, 1.0f);
gridConfig.m_primaryColor = AZ::Color(0.3f, 0.3f, 0.3f, 1.0f);
gridConfig.m_secondaryColor = AZ::Color(0.5f, 0.1f, 0.1f, 1.0f);
auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId);
gridComponent->SetConfiguration(gridConfig);
m_gridEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_gridEntity->Activate();
Reinit();
}
AnimViewportRenderer::~AnimViewportRenderer()
{
// Destroy all the entity we created.
m_entityContext->DestroyEntity(m_iblEntity);
m_entityContext->DestroyEntity(m_postProcessEntity);
m_entityContext->DestroyEntity(m_gridEntity);
for (AZ::Entity* entity : m_actorEntities)
{
m_entityContext->DestroyEntity(entity);
}
m_actorEntities.clear();
m_entityContext->DestroyContext();
for (AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle& handle : m_lightHandles)
{
m_directionalLightFeatureProcessor->ReleaseLight(handle);
}
m_lightHandles.clear();
m_frameworkScene->UnsetSubsystem(m_scene);
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "AtomViewportRenderer was unable to get the scene system during destruction.");
bool removeSuccess = sceneSystem->RemoveScene("AnimViewport");
if (!removeSuccess)
{
AZ_Assert(false, "AtomViewportRenderer should be removed.");
}
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_scene = nullptr;
}
void AnimViewportRenderer::Reinit()
{
ReinitActorEntities();
ResetEnvironment();
}
void AnimViewportRenderer::ResetEnvironment()
{
// Reset environment
AZ::Transform iblTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::Event(m_iblEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, iblTransform);
const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity();
AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix);
}
void AnimViewportRenderer::ReinitActorEntities()
{
// 1. Destroy all the entities that do not point to any actorAsset anymore.
AZStd::set<AZ::Data::AssetId> assetLookup;
AzFramework::EntityContext* entityContext = m_entityContext.get();
const size_t numActors = EMotionFX::GetActorManager().GetNumActors();
for (size_t i = 0; i < numActors; ++i)
{
assetLookup.emplace(EMotionFX::GetActorManager().GetActorAsset(i).GetId());
}
m_actorEntities.erase(
AZStd::remove_if(
m_actorEntities.begin(), m_actorEntities.end(),
[&assetLookup, entityContext](AZ::Entity* entity)
{
EMotionFX::Integration::ActorComponent* actorComponent =
entity->FindComponent<EMotionFX::Integration::ActorComponent>();
if (assetLookup.find(actorComponent->GetActorAsset().GetId()) == assetLookup.end())
{
entityContext->DestroyEntity(entity);
return true;
}
return false;
}),
m_actorEntities.end());
// 2. Create an entity for every actorAsset stored in actor manager.
for (size_t i = 0; i < numActors; ++i)
{
AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset = EMotionFX::GetActorManager().GetActorAsset(i);
if (!actorAsset->IsReady())
{
continue;
}
AZ::Entity* entity = FindActorEntity(actorAsset);
if (!entity)
{
m_actorEntities.emplace_back(CreateActorEntity(actorAsset));
}
}
}
AZ::Entity* AnimViewportRenderer::FindActorEntity(AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset) const
{
const auto foundEntity = AZStd::find_if(
begin(m_actorEntities), end(m_actorEntities),
[match = actorAsset](const AZ::Entity* entity)
{
EMotionFX::Integration::ActorComponent* actorComponent = entity->FindComponent<EMotionFX::Integration::ActorComponent>();
return actorComponent->GetActorAsset() == match;
});
return foundEntity != end(m_actorEntities) ? (*foundEntity) : nullptr;
}
AZ::Entity* AnimViewportRenderer::CreateActorEntity(AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset)
{
AZ::Entity* actorEntity = m_entityContext->CreateEntity(actorAsset->GetActor()->GetName());
actorEntity->CreateComponent(azrtti_typeid<EMotionFX::Integration::ActorComponent>());
actorEntity->CreateComponent(AZ::Render::MaterialComponentTypeId);
actorEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
actorEntity->Activate();
EMotionFX::Integration::ActorComponent* actorComponent = actorEntity->FindComponent<EMotionFX::Integration::ActorComponent>();
actorComponent->SetActorAsset(actorAsset);
// Since this entity belongs to the animation editor, we need to set the isOwnByRuntime flag to false.
actorComponent->GetActorInstance()->SetIsOwnedByRuntime(false);
// Selet the actor instance in the command manager after it has been created.
AZStd::string outResult;
EMStudioManager::GetInstance()->GetCommandManager()->ExecuteCommandInsideCommand(
AZStd::string::format("Select -actorInstanceID %i", actorComponent->GetActorInstance()->GetID()).c_str(), outResult);
return actorEntity;
}
void AnimViewportRenderer::SetLightingPreset(const AZ::Render::LightingPreset* preset)
{
if (!preset)
{
AZ_Warning("AnimViewportRenderer", false, "Attempting to set invalid lighting preset.");
return;
}
AZ::Render::ImageBasedLightFeatureProcessorInterface* iblFeatureProcessor =
m_scene->GetFeatureProcessor<AZ::Render::ImageBasedLightFeatureProcessorInterface>();
AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor =
m_scene->GetFeatureProcessor<AZ::Render::PostProcessFeatureProcessorInterface>();
AZ::Render::ExposureControlSettingsInterface* exposureControlSettingInterface =
postProcessFeatureProcessor->GetOrCreateSettingsInterface(m_postProcessEntity->GetId())
->GetOrCreateExposureControlSettingsInterface();
Camera::Configuration cameraConfig;
cameraConfig.m_fovRadians = AZ::Constants::HalfPi;
cameraConfig.m_nearClipDistance = DepthNear;
preset->ApplyLightingPreset(
iblFeatureProcessor, m_skyboxFeatureProcessor, exposureControlSettingInterface, m_directionalLightFeatureProcessor,
cameraConfig, m_lightHandles, nullptr, AZ::RPI::MaterialPropertyIndex::Null, false);
}
} // namespace EMStudio
@@ -0,0 +1,88 @@
/*
* 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/Component/TickBus.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/Scene/Scene.h>
#include <Integration/Assets/ActorAsset.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
namespace AZ
{
class Entity;
class Component;
namespace Render
{
class DisplayMapperFeatureProcessorInterface;
class DirectionalLightFeatureProcessorInterface;
class MeshFeatureProcessorInterface;
}
namespace RPI
{
class WindowContext;
}
}
namespace EMStudio
{
class AnimViewportRenderer
{
public:
AZ_CLASS_ALLOCATOR(AnimViewportRenderer, AZ::SystemAllocator, 0);
AnimViewportRenderer(AZ::RPI::ViewportContextPtr viewportContext);
~AnimViewportRenderer();
void Reinit();
private:
// This function resets the light, camera and other environment settings.
void ResetEnvironment();
// This function creates in-editor entities for all actor assets stored in the actor manager,
// and deletes all the actor entities that no longer has an actor asset in the actor manager.
// Those entities are used in atom render viewport to visualize actors in animation editor.
void ReinitActorEntities();
AZ::Entity* CreateActorEntity(AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset);
AZ::Entity* FindActorEntity(AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset) const;
void SetLightingPreset(const AZ::Render::LightingPreset* preset);
AZStd::shared_ptr<AZ::RPI::WindowContext> m_windowContext;
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
AZStd::shared_ptr<AzFramework::Scene> m_frameworkScene;
AZ::RPI::ScenePtr m_scene;
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = nullptr;
AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = nullptr;
AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
AZ::Entity* m_postProcessEntity = nullptr;
AZ::Entity* m_iblEntity = nullptr;
AZ::Entity* m_cameraEntity = nullptr;
AZ::Component* m_cameraComponent = nullptr;
AZ::Entity* m_modelEntity = nullptr;
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = nullptr;
AZStd::vector<AZ::Entity*> m_actorEntities;
AZStd::vector<AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle> m_lightHandles;
};
}
@@ -0,0 +1,71 @@
/*
* 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 <EMStudio/AnimViewportSettings.h>
namespace EMStudio::ViewportUtil
{
constexpr AZStd::string_view CameraRotateSmoothnessSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothness";
constexpr AZStd::string_view CameraTranslateSmoothnessSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothness";
constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing";
constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing";
constexpr AZStd::string_view CameraOrbitLookIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitLookId";
constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId";
constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId";
constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId";
constexpr AZStd::string_view CameraTranslateRightIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateRightId";
constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId";
constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId";
constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId";
AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds()
{
AzFramework::TranslateCameraInputChannelIds translateCameraInputChannelIds;
translateCameraInputChannelIds.m_leftChannelId =
AzFramework::InputChannelId(GetRegistry(CameraTranslateLeftIdSetting, AZStd::string("keyboard_key_alphanumeric_A")).c_str());
translateCameraInputChannelIds.m_rightChannelId =
AzFramework::InputChannelId(GetRegistry(CameraTranslateRightIdSetting, AZStd::string("keyboard_key_alphanumeric_D")).c_str());
translateCameraInputChannelIds.m_forwardChannelId =
AzFramework::InputChannelId(GetRegistry(CameraTranslateForwardIdSetting, AZStd::string("keyboard_key_alphanumeric_W")).c_str());
translateCameraInputChannelIds.m_backwardChannelId = AzFramework::InputChannelId(
GetRegistry(CameraTranslateBackwardIdSetting, AZStd::string("keyboard_key_alphanumeric_S")).c_str());
translateCameraInputChannelIds.m_upChannelId =
AzFramework::InputChannelId(GetRegistry(CameraTranslateUpIdSetting, AZStd::string("keyboard_key_alphanumeric_E")).c_str());
translateCameraInputChannelIds.m_downChannelId =
AzFramework::InputChannelId(GetRegistry(CameraTranslateDownIdSetting, AZStd::string("keyboard_key_alphanumeric_Q")).c_str());
translateCameraInputChannelIds.m_boostChannelId =
AzFramework::InputChannelId(GetRegistry(CameraTranslateBoostIdSetting, AZStd::string("keyboard_key_modifier_shift_l")).c_str());
return translateCameraInputChannelIds;
}
float CameraRotateSmoothness()
{
return aznumeric_cast<float>(GetRegistry(CameraRotateSmoothnessSetting, 5.0));
}
float CameraTranslateSmoothness()
{
return aznumeric_cast<float>(GetRegistry(CameraTranslateSmoothnessSetting, 5.0));
}
bool CameraRotateSmoothingEnabled()
{
return GetRegistry(CameraRotateSmoothingSetting, true);
}
bool CameraTranslateSmoothingEnabled()
{
return GetRegistry(CameraTranslateSmoothingSetting, true);
}
AzFramework::InputChannelId BuildRotateCameraInputId()
{
return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
}
}
@@ -0,0 +1,37 @@
/*
* 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/Settings/SettingsRegistry.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
namespace EMStudio::ViewportUtil
{
template<typename T>
AZStd::remove_cvref_t<T> GetRegistry(const AZStd::string_view setting, T&& defaultValue)
{
AZStd::remove_cvref_t<T> value = AZStd::forward<T>(defaultValue);
if (const auto* registry = AZ::SettingsRegistry::Get())
{
T potentialValue;
if (registry->Get(potentialValue, setting))
{
value = AZStd::move(potentialValue);
}
}
return value;
}
float CameraRotateSmoothness();
float CameraTranslateSmoothness();
bool CameraRotateSmoothingEnabled();
bool CameraTranslateSmoothingEnabled();
AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds();
AzFramework::InputChannelId BuildRotateCameraInputId();
}
@@ -0,0 +1,103 @@
/*
* 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 <AzCore/Settings/SettingsRegistry.h>
#include <AzFramework/Viewport/ViewportControllerList.h>
#include <AzFramework/Viewport/CameraInput.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
#include <EMStudio/AnimViewportWidget.h>
#include <EMStudio/AnimViewportRenderer.h>
#include <EMStudio/AnimViewportSettings.h>
namespace EMStudio
{
AnimViewportWidget::AnimViewportWidget(QWidget* parent)
: AtomToolsFramework::RenderViewportWidget(parent)
{
setObjectName(QString::fromUtf8("AtomViewportWidget"));
QSizePolicy qSize(QSizePolicy::Preferred, QSizePolicy::Preferred);
qSize.setHorizontalStretch(0);
qSize.setVerticalStretch(0);
qSize.setHeightForWidth(sizePolicy().hasHeightForWidth());
setSizePolicy(qSize);
setAutoFillBackground(false);
setStyleSheet(QString::fromUtf8(""));
m_renderer = AZStd::make_unique<AnimViewportRenderer>(GetViewportContext());
SetupCameras();
SetupCameraController();
}
void AnimViewportWidget::SetupCameras()
{
m_rotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(EMStudio::ViewportUtil::BuildRotateCameraInputId());
const auto translateCameraInputChannelIds = EMStudio::ViewportUtil::BuildTranslateCameraInputChannelIds();
m_translateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook);
m_translateCamera.get()->m_translateSpeedFn = []
{
return 3.0f;
};
m_orbitDollyScrollCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
}
void AnimViewportWidget::SetupCameraController()
{
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
controller->SetCameraViewportContextBuilderCallback(
[viewportId =
GetViewportContext()->GetId()](AZStd::unique_ptr<AtomToolsFramework::ModularCameraViewportContext>& cameraViewportContext)
{
cameraViewportContext = AZStd::make_unique<AtomToolsFramework::ModularCameraViewportContextImpl>(viewportId);
});
controller->SetCameraPriorityBuilderCallback(
[](AtomToolsFramework::CameraControllerPriorityFn& cameraControllerPriorityFn)
{
cameraControllerPriorityFn = AtomToolsFramework::DefaultCameraControllerPriority;
});
controller->SetCameraPropsBuilderCallback(
[](AzFramework::CameraProps& cameraProps)
{
cameraProps.m_rotateSmoothnessFn = []
{
return EMStudio::ViewportUtil::CameraRotateSmoothness();
};
cameraProps.m_translateSmoothnessFn = []
{
return EMStudio::ViewportUtil::CameraTranslateSmoothness();
};
cameraProps.m_rotateSmoothingEnabledFn = []
{
return EMStudio::ViewportUtil::CameraRotateSmoothingEnabled();
};
cameraProps.m_translateSmoothingEnabledFn = []
{
return EMStudio::ViewportUtil::CameraTranslateSmoothingEnabled();
};
});
controller->SetCameraListBuilderCallback(
[this](AzFramework::Cameras& cameras)
{
cameras.AddCamera(m_rotateCamera);
cameras.AddCamera(m_translateCamera);
cameras.AddCamera(m_orbitDollyScrollCamera);
});
GetControllerList()->Add(controller);
}
} // namespace EMStudio
@@ -0,0 +1,33 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AzFramework/Viewport/CameraInput.h>
namespace EMStudio
{
class AnimViewportRenderer;
class AnimViewportWidget
: public AtomToolsFramework::RenderViewportWidget
{
public:
AnimViewportWidget(QWidget* parent = nullptr);
AnimViewportRenderer* GetAnimViewportRenderer() { return m_renderer.get(); }
private:
void SetupCameras();
void SetupCameraController();
AZStd::unique_ptr<AnimViewportRenderer> m_renderer;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_rotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_translateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
};
}
@@ -0,0 +1,140 @@
/*
* 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 <EMStudio/AtomRenderPlugin.h>
#include <EMStudio/AnimViewportRenderer.h>
#include <Integration/Components/ActorComponent.h>
#include <EMotionFX/CommandSystem/Source/CommandManager.h>
#include <EMotionFX/Source/Allocators.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#include <QHBoxLayout>
namespace EMStudio
{
AZ_CLASS_ALLOCATOR_IMPL(AtomRenderPlugin, EMotionFX::EditorAllocator, 0);
AtomRenderPlugin::AtomRenderPlugin()
: DockWidgetPlugin()
{
}
AtomRenderPlugin::~AtomRenderPlugin()
{
}
const char* AtomRenderPlugin::GetName() const
{
return "Atom Render Window (Preview)";
}
uint32 AtomRenderPlugin::GetClassID() const
{
return static_cast<uint32>(AtomRenderPlugin::CLASS_ID);
}
const char* AtomRenderPlugin::GetCreatorName() const
{
return "O3DE";
}
float AtomRenderPlugin::GetVersion() const
{
return 1.0f;
}
bool AtomRenderPlugin::GetIsClosable() const
{
return true;
}
bool AtomRenderPlugin::GetIsFloatable() const
{
return true;
}
bool AtomRenderPlugin::GetIsVertical() const
{
return false;
}
EMStudioPlugin* AtomRenderPlugin::Clone()
{
return new AtomRenderPlugin();
}
EMStudioPlugin::EPluginType AtomRenderPlugin::GetPluginType() const
{
return EMStudioPlugin::PLUGINTYPE_RENDERING;
}
void AtomRenderPlugin::ReinitRenderer()
{
m_animViewportWidget->GetAnimViewportRenderer()->Reinit();
}
bool AtomRenderPlugin::Init()
{
m_innerWidget = new QWidget();
m_dock->setWidget(m_innerWidget);
QVBoxLayout* verticalLayout = new QVBoxLayout(m_innerWidget);
verticalLayout->setSizeConstraint(QLayout::SetNoConstraint);
verticalLayout->setSpacing(1);
verticalLayout->setMargin(0);
m_animViewportWidget = new AnimViewportWidget(m_innerWidget);
verticalLayout->addWidget(m_animViewportWidget);
// Register command callbacks.
m_importActorCallback = new ImportActorCallback(false);
m_removeActorCallback = new RemoveActorCallback(false);
EMStudioManager::GetInstance()->GetCommandManager()->RegisterCommandCallback("ImportActor", m_importActorCallback);
EMStudioManager::GetInstance()->GetCommandManager()->RegisterCommandCallback("RemoveActor", m_removeActorCallback);
return true;
}
// Command callbacks
bool ReinitAtomRenderPlugin()
{
EMStudioPlugin* plugin = EMStudio::GetPluginManager()->FindActivePlugin(static_cast<uint32>(AtomRenderPlugin::CLASS_ID));
if (!plugin)
{
AZ_Error("AtomRenderPlugin", false, "Cannot execute command callback. Atom render plugin does not exist.");
return false;
}
AtomRenderPlugin* atomRenderPlugin = static_cast<AtomRenderPlugin*>(plugin);
atomRenderPlugin->ReinitRenderer();
return true;
}
bool AtomRenderPlugin::ImportActorCallback::Execute(
[[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine)
{
return ReinitAtomRenderPlugin();
}
bool AtomRenderPlugin::ImportActorCallback::Undo(
[[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine)
{
return ReinitAtomRenderPlugin();
}
bool AtomRenderPlugin::RemoveActorCallback::Execute(
[[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine)
{
return ReinitAtomRenderPlugin();
}
bool AtomRenderPlugin::RemoveActorCallback::Undo(
[[maybe_unused]] MCore::Command* command, [[maybe_unused]] const MCore::CommandLine& commandLine)
{
return ReinitAtomRenderPlugin();
}
}// namespace EMStudio
@@ -0,0 +1,61 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <MCore/Source/Command.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/DockWidgetPlugin.h>
#include <EMStudio/AnimViewportWidget.h>
#include <QWidget>
#endif
namespace AZ
{
class Entity;
}
namespace EMStudio
{
class AtomRenderPlugin
: public DockWidgetPlugin
{
public:
AZ_CLASS_ALLOCATOR_DECL
enum
{
CLASS_ID = 0x32b0c04d
};
AtomRenderPlugin();
~AtomRenderPlugin();
// Plugin information
const char* GetName() const override;
uint32 GetClassID() const override;
const char* GetCreatorName() const override;
float GetVersion() const override;
bool GetIsClosable() const override;
bool GetIsFloatable() const override;
bool GetIsVertical() const override;
bool Init() override;
EMStudioPlugin* Clone();
EMStudioPlugin::EPluginType GetPluginType() const override;
void ReinitRenderer();
private:
MCORE_DEFINECOMMANDCALLBACK(ImportActorCallback);
MCORE_DEFINECOMMANDCALLBACK(RemoveActorCallback);
ImportActorCallback* m_importActorCallback = nullptr;
RemoveActorCallback* m_removeActorCallback = nullptr;
QWidget* m_innerWidget = nullptr;
AnimViewportWidget* m_animViewportWidget = nullptr;
};
}// namespace EMStudio
@@ -8,4 +8,14 @@
set(FILES
Source/ActorModule.cpp
Source/Editor/EditorSystemComponent.h
Source/Editor/EditorSystemComponent.cpp
Tools/EMStudio/AtomRenderPlugin.h
Tools/EMStudio/AtomRenderPlugin.cpp
Tools/EMStudio/AnimViewportWidget.h
Tools/EMStudio/AnimViewportWidget.cpp
Tools/EMStudio/AnimViewportRenderer.h
Tools/EMStudio/AnimViewportRenderer.cpp
Tools/EMStudio/AnimViewportSettings.h
Tools/EMStudio/AnimViewportSettings.cpp
)
+2
View File
@@ -109,6 +109,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AzToolsFramework
Legacy::Editor.Headers
3rdParty::OpenGLInterface
Gem::AtomToolsFramework.Static
Gem::AtomToolsFramework.Editor
COMPILE_DEFINITIONS
PUBLIC
EMFX_EMSTUDIOLYEMBEDDED
@@ -22,6 +22,7 @@
#include "CommandManager.h"
#include <EMotionFX/Source/EMotionFXManager.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <Source/Integration/Assets/ActorAsset.h>
namespace CommandSystem
@@ -729,7 +730,8 @@ namespace CommandSystem
m_oldWorkspaceDirtyFlag = GetCommandManager()->GetWorkspaceDirtyFlag();
// get rid of the actor
EMotionFX::GetActorManager().UnregisterActor(EMotionFX::GetActorManager().FindSharedActorByID(actor->GetID()));
const AZ::Data::AssetId actorAssetId = EMotionFX::GetActorManager().FindAssetIdByActorId(actor->GetID());
EMotionFX::GetActorManager().UnregisterActor(actorAssetId);
// mark the workspace as dirty
GetCommandManager()->SetWorkspaceDirtyFlag(true);
@@ -818,7 +820,6 @@ namespace CommandSystem
{
continue;
}
// ignore visualization actor instances
if (actorInstance->GetIsUsedForVisualization())
{
@@ -849,12 +850,6 @@ namespace CommandSystem
// get the current actor
EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i);
// ignore runtime-owned actors
if (actor->GetIsOwnedByRuntime())
{
continue;
}
// ignore visualization actors
if (actor->GetIsUsedForVisualization())
{
@@ -17,6 +17,7 @@
#include <EMotionFX/Exporters/ExporterLib/Exporter/Exporter.h>
#include "CommandManager.h"
#include <AzFramework/API/ApplicationAPI.h>
#include <Source/Integration/Assets/ActorAsset.h>
namespace CommandSystem
@@ -65,35 +66,29 @@ namespace CommandSystem
filename = EMotionFX::EMotionFXManager::ResolvePath(filename.c_str());
}
// check if we have already loaded the actor
EMotionFX::Actor* actorFromManager = EMotionFX::GetActorManager().FindActorByFileName(filename.c_str());
if (actorFromManager)
AZ::Data::AssetId actorAssetId;
EBUS_EVENT_RESULT(
actorAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, filename.c_str(), AZ::Data::s_invalidAssetType, false);
if (!actorAssetId.IsValid())
{
AZStd::to_string(outResult, actorFromManager->GetID());
return true;
}
// init the settings
EMotionFX::Importer::ActorSettings settings;
// extract default values from the command syntax automatically, if they aren't specified explicitly
settings.m_loadLimits = parameters.GetValueAsBool("loadLimits", this);
settings.m_loadMorphTargets = parameters.GetValueAsBool("loadMorphTargets", this);
settings.m_loadSkeletalLoDs = parameters.GetValueAsBool("loadSkeletalLODs", this);
settings.m_dualQuatSkinning = parameters.GetValueAsBool("dualQuatSkinning", this);
// try to load the actor
AZStd::shared_ptr<EMotionFX::Actor> actor {EMotionFX::GetImporter().LoadActor(filename.c_str(), &settings)};
if (!actor)
{
outResult = AZStd::string::format("Failed to load actor from '%s'. File may not exist at this path or may have incorrect permissions", filename.c_str());
outResult = AZStd::string::format("Cannot import actor. Cannot find asset at path %s.", filename.c_str());
return false;
}
// Because the actor is directly loaded from disk (without going through an actor asset), we need to ask for a blocking
// load for the asset that actor is depend on.
actor->Finalize(EMotionFX::Actor::LoadRequirement::RequireBlockingLoad);
// check if we have already loaded the actor
const size_t actorIndex = EMotionFX::GetActorManager().FindActorIndex(actorAssetId);
if (actorIndex != InvalidIndex)
{
return true;
}
// Do a blocking load of the asset.
AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset =
AZ::Data::AssetManager::Instance().GetAsset<EMotionFX::Integration::ActorAsset>(
actorAssetId, AZ::Data::AssetLoadBehavior::Default);
actorAsset.BlockUntilLoadComplete();
EMotionFX::Actor* actor = actorAsset->GetActor();
// set the actor id in case we have specified it as parameter
if (actorID != MCORE_INVALIDINDEX32)
{
@@ -113,7 +108,6 @@ namespace CommandSystem
GetCommandManager()->ExecuteCommandInsideCommand(AZStd::string::format("Select -actorID %i", actor->GetID()).c_str(), outResult);
}
// mark the workspace as dirty
m_oldWorkspaceDirtyFlag = GetCommandManager()->GetWorkspaceDirtyFlag();
GetCommandManager()->SetWorkspaceDirtyFlag(true);
@@ -121,7 +115,8 @@ namespace CommandSystem
// return the id of the newly created actor
AZStd::to_string(outResult, actor->GetID());
EMotionFX::GetActorManager().RegisterActor(AZStd::move(actor));
// Register actor asset.
EMotionFX::GetActorManager().RegisterActor(AZStd::move(actorAsset));
return true;
}
@@ -145,14 +140,14 @@ namespace CommandSystem
}
// find the actor based on the given id
AZStd::shared_ptr<EMotionFX::Actor> actor = EMotionFX::GetActorManager().FindSharedActorByID(actorID);
if (actor == nullptr)
AZ::Data::AssetId actorAssetId = EMotionFX::GetActorManager().FindAssetIdByActorId(actorID);
if (!actorAssetId.IsValid())
{
outResult = AZStd::string::format("Cannot remove actor. Actor ID %i is not valid.", actorID);
return false;
}
EMotionFX::GetActorManager().UnregisterActor(actor);
EMotionFX::GetActorManager().UnregisterActor(actorAssetId);
// update our render actors
AZStd::string updateRenderActorsResult;
@@ -183,11 +183,6 @@ namespace CommandSystem
for (size_t i = 0; i < numActors; ++i)
{
EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i);
if (actor->GetIsOwnedByRuntime())
{
continue;
}
if (unselect == false)
{
@@ -211,11 +206,6 @@ namespace CommandSystem
return false;
}
if (actor->GetIsOwnedByRuntime())
{
return false;
}
if (unselect == false)
{
selection.AddActor(actor);
@@ -244,11 +234,6 @@ namespace CommandSystem
{
EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i);
if (actor->GetIsOwnedByRuntime())
{
continue;
}
if (AzFramework::StringFunc::Equal(valueString.c_str(), actor->GetName(), false /* no case */))
{
if (unselect == false)
@@ -8,7 +8,6 @@
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <EMotionFX/Source/Importer/Importer.h>
#include <EMotionFX/CommandSystem/Source/MetaData.h>
#include <EMotionFX/Exporters/ExporterLib/Exporter/Exporter.h>
@@ -8,7 +8,6 @@
#pragma once
#include <AzCore/std/optional.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <Integration/System/SystemCommon.h>
@@ -44,7 +43,7 @@ namespace EMotionFX
static AZStd::optional<AZ::SceneAPI::Events::ExportProduct> GetFirstProductByType(
const ActorGroupExportContext& context, AZ::Data::AssetType type);
AutoRegisteredActor m_actor;
AZStd::shared_ptr<Actor> m_actor;
AZStd::vector<AZStd::string> m_actorMaterialReferences;
};
} // namespace Pipeline
@@ -91,9 +91,6 @@ namespace EMotionFX
m_simulatedObjectSetup = AZStd::make_shared<SimulatedObjectSetup>(this);
m_optimizeSkeleton = false;
#if defined(EMFX_DEVELOPMENT_BUILD)
m_isOwnedByRuntime = false;
#endif // EMFX_DEVELOPMENT_BUILD
// make sure we have at least allocated the first LOD of materials and facial setups
m_materials.reserve(4); // reserve space for 4 lods
@@ -2074,25 +2071,6 @@ namespace EMotionFX
return m_usedForVisualization;
}
void Actor::SetIsOwnedByRuntime(bool isOwnedByRuntime)
{
#if defined(EMFX_DEVELOPMENT_BUILD)
m_isOwnedByRuntime = isOwnedByRuntime;
#else
AZ_UNUSED(isOwnedByRuntime);
#endif
}
bool Actor::GetIsOwnedByRuntime() const
{
#if defined(EMFX_DEVELOPMENT_BUILD)
return m_isOwnedByRuntime;
#else
return true;
#endif
}
const AZ::Aabb& Actor::GetStaticAabb() const
{
return m_staticAabb;
@@ -720,12 +720,6 @@ namespace EMotionFX
void SetIsUsedForVisualization(bool flag);
bool GetIsUsedForVisualization() const;
/**
* Marks the actor as used by the engine runtime, as opposed to the tool suite.
*/
void SetIsOwnedByRuntime(bool isOwnedByRuntime);
bool GetIsOwnedByRuntime() const;
/**
* Recursively find the parent bone that is enabled in a given LOD, starting from a given node.
* For example if you have a finger bone, while the finger bones are disabled in the skeletal LOD, this function will return the index to the hand bone.
@@ -940,8 +934,5 @@ namespace EMotionFX
bool m_usedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */
bool m_optimizeSkeleton; /**< Indicates if we should perform/ */
bool m_isReady = false; /**< If actor as well as its dependent files are fully loaded and initialized.*/
#if defined(EMFX_DEVELOPMENT_BUILD)
bool m_isOwnedByRuntime; /**< Set if the actor is used/owned by the engine runtime. */
#endif // EMFX_DEVELOPMENT_BUILD
};
} // namespace EMotionFX
@@ -31,7 +31,6 @@ namespace EMotionFX
SetScheduler(MultiThreadScheduler::Create());
// reserve memory
m_actors.reserve(512);
m_actorInstances.reserve(1024);
m_rootActorInstances.reserve(1024);
}
@@ -111,20 +110,23 @@ namespace EMotionFX
// register the actor
void ActorManager::RegisterActor(AZStd::shared_ptr<Actor> actor)
void ActorManager::RegisterActor(ActorAssetData actorAsset)
{
LockActors();
// check if we already registered
if (FindActorIndex(actor.get()) != InvalidIndex)
if (FindActorIndex(actorAsset.GetId()) != InvalidIndex)
{
MCore::LogWarning("EMotionFX::ActorManager::RegisterActor() - The actor at location 0x%x has already been registered as actor, most likely already by the LoadActor of the importer.", actor.get());
MCore::LogWarning(
"EMotionFX::ActorManager::RegisterActor() - The actor %s has already been registered as actor, most likely "
"already by the LoadActor of the importer.",
actorAsset->GetActor()->GetName());
UnlockActors();
return;
}
// register it
m_actors.emplace_back(AZStd::move(actor));
m_actorAssets.emplace_back(AZStd::move(actorAsset));
UnlockActors();
}
@@ -146,60 +148,82 @@ namespace EMotionFX
Actor* ActorManager::FindActorByName(const char* actorName) const
{
// get the number of actors and iterate through them
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [actorName](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[actorName](const ActorAssetData& a)
{
return a->GetNameString() == actorName;
return a->GetActor()->GetNameString() == actorName;
});
return (found != m_actors.end()) ? found->get() : nullptr;
return (found != m_actorAssets.end()) ? (*found)->GetActor() : nullptr;
}
// find the actor for a given filename
Actor* ActorManager::FindActorByFileName(const char* fileName) const
{
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [fileName](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[fileName](const ActorAssetData& a)
{
return AzFramework::StringFunc::Equal(a->GetFileNameString().c_str(), fileName, false /* no case */);
return AzFramework::StringFunc::Equal(
a->GetActor()->GetFileNameString().c_str(), fileName, false /* no case */);
});
return (found != m_actors.end()) ? found->get() : nullptr;
return (found != m_actorAssets.end()) ? (*found)->GetActor() : nullptr;
}
// find the leader actor record for a given actor
size_t ActorManager::FindActorIndex(Actor* actor) const
size_t ActorManager::FindActorIndex(AZ::Data::AssetId assetId) const
{
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [actor](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[assetId](const ActorAssetData& a)
{
return a.get() == actor;
return a.GetId() == assetId;
});
return (found != m_actors.end()) ? AZStd::distance(m_actors.begin(), found) : InvalidIndex;
return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex;
}
size_t ActorManager::FindActorIndex(const Actor* actor) const
{
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[actor](const ActorAssetData& a)
{
return a->GetActor() == actor;
});
return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex;
}
// find the actor for a given actor name
size_t ActorManager::FindActorIndexByName(const char* actorName) const
{
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [actorName](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[actorName](const ActorAssetData& a)
{
return a->GetNameString() == actorName;
return a->GetActor()->GetNameString() == actorName;
});
return (found != m_actors.end()) ? AZStd::distance(m_actors.begin(), found) : InvalidIndex;
return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex;
}
// find the actor for a given actor filename
size_t ActorManager::FindActorIndexByFileName(const char* filename) const
{
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [filename](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[filename](const ActorAssetData& a)
{
return a->GetFileNameString() == filename;
return a->GetActor()->GetFileNameString() == filename;
});
return (found != m_actors.end()) ? AZStd::distance(m_actors.begin(), found) : InvalidIndex;
return (found != m_actorAssets.end()) ? AZStd::distance(m_actorAssets.begin(), found) : InvalidIndex;
}
@@ -237,22 +261,26 @@ namespace EMotionFX
// find the actor by the identification number
Actor* ActorManager::FindActorByID(uint32 id) const
{
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [id](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[id](const ActorAssetData& a)
{
return a->GetID() == id;
return a->GetActor()->GetID() == id;
});
return (found != m_actors.end()) ? found->get() : nullptr;
return (found != m_actorAssets.end()) ? (*found)->GetActor() : nullptr;
}
AZStd::shared_ptr<Actor> ActorManager::FindSharedActorByID(uint32 id) const
AZ::Data::AssetId ActorManager::FindAssetIdByActorId(uint32 id) const
{
const auto found = AZStd::find_if(m_actors.begin(), m_actors.end(), [id](const AZStd::shared_ptr<Actor>& a)
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[id](const ActorAssetData& a)
{
return a->GetID() == id;
return a->GetActor()->GetID() == id;
});
return (found != m_actors.end()) ? *found : nullptr;
return (found != m_actorAssets.end()) ? found->GetId() : AZ::Data::AssetId();
}
@@ -264,14 +292,20 @@ namespace EMotionFX
// unregister an actor
void ActorManager::UnregisterActor(const AZStd::shared_ptr<Actor>& actor)
void ActorManager::UnregisterActor(AZ::Data::AssetId actorAssetID)
{
LockActors();
auto result = AZStd::find(m_actors.begin(), m_actors.end(), actor);
if (result != m_actors.end())
const auto found = AZStd::find_if(
m_actorAssets.begin(), m_actorAssets.end(),
[actorAssetID](const ActorAssetData& a)
{
return a.GetId() == actorAssetID;
});
if (found != m_actorAssets.end())
{
m_actors.erase(result);
m_actorAssets.erase(found);
}
UnlockActors();
}
@@ -297,7 +331,7 @@ namespace EMotionFX
LockActors();
// clear all actors
m_actors.clear();
m_actorAssets.clear();
// TODO: what if there are still references to the actors inside the list of registered actor instances?
UnlockActors();
@@ -390,7 +424,13 @@ namespace EMotionFX
Actor* ActorManager::GetActor(size_t nr) const
{
return m_actors[nr].get();
return m_actorAssets[nr]->GetActor();
}
ActorAssetData ActorManager::GetActorAsset(size_t nr) const
{
return m_actorAssets[nr];
}
@@ -16,7 +16,8 @@
#include <MCore/Source/MultiThreadManager.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/weak_ptr.h>
#include <Source/Integration/Assets/ActorAsset.h>
#include <Source/Integration/System/SystemCommon.h>
namespace EMotionFX
{
@@ -47,7 +48,7 @@ namespace EMotionFX
* Register an actor.
* @param actor The actor to register.
*/
void RegisterActor(AZStd::shared_ptr<Actor> actor);
void RegisterActor(ActorAssetData actorAsset);
/**
* Unregister all actors.
@@ -60,14 +61,14 @@ namespace EMotionFX
* Unregister a specific actor.
* @param actor The actor you passed to the RegisterActor function sometime before.
*/
void UnregisterActor(const AZStd::shared_ptr<Actor>& actor);
void UnregisterActor(AZ::Data::AssetId actorAssetID);
/**
* Get the number of registered actors.
* This does not include the clones that have been optionally created.
* @result The number of registered actors.
*/
MCORE_INLINE size_t GetNumActors() const { return m_actors.size(); }
MCORE_INLINE size_t GetNumActors() const { return m_actorAssets.size(); }
/**
* Get a given actor.
@@ -78,6 +79,7 @@ namespace EMotionFX
* @result A reference to the actor object that contains the array of Actor objects.
*/
Actor* GetActor(size_t nr) const;
ActorAssetData GetActorAsset(size_t nr) const;
/**
* Find the given actor by name.
@@ -99,7 +101,8 @@ namespace EMotionFX
* @param actor The actor object you once passed to RegisterActor.
* @result Returns the actor number, which is in range of [0..GetNumActors()-1], or returns MCORE_INVALIDINDEX32 when not found.
*/
size_t FindActorIndex(Actor* actor) const;
size_t FindActorIndex(AZ::Data::AssetId assetId) const;
size_t FindActorIndex(const Actor* actor) const;
/**
* Find the actor number for a given actor name.
@@ -160,7 +163,7 @@ namespace EMotionFX
*/
Actor* FindActorByID(uint32 id) const;
AZStd::shared_ptr<Actor> FindSharedActorByID(uint32 id) const;
AZ::Data::AssetId FindAssetIdByActorId(uint32 id) const;
/**
* Check if the given actor instance is registered.
@@ -255,9 +258,9 @@ namespace EMotionFX
void UnlockActors();
private:
AZStd::vector<ActorInstance*> m_actorInstances; /**< The registered actor instances. */
AZStd::vector<AZStd::shared_ptr<Actor>> m_actors; /**< The registered actors. */
AZStd::vector<ActorInstance*> m_rootActorInstances; /**< Root actor instances (roots of all attachment chains). */
AZStd::vector<ActorInstance*> m_actorInstances; /**< The registered actor instances. */
AZStd::vector<ActorAssetData> m_actorAssets;
AZStd::vector<ActorInstance*> m_rootActorInstances; /**< Root actor instances (roots of all attachment chains). */
ActorUpdateScheduler* m_scheduler; /**< The update scheduler to use. */
MCore::MutexRecursive m_actorLock; /**< The multithread lock for touching the actors array. */
MCore::MutexRecursive m_actorInstanceLock; /**< The multithread lock for touching the actor instances array. */
@@ -1,109 +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/smart_ptr/shared_ptr.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/ActorManager.h>
namespace EMotionFX
{
class Actor;
/**
* @brief An Actor pointer that unregisters itself when it goes out of
* scope
*
* This class allows for simple functionality of automatically registering
* and unregistering an actor from the manager. Its primary use case is the
* ActorAsset, that shares ownership with the manager. But it can also be
* used anywhere that needs to make an Actor that needs to be in the
* Manager for a given period of time. A good example of this is anything
* that needs Actor commands to work on an actor that is made in a given
* scope. One main place where this happens is in the Actor asset processor
* code.
*/
class AutoRegisteredActor
{
public:
AutoRegisteredActor() = default;
template<class T>
AutoRegisteredActor(AZStd::shared_ptr<T> actor)
: m_actor(AZStd::move(actor))
{
Register(m_actor);
}
template<class T>
AutoRegisteredActor(AZStd::unique_ptr<T> actor)
: m_actor(AZStd::move(actor))
{
Register(m_actor);
}
// This class is not copyable, because a given actor cannot be
// registered with the manager multiple times
AutoRegisteredActor(const AutoRegisteredActor&) = delete;
AutoRegisteredActor& operator=(const AutoRegisteredActor&) = delete;
AutoRegisteredActor(AutoRegisteredActor&& other) noexcept
{
*this = AZStd::move(other);
}
AutoRegisteredActor& operator=(AutoRegisteredActor&& other) noexcept
{
if (this != &other)
{
Unregister(m_actor);
m_actor = AZStd::move(other.m_actor);
}
return *this;
}
~AutoRegisteredActor()
{
Unregister(m_actor);
}
Actor* operator->() const
{
return m_actor.operator->();
}
operator bool() const
{
return static_cast<bool>(m_actor);
}
Actor* get() const
{
return m_actor.get();
}
private:
void Register(const AZStd::shared_ptr<Actor>& actor)
{
if (actor)
{
GetActorManager().RegisterActor(actor);
}
}
void Unregister(const AZStd::shared_ptr<Actor>& actor)
{
if (actor)
{
GetActorManager().UnregisterActor(actor);
}
}
AZStd::shared_ptr<Actor> m_actor;
};
} // namespace EMotionFX
@@ -46,15 +46,10 @@ AZ_POP_DISABLE_WARNING
namespace EMStudio
{
//--------------------------------------------------------------------------
// globals
//--------------------------------------------------------------------------
EMStudioManager* gEMStudioMgr = nullptr;
//--------------------------------------------------------------------------
// class EMStudioManager
//--------------------------------------------------------------------------
AZ_CLASS_ALLOCATOR_IMPL(EMStudioManager, AZ::SystemAllocator, 0)
// constructor
EMStudioManager::EMStudioManager(QApplication* app, [[maybe_unused]] int& argc, [[maybe_unused]] char* argv[])
@@ -105,8 +100,9 @@ namespace EMStudio
// log some information
LogInfo();
}
AZ::Interface<EMStudioManager>::Register(this);
}
// destructor
EMStudioManager::~EMStudioManager()
@@ -130,6 +126,8 @@ namespace EMStudio
delete m_commandManager;
AZ::AllocatorInstance<UIAllocator>::Destroy();
AZ::Interface<EMStudioManager>::Unregister(this);
}
MainWindow* EMStudioManager::GetMainWindow()
@@ -422,6 +420,12 @@ namespace EMStudio
}
EMStudioManager* EMStudioManager::GetInstance()
{
return AZ::Interface<EMStudioManager>().Get();
}
// function to add a gizmo to the manager
MCommon::TransformationManipulator* EMStudioManager::AddTransformationManipulator(MCommon::TransformationManipulator* manipulator)
{
@@ -494,30 +498,48 @@ namespace EMStudio
painter.drawPath(path);
}
//--------------------------------------------------------------------------
// class Initializer
//--------------------------------------------------------------------------
// initialize EMotion Studio
bool Initializer::Init(QApplication* app, int& argc, char* argv[])
// shortcuts
QApplication* GetApp()
{
// do nothing if we already have initialized
if (gEMStudioMgr)
{
return true;
}
// create the new EMStudio object
gEMStudioMgr = new EMStudioManager(app, argc, argv);
// return success
return true;
return EMStudioManager::GetInstance()->GetApp();
}
EMStudioManager* GetManager()
{
return EMStudioManager::GetInstance();
}
// the shutdown function
void Initializer::Shutdown()
bool HasMainWindow()
{
delete gEMStudioMgr;
gEMStudioMgr = nullptr;
return EMStudioManager::GetInstance()->HasMainWindow();
}
MainWindow* GetMainWindow()
{
return EMStudioManager::GetInstance()->GetMainWindow();
}
PluginManager* GetPluginManager()
{
return EMStudioManager::GetInstance()->GetPluginManager();
}
LayoutManager* GetLayoutManager()
{
return EMStudioManager::GetInstance()->GetLayoutManager();
}
NotificationWindowManager* GetNotificationWindowManager()
{
return EMStudioManager::GetInstance()->GetNotificationWindowManager();
}
MotionEventPresetManager* GetEventPresetManager()
{
return EMStudioManager::GetInstance()->GetEventPresetManger();
}
CommandSystem::CommandManager* GetCommandManager()
{
return EMStudioManager::GetInstance()->GetCommandManager();
}
} // namespace EMStudio
@@ -53,9 +53,10 @@ namespace EMStudio
class EMSTUDIO_API EMStudioManager
: private EMotionFX::SkeletonOutlinerNotificationBus::Handler
{
MCORE_MEMORYOBJECTCATEGORY(EMStudioManager, MCore::MCORE_DEFAULT_ALIGNMENT, MEMCATEGORY_EMSTUDIOSDK)
public:
AZ_RTTI(EMStudio::EMStudioManager, "{D45E95CF-0C7B-44F1-A9D4-99A1E12A5AB5}")
AZ_CLASS_ALLOCATOR_DECL
EMStudioManager(QApplication* app, int& argc, char* argv[]);
~EMStudioManager();
@@ -72,6 +73,9 @@ namespace EMStudio
AZStd::string GetRecoverFolder() const;
AZStd::string GetAutosavesFolder() const;
// Singleton pattern
static EMStudioManager* GetInstance();
// text rendering helper function
static void RenderText(QPainter& painter, const QString& text, const QColor& textColor, const QFont& font, const QFontMetrics& fontMetrics, Qt::Alignment textAlignment, const QRect& rect);
@@ -150,34 +154,17 @@ namespace EMStudio
void OnRemoveCommand(size_t historyIndex) override { MCORE_UNUSED(historyIndex); }
void OnSetCurrentCommand(size_t index) override { MCORE_UNUSED(index); }
};
EventProcessingCallback* m_eventProcessingCallback;
EventProcessingCallback* m_eventProcessingCallback = nullptr;
};
/**
*
*
*
*/
class EMSTUDIO_API Initializer
{
public:
static bool MCORE_CDECL Init(QApplication* app, int& argc, char* argv[]);
static void MCORE_CDECL Shutdown();
};
// the global manager
extern EMSTUDIO_API EMStudioManager* gEMStudioMgr;
// shortcuts
MCORE_INLINE QApplication* GetApp() { return gEMStudioMgr->GetApp(); }
MCORE_INLINE EMStudioManager* GetManager() { return gEMStudioMgr; }
MCORE_INLINE bool HasMainWindow() { return gEMStudioMgr->HasMainWindow(); }
MCORE_INLINE MainWindow* GetMainWindow() { return gEMStudioMgr->GetMainWindow(); }
MCORE_INLINE PluginManager* GetPluginManager() { return gEMStudioMgr->GetPluginManager(); }
MCORE_INLINE LayoutManager* GetLayoutManager() { return gEMStudioMgr->GetLayoutManager(); }
MCORE_INLINE NotificationWindowManager* GetNotificationWindowManager() { return gEMStudioMgr->GetNotificationWindowManager(); }
MCORE_INLINE MotionEventPresetManager* GetEventPresetManager() { return gEMStudioMgr->GetEventPresetManger(); }
MCORE_INLINE CommandSystem::CommandManager* GetCommandManager() { return gEMStudioMgr->GetCommandManager(); }
// Shortcuts
QApplication* GetApp();
EMStudioManager* GetManager();
bool HasMainWindow();
MainWindow* GetMainWindow();
PluginManager* GetPluginManager();
LayoutManager* GetLayoutManager();
NotificationWindowManager* GetNotificationWindowManager();
MotionEventPresetManager* GetEventPresetManager();
CommandSystem::CommandManager* GetCommandManager();
} // namespace EMStudio
@@ -112,10 +112,6 @@ namespace EMStudio
for (size_t i = 0; i < actorCount; ++i)
{
EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i);
if (actor->GetIsOwnedByRuntime())
{
continue;
}
if (AzFramework::StringFunc::Equal(filename, actor->GetFileName()))
{
@@ -1792,11 +1792,6 @@ namespace EMStudio
{
EMotionFX::Actor* actor = selectionList.GetActorInstance(i)->GetActor();
if (actor->GetIsOwnedByRuntime())
{
continue;
}
if (AZStd::find(savingActors.begin(), savingActors.end(), actor) == savingActors.end())
{
savingActors.push_back(actor);
@@ -10,9 +10,9 @@
#if !defined(Q_MOC_RUN)
#include <AzCore/Component/TickBus.h>
#include <EMotionStudio/EMStudioSDK/Source/EMStudioConfig.h>
#include <EMotionStudio/EMStudioSDK/Source/GUIOptions.h>
#include <EMotionStudio/EMStudioSDK/Source/PluginOptionsBus.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioConfig.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/GUIOptions.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/PluginOptionsBus.h>
#include <AzCore/std/containers/vector.h>
#include <MCore/Source/Command.h>
#include <MCore/Source/StandardHeaders.h>
@@ -389,7 +389,7 @@ namespace EMStudio
// get the current actor and the number of clones
EMotionFX::Actor* actor = EMotionFX::GetActorManager().GetActor(i);
if (actor->GetIsOwnedByRuntime() || !actor->IsReady())
if (!actor->IsReady())
{
continue;
}
@@ -421,7 +421,7 @@ namespace EMStudio
// At this point the render actor could point to an already deleted actor.
// In case the actor got deleted we might get an unexpected flag as result.
if (!found || (found && actor->GetIsOwnedByRuntime()) || (!actor->IsReady()))
if (!found || (!actor->IsReady()))
{
DestroyEMStudioActor(actor);
}
@@ -155,12 +155,12 @@ namespace EMStudio
cameraMenu->addAction("Reset Camera", [this]() { this->OnResetCamera(); });
QAction* showSelectedAction = cameraMenu->addAction("Show Selected", this, &RenderViewWidget::OnShowSelected);
showSelectedAction->setShortcut(Qt::Key_S);
showSelectedAction->setShortcut(QKeySequence(Qt::Key_S + Qt::SHIFT));
GetMainWindow()->GetShortcutManager()->RegisterKeyboardShortcut(showSelectedAction, RenderPlugin::s_renderWindowShortcutGroupName, true);
addAction(showSelectedAction);
QAction* showEntireSceneAction = cameraMenu->addAction("Show Entire Scene", this, &RenderViewWidget::OnShowEntireScene);
showEntireSceneAction->setShortcut(Qt::Key_A);
showEntireSceneAction->setShortcut(QKeySequence(Qt::Key_A + Qt::SHIFT));
GetMainWindow()->GetShortcutManager()->RegisterKeyboardShortcut(showEntireSceneAction, RenderPlugin::s_renderWindowShortcutGroupName, true);
addAction(showEntireSceneAction);
@@ -65,8 +65,7 @@ namespace EMStudio
m_actorCheckbox = new QCheckBox("Actors");
m_actorCheckbox->setObjectName("EMFX.ResetSettingsDialog.Actors");
const bool hasActors = HasEntityInEditor(
EMotionFX::GetActorManager(), &EMotionFX::ActorManager::GetNumActors, &EMotionFX::ActorManager::GetActor);
const bool hasActors = EMotionFX::GetActorManager().GetNumActors() > 0;
m_actorCheckbox->setChecked(hasActors);
m_actorCheckbox->setDisabled(!hasActors);
@@ -327,7 +327,16 @@ namespace EMStudio
void NodeWindowPlugin::OnActorReady([[maybe_unused]] EMotionFX::Actor* actor)
{
ReInit();
m_reinitRequested = true;
}
void NodeWindowPlugin::ProcessFrame([[maybe_unused]] float timePassedInSeconds)
{
if (m_reinitRequested)
{
ReInit();
m_reinitRequested = false;
}
}
//-----------------------------------------------------------------------------------------
@@ -60,6 +60,8 @@ namespace EMStudio
EMStudioPlugin* Clone() override;
void ReInit();
void ProcessFrame(float timePassedInSeconds) override;
public slots:
void OnNodeChanged();
void VisibilityChanged(bool isVisible);
@@ -87,5 +89,8 @@ namespace EMStudio
AZStd::unique_ptr<ActorInfo> m_actorInfo;
AZStd::unique_ptr<NodeInfo> m_nodeInfo;
// Use this flag to defer the reinit function to main thread.
bool m_reinitRequested = false;
};
} // namespace EMStudio
@@ -123,12 +123,6 @@ namespace EMStudio
continue;
}
// ignore engine actors
if (actor->GetIsOwnedByRuntime())
{
continue;
}
// create a tree item for the new attachment
QTreeWidgetItem* newItem = new QTreeWidgetItem(m_treeWidget);
@@ -25,7 +25,6 @@ set(FILES
Source/AttachmentNode.h
Source/AttachmentSkin.cpp
Source/AttachmentSkin.h
Source/AutoRegisteredActor.h
Source/BaseObject.cpp
Source/BaseObject.h
Source/CompressedKeyFrames.h
@@ -16,7 +16,7 @@
#include <AzCore/RTTI/TypeInfo.h>
#include <AzFramework/Physics/AnimationConfiguration.h>
#include <AzFramework/Physics/Character.h>
#include <Integration/Assets/ActorAsset.h>
namespace EMotionFX
{
@@ -96,6 +96,9 @@ namespace EMotionFX
/// Returns skinning method used by the actor.
virtual SkinningMethod GetSkinningMethod() const = 0;
// Use this to alter the actor asset.
virtual void SetActorAsset(AZ::Data::Asset<EMotionFX::Integration::ActorAsset> actorAsset) = 0;
static const size_t s_invalidJointIndex = std::numeric_limits<size_t>::max();
};
@@ -46,6 +46,9 @@ namespace EMotionFX
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
// Use this bus to register custom EMotionFX plugin.
virtual void OnRegisterPlugin() = 0;
};
using SystemNotificationBus = AZ::EBus<SystemNotifications>;
@@ -6,7 +6,6 @@
*
*/
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/Importer/Importer.h>
@@ -64,8 +63,7 @@ namespace EMotionFX
&actorSettings,
"");
// Set the is owned by runtime flag before finalizing the actor, as that uses the flag already.
assetData->m_emfxActor->SetIsOwnedByRuntime(true);
assetData->m_emfxActor->SetFileName(asset.GetHint().c_str());
assetData->m_emfxActor->Finalize();
// Clear out the EMFX raw asset data.
@@ -15,7 +15,6 @@
#include <Integration/Assets/AssetCommon.h>
#include <Integration/Rendering/RenderActor.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
namespace EMotionFX
@@ -58,7 +57,7 @@ namespace EMotionFX
void InitRenderActor();
private:
AutoRegisteredActor m_emfxActor; ///< Pointer to shared EMotionFX actor
AZStd::shared_ptr<Actor> m_emfxActor;
AZStd::unique_ptr<RenderActor> m_renderActor;
};
@@ -81,6 +80,8 @@ namespace EMotionFX
const char* GetBrowserIcon() const override;
};
} // namespace Integration
using ActorAssetData = AZ::Data::Asset<Integration::ActorAsset>;
} // namespace EMotionFX
namespace AZ
@@ -121,6 +121,7 @@ namespace EMotionFX
void SetRenderCharacter(bool enable) override;
bool GetRenderActorVisible() const override;
SkinningMethod GetSkinningMethod() const override;
void SetActorAsset(AZ::Data::Asset<ActorAsset> actorAsset) override;
//////////////////////////////////////////////////////////////////////////
// ActorComponentNotificationBus::Handler
@@ -178,8 +179,6 @@ namespace EMotionFX
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
bool IsPhysicsSceneSimulationFinishEventConnected() const;
void SetActorAsset(AZ::Data::Asset<ActorAsset> actorAsset);
AZ::Data::Asset<ActorAsset> GetActorAsset() const { return m_configuration.m_actorAsset; }
private:
@@ -60,6 +60,7 @@ namespace EMotionFX
bool GetRenderActorVisible() const override;
size_t GetNumJoints() const override;
SkinningMethod GetSkinningMethod() const override;
void SetActorAsset(AZ::Data::Asset<ActorAsset> actorAsset) override;
// EditorActorComponentRequestBus overrides ...
const AZ::Data::AssetId& GetActorAssetId() override;
@@ -79,8 +80,6 @@ namespace EMotionFX
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void SetActorAsset(AZ::Data::Asset<ActorAsset> actorAsset);
// BoundsRequestBus overrides ...
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
@@ -34,6 +34,7 @@
#include <EMotionFX/Source/MotionEventTrack.h>
#include <EMotionFX/Source/AnimGraphSyncTrack.h>
#include <EMotionFX/Source/AnimGraph.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/PhysicsSetup.h>
#include <EMotionFX/Source/SimulatedObjectSetup.h>
@@ -45,6 +46,7 @@
#include <EMotionFX/Source/PoseData.h>
#include <EMotionFX/Source/PoseDataRagdoll.h>
#include <Integration/AnimationBus.h>
#include <Integration/EMotionFXBus.h>
#include <Integration/Assets/ActorAsset.h>
#include <Integration/Assets/MotionAsset.h>
@@ -69,7 +71,6 @@
# include <AzToolsFramework/API/ViewPaneOptions.h>
# include <AzCore/std/string/wildcard.h>
# include <QApplication>
# include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
# include <EMotionStudio/EMStudioSDK/Source/MainWindow.h>
# include <EMotionStudio/EMStudioSDK/Source/PluginManager.h>
// EMStudio plugins
@@ -528,7 +529,7 @@ namespace EMotionFX
if (EMStudio::GetManager())
{
EMStudio::Initializer::Shutdown();
m_emstudioManager.reset();
MysticQt::Initializer::Shutdown();
}
@@ -798,6 +799,8 @@ namespace EMotionFX
pluginManager->RegisterPlugin(new EMotionFX::RagdollNodeInspectorPlugin());
pluginManager->RegisterPlugin(new EMotionFX::ClothJointInspectorPlugin());
pluginManager->RegisterPlugin(new EMotionFX::SimulatedObjectWidget());
SystemNotificationBus::Broadcast(&SystemNotificationBus::Events::OnRegisterPlugin);
}
//////////////////////////////////////////////////////////////////////////
@@ -813,7 +816,7 @@ namespace EMotionFX
char** argv = nullptr;
MysticQt::Initializer::Init("", editorAssetsPath.c_str());
EMStudio::Initializer::Init(qApp, argc, argv);
m_emstudioManager = AZStd::make_unique<EMStudio::EMStudioManager>(qApp, argc, argv);
InitializeEMStudioPlugins();
@@ -24,6 +24,7 @@
# include <AzToolsFramework/API/ToolsApplicationAPI.h>
# include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
# include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
# include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#endif // EMOTIONFXANIMATION_EDITOR
namespace AZ
@@ -126,6 +127,10 @@ namespace EMotionFX
AZStd::vector<AZStd::unique_ptr<AZ::Data::AssetHandler> > m_assetHandlers;
AZStd::unique_ptr<EMotionFXEventHandler> m_eventHandler;
AZStd::unique_ptr<RenderBackendManager> m_renderBackendManager;
#if defined(EMOTIONFXANIMATION_EDITOR)
AZStd::unique_ptr<EMStudio::EMStudioManager> m_emstudioManager;
#endif // EMOTIONFXANIMATION_EDITOR
};
}
}
+14 -3
View File
@@ -10,12 +10,15 @@
#include <MCore/Source/ReflectionSerializer.h>
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/Importer/Importer.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/SimulatedObjectSetup.h>
#include <Tests/TestAssetCode/JackActor.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
namespace EMotionFX
{
@@ -23,8 +26,9 @@ namespace EMotionFX
{
SystemComponentFixture::SetUp();
m_actor = ActorFactory::CreateAndInit<JackNoMeshesActor>();
m_actorInstance = ActorInstance::Create(m_actor.get());
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<JackNoMeshesActor>(actorAssetId);
m_actorInstance = ActorInstance::Create(GetActor());
}
void ActorFixture::TearDown()
@@ -35,6 +39,7 @@ namespace EMotionFX
m_actorInstance = nullptr;
}
GetEMotionFX().GetActorManager()->UnregisterAllActors();
SystemComponentFixture::TearDown();
}
@@ -71,7 +76,7 @@ namespace EMotionFX
AZ::ObjectStream::FilterDescriptor loadFilter(nullptr, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES);
SimulatedObjectSetup* setup = AZ::Utils::LoadObjectFromBuffer<EMotionFX::SimulatedObjectSetup>(data.data(), data.size(), serializeContext, loadFilter);
setup->InitAfterLoad(m_actor.get());
setup->InitAfterLoad(GetActor());
return setup;
}
@@ -80,4 +85,10 @@ namespace EMotionFX
{
return { "Bip01__pelvis", "l_upLeg", "l_loLeg", "l_ankle" };
}
Actor* ActorFixture::GetActor() const
{
return m_actorAsset->GetActor();
}
} // namespace EMotionFX
+4 -3
View File
@@ -9,8 +9,7 @@
#pragma once
#include "SystemComponentFixture.h"
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <Integration/Assets/ActorAsset.h>
namespace EMotionFX
{
@@ -31,7 +30,9 @@ namespace EMotionFX
AZStd::vector<AZStd::string> GetTestJointNames() const;
protected:
AutoRegisteredActor m_actor{};
Actor* GetActor() const;
AZ::Data::Asset<Integration::ActorAsset> m_actorAsset;
ActorInstance* m_actorInstance = nullptr;
};
} // namespace EMotionFX
@@ -26,7 +26,7 @@ namespace EMotionFX
public:
void CreateSubMotionLikeBindPose(const std::string& name)
{
const Skeleton* skeleton = m_actor->GetSkeleton();
const Skeleton* skeleton = GetActor()->GetSkeleton();
size_t jointIndex = InvalidIndex;
const Node* node = skeleton->FindNodeAndIndexByName(name.c_str(), jointIndex);
ASSERT_NE(node, nullptr);
@@ -40,7 +40,7 @@ namespace EMotionFX
void CreateSubMotion(const std::string& name, const Transform& transform)
{
// Find and store the joint index.
const Skeleton* skeleton = m_actor->GetSkeleton();
const Skeleton* skeleton = GetActor()->GetSkeleton();
size_t jointIndex = InvalidIndex;
const Node* node = skeleton->FindNodeAndIndexByName(name.c_str(), jointIndex);
ASSERT_NE(node, nullptr);
@@ -55,7 +55,7 @@ namespace EMotionFX
ActorFixture::SetUp();
// Get the joint that isn't in the motion data.
Node* footNode = m_actor->GetSkeleton()->FindNodeAndIndexByName("l_ball", m_footIndex);
Node* footNode = GetActor()->GetSkeleton()->FindNodeAndIndexByName("l_ball", m_footIndex);
ASSERT_NE(footNode, nullptr);
ASSERT_NE(m_footIndex, InvalidIndex32);
@@ -98,7 +98,7 @@ namespace EMotionFX
TEST_F(MotionSamplingFixture, SampleAdditiveJoint)
{
const Skeleton* skeleton = m_actor->GetSkeleton();
const Skeleton* skeleton = GetActor()->GetSkeleton();
// Sample the joints that exist in our actor skeleton as well as inside the motion data.
const Pose* bindPose = m_actorInstance->GetTransformData()->GetBindPose();
@@ -106,7 +106,7 @@ namespace EMotionFX
{
// Sample the motion.
Transform transform = Transform::CreateZero(); // Set all to Zero, not identity as this methods might return identity and we want to verify that.
m_motion->CalcNodeTransform(m_motionInstance, &transform, m_actor.get(), skeleton->GetNode(jointIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false);
m_motion->CalcNodeTransform(m_motionInstance, &transform, GetActor(), skeleton->GetNode(jointIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false);
const Transform& bindTransform = bindPose->GetLocalSpaceTransform(jointIndex);
EXPECT_THAT(transform, IsClose(bindTransform));
@@ -114,7 +114,7 @@ namespace EMotionFX
// Sample the motion for the foot node.
Transform footTransform = Transform::CreateZero(); // Set all to Zero, not identity as this methods might return identity and we want to verify that.
m_motion->CalcNodeTransform(m_motionInstance, &footTransform, m_actor.get(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false);
m_motion->CalcNodeTransform(m_motionInstance, &footTransform, GetActor(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false);
// Make sure we get an identity transform back as we try to sample a node that doesn't have a submotion in an additive motion.
EXPECT_THAT(footTransform, IsClose(Transform::CreateIdentity()));
@@ -125,7 +125,7 @@ namespace EMotionFX
// Make sure we do not get an identity transform back now that it is a non-additive motion.
footTransform.Zero(); // Set all to Zero, not identity as this methods might return identity and we want to verify that.
const Transform& expectedFootTransform = m_actorInstance->GetTransformData()->GetCurrentPose()->GetLocalSpaceTransform(m_footIndex);
m_motion->CalcNodeTransform(m_motionInstance, &footTransform, m_actor.get(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false);
m_motion->CalcNodeTransform(m_motionInstance, &footTransform, GetActor(), skeleton->GetNode(m_footIndex), /*timeValue=*/0.0f, /*enableRetargeting=*/false);
EXPECT_THAT(footTransform, IsClose(expectedFootTransform));
}
@@ -134,7 +134,7 @@ namespace EMotionFX
// Sample a pose from the motion.
Pose pose;
pose.LinkToActorInstance(m_actorInstance);
pose.InitFromBindPose(m_actor.get());
pose.InitFromBindPose(GetActor());
pose.Zero();
m_motion->Update(&pose, &pose, m_motionInstance);
@@ -15,6 +15,7 @@
#include <EMotionFX/Source/BlendTreeBlend2Node.h>
#include <EMotionFX/Source/BlendTreeBlendNNode.h>
#include <EMotionFX/Source/BlendTreeParameterNode.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
#include <EMotionFX/Source/Parameter/ParameterFactory.h>
#include <MCore/Source/AttributeFloat.h>
@@ -105,7 +105,7 @@ namespace EMotionFX
TEST_P(RagdollRootNodeFixture, RagdollRootNodeIsSimulatedTests)
{
Physics::RagdollConfiguration& ragdollConfig = m_actor->GetPhysicsSetup()->GetRagdollConfig();
Physics::RagdollConfiguration& ragdollConfig = GetActor()->GetPhysicsSetup()->GetRagdollConfig();
AZStd::vector<Physics::RagdollNodeConfiguration>& ragdollNodes = ragdollConfig.m_nodes;
const RagdollRootNodeParam& param = GetParam();
const AZStd::string ragdollRootNodeName = param.m_ragdollRootNode.c_str();
@@ -24,13 +24,13 @@ namespace EMotionFX
CommandSystem::CommandManager commandManager;
MCore::CommandGroup commandGroup;
const AZ::u32 actorId = m_actor->GetID();
const AZ::u32 actorId = GetActor()->GetID();
const AZStd::vector<AZStd::string> jointNames = GetTestJointNames();
const size_t jointCount = jointNames.size();
// 1. Add colliders
const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(GetActor());
for (const AZStd::string& jointName : jointNames)
{
CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid<Physics::BoxShapeConfiguration>(), &commandGroup);
@@ -39,23 +39,27 @@ namespace EMotionFX
}
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serializedAfterAdd = SerializePhysicsSetup(m_actor.get());
EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(jointCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
const AZStd::string serializedAfterAdd = SerializePhysicsSetup(GetActor());
EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(
jointCount,
PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Box));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(jointCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(
jointCount,
PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Box));
EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(GetActor()));
// 2. Remove colliders
commandGroup.RemoveAllCommands();
const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(GetActor());
size_t colliderIndexToRemove = 1;
for (const AZStd::string& jointName : jointNames)
@@ -64,18 +68,24 @@ namespace EMotionFX
}
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serializedAfterRemove = SerializePhysicsSetup(m_actor.get());
EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Capsule));
const AZStd::string serializedAfterRemove = SerializePhysicsSetup(GetActor());
EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(
0,
PhysicsSetupUtils::CountColliders(
GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Capsule));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(jointCount * 3, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Capsule));
EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(jointCount * 2, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(
0,
PhysicsSetupUtils::CountColliders(
GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/ false, Physics::ShapeType::Capsule));
EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(GetActor()));
}
TEST_F(ColliderCommandTests, AddRemove1000Colliders)
@@ -84,11 +94,11 @@ namespace EMotionFX
CommandSystem::CommandManager commandManager;
MCore::CommandGroup commandGroup;
const AZ::u32 actorId = m_actor->GetID();
const AZ::u32 actorId = GetActor()->GetID();
const AZStd::string jointName = "Bip01__pelvis";
// 1. Add colliders
const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeAdd = SerializePhysicsSetup(GetActor());
const size_t colliderCount = 1000;
for (AZ::u32 i = 0; i < colliderCount; ++i)
{
@@ -96,51 +106,51 @@ namespace EMotionFX
}
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serializedAfterAdd = SerializePhysicsSetup(m_actor.get());
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
const AZStd::string serializedAfterAdd = SerializePhysicsSetup(GetActor());
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeAdd, SerializePhysicsSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_EQ(serializedAfterAdd, SerializePhysicsSetup(GetActor()));
// 2. Clear colliders
commandGroup.RemoveAllCommands();
const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeRemove = SerializePhysicsSetup(GetActor());
CommandColliderHelpers::ClearColliders(actorId, jointName, PhysicsSetup::HitDetection, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serializedAfterRemove = SerializePhysicsSetup(m_actor.get());
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
const AZStd::string serializedAfterRemove = SerializePhysicsSetup(GetActor());
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(colliderCount, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(serializedBeforeRemove, SerializePhysicsSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(m_actor.get(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(m_actor.get()));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection));
EXPECT_EQ(0, PhysicsSetupUtils::CountColliders(GetActor(), PhysicsSetup::HitDetection, /*ignoreShapeType*/false, Physics::ShapeType::Box));
EXPECT_EQ(serializedAfterRemove, SerializePhysicsSetup(GetActor()));
}
TEST_F(ColliderCommandTests, AutoSizingColliders)
{
CommandSystem::CommandManager commandManager;
const AZ::u32 actorId = m_actor->GetID();
const AZ::u32 actorId = GetActor()->GetID();
const AZStd::vector<AZStd::string> jointNames = GetTestJointNames();
ASSERT_TRUE(jointNames.size() > 0) << "The joint names test data needs at least one joint for this test.";
const AZStd::string& jointName = jointNames[0];
CommandColliderHelpers::AddCollider(actorId, jointName, PhysicsSetup::HitDetection, azrtti_typeid<Physics::BoxShapeConfiguration>());
const AZStd::shared_ptr<PhysicsSetup>& physicsSetup = m_actor->GetPhysicsSetup();
const AZStd::shared_ptr<PhysicsSetup>& physicsSetup = GetActor()->GetPhysicsSetup();
Physics::CharacterColliderConfiguration* colliderConfig = physicsSetup->GetColliderConfigByType(PhysicsSetup::HitDetection);
EXPECT_NE(colliderConfig, nullptr) << "Collider config should be valid after we added a collider to it.";
@@ -184,11 +194,11 @@ namespace EMotionFX
const PhysicsSetup::ColliderConfigType m_configType = PhysicsSetup::ColliderConfigType::HitDetection;
// Add collider to the given joint first.
const AZStd::shared_ptr<PhysicsSetup>& physicsSetup = m_actor->GetPhysicsSetup();
EXPECT_TRUE(CommandColliderHelpers::AddCollider(m_actor->GetID(), m_jointName, m_configType, param.m_shapeType));
const AZStd::shared_ptr<PhysicsSetup>& physicsSetup = GetActor()->GetPhysicsSetup();
EXPECT_TRUE(CommandColliderHelpers::AddCollider(GetActor()->GetID(), m_jointName, m_configType, param.m_shapeType));
Physics::CharacterColliderConfiguration* characterColliderConfig = physicsSetup->GetColliderConfigByType(m_configType);
ASSERT_TRUE(characterColliderConfig != nullptr);
Physics::CharacterColliderNodeConfiguration* nodeConfig = CommandColliderHelpers::GetCreateNodeConfig(m_actor.get(), m_jointName, *characterColliderConfig, result);
Physics::CharacterColliderNodeConfiguration* nodeConfig = CommandColliderHelpers::GetCreateNodeConfig(GetActor(), m_jointName, *characterColliderConfig, result);
ASSERT_TRUE(nodeConfig != nullptr);
EXPECT_EQ(nodeConfig->m_shapes.size(), 1);
@@ -200,7 +210,8 @@ namespace EMotionFX
// Create the adjust collider command and using the data from the test parameter.
MCore::Command* orgCommand = CommandSystem::GetCommandManager()->FindCommand(CommandAdjustCollider::s_commandName);
CommandAdjustCollider* command = aznew CommandAdjustCollider(m_actor->GetID(), m_jointName, m_configType, /*colliderIndex=*/0, orgCommand);
CommandAdjustCollider* command =
aznew CommandAdjustCollider(GetActor()->GetID(), m_jointName, m_configType, /*colliderIndex=*/0, orgCommand);
command->SetOldIsTrigger(colliderConfig->m_isTrigger);
command->SetIsTrigger(param.m_isTrigger);
command->SetOldPosition(colliderConfig->m_position);
@@ -223,9 +234,9 @@ namespace EMotionFX
}
// Check execute.
const AZStd::string serializedBeforeExecute = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeExecute = SerializePhysicsSetup(GetActor());
EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(command, result));
const AZStd::string serializedAfterExecute = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedAfterExecute = SerializePhysicsSetup(GetActor());
EXPECT_EQ(colliderConfig->m_isTrigger, param.m_isTrigger);
EXPECT_EQ(colliderConfig->m_position, param.m_position);
@@ -243,12 +254,12 @@ namespace EMotionFX
// Check undo.
EXPECT_TRUE(CommandSystem::GetCommandManager()->Undo(result));
const AZStd::string serializedAfterUndo = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedAfterUndo = SerializePhysicsSetup(GetActor());
EXPECT_EQ(serializedAfterUndo, serializedBeforeExecute);
// Check redo.
EXPECT_TRUE(CommandSystem::GetCommandManager()->Redo(result));
const AZStd::string serializedAfterRedo = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedAfterRedo = SerializePhysicsSetup(GetActor());
EXPECT_EQ(serializedAfterRedo, serializedAfterExecute);
}
@@ -13,9 +13,11 @@
#include <QtTest>
#include <Tests/UI/UIFixture.h>
#include <Tests/TestAssetCode/JackActor.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <EMotionFX/CommandSystem/Source/CommandManager.h>
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <EMotionFX/Source/ActorManager.h>
namespace EMotionFX
{
@@ -33,14 +35,12 @@ namespace EMotionFX
ASSERT_EQ(GetActorManager().GetNumActors(), 0);
// Load an Actor
const char* actorCmd{ "ImportActor -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor" };
{
AZStd::string result;
EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(actorCmd, result)) << result.c_str();
}
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
AZ::Data::Asset<Integration::ActorAsset> actorAsset =
TestActorAssets::CreateActorAssetAndRegister<JackNoMeshesActor>(actorAssetId, "Jack");
// Ensure the Actor is correct
ASSERT_TRUE(GetActorManager().FindActorByName("rinActor"));
ASSERT_TRUE(GetActorManager().FindActorByName("Jack"));
EXPECT_EQ(GetActorManager().GetNumActors(), 1);
}
} // namespace EMotionFX
@@ -8,6 +8,7 @@
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Components/TransformComponent.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/AnimGraphMotionNode.h>
#include <EMotionFX/Source/MotionSet.h>
#include <EMotionFX/Source/Motion.h>
@@ -9,6 +9,8 @@
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/ActorUpdateScheduler.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/MultiThreadScheduler.h>
#include <Tests/SystemComponentFixture.h>
#include <Tests/TestAssetCode/JackActor.h>
@@ -19,11 +19,13 @@
#include <EMotionFX/Source/AnimGraphEntryNode.h>
#include <EMotionFX/Source/AnimGraphHubNode.h>
#include <EMotionFX/Source/AnimGraphManager.h>
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphPlugin.h>
#include <EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/BlendGraphViewWidget.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/SimpleActors.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <EMotionFX/Source/MotionManager.h>
namespace EMotionFX
@@ -78,7 +80,8 @@ namespace EMotionFX
EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommandGroup(group, commandResult)) << commandResult.c_str();
// Create temp Actor
m_actor = ActorFactory::CreateAndInit<SimpleJointChainActor>(1, "tempActor");
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
m_actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 1, "tempActor");
// Cache some local poitners.
m_animGraphPlugin = static_cast<EMStudio::AnimGraphPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID));
@@ -90,6 +93,7 @@ namespace EMotionFX
void TearDown() override
{
GetEMotionFX().GetActorManager()->UnregisterAllActors();
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
delete m_animGraph;
UIFixture::TearDown();
@@ -104,7 +108,7 @@ namespace EMotionFX
AZStd::string m_entryNodeName = "testEntry";
AnimGraph* m_animGraph = nullptr;
EMStudio::AnimGraphPlugin* m_animGraphPlugin = nullptr;
AutoRegisteredActor m_actor;
AZ::Data::Asset<Integration::ActorAsset> m_actorAsset;
};
TEST_F(PopulatedAnimGraphFixture, CanActivateValidGraph)
@@ -27,7 +27,6 @@
#include <EMotionFX/Source/AnimGraphMotionNode.h>
#include <EMotionFX/Source/AnimGraphReferenceNode.h>
#include <EMotionFX/Source/AnimGraphStateMachine.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/Parameter/BoolParameter.h>
#include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
@@ -43,6 +42,7 @@
#include <Tests/TestAssetCode/SimpleActors.h>
#include <Tests/ProvidesUI/AnimGraph/SimpleAnimGraphUIFixture.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <Tests/Mocks/EventHandler.h>
namespace EMotionFX
@@ -423,7 +423,10 @@ namespace EMotionFX
using testing::Eq;
using testing::Not;
AutoRegisteredActor actor = EMotionFX::ActorFactory::CreateAndInit<EMotionFX::SimpleJointChainActor>(1);
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
AZ::Data::Asset<Integration::ActorAsset> actorAsset =
TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 1);
auto motionSet = AZStd::make_unique<EMotionFX::MotionSet>();
{
@@ -432,7 +435,7 @@ namespace EMotionFX
}
auto* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(0);
auto* actorInstance = EMotionFX::ActorInstance::Create(actor.get());
auto* actorInstance = EMotionFX::ActorInstance::Create(actorAsset->GetActor());
auto* animGraphInstance = EMotionFX::AnimGraphInstance::Create(animGraph, actorInstance, motionSet.get());
actorInstance->SetAnimGraphInstance(animGraphInstance);
@@ -24,12 +24,11 @@
#include <EMotionStudio/EMStudioSDK/Source/SaveChangedFilesManager.h>
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <Tests/TestAssetCode/SimpleActors.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <Tests/UI/ModalPopupHandler.h>
namespace EMotionFX
{
@@ -58,8 +57,10 @@ namespace EMotionFX
ASSERT_EQ(GetMotionManager().GetNumMotions(), 0) << "Expected exactly zero motions";
// Create Actor, AnimGraph, Motionset and Motion
AutoRegisteredActor actor = ActorFactory::CreateAndInit<SimpleJointChainActor>(2, "SampleActor");
ActorInstance::Create(actor.get());
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
AZ::Data::Asset<Integration::ActorAsset> actorAsset =
TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 2, "SampleActor");
ActorInstance::Create(actorAsset->GetActor());
{
AZStd::string result;
ASSERT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(createAnimGraphCmd, result)) << result.c_str();
@@ -26,6 +26,7 @@
#include <Tests/Mocks/PhysicsSystem.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/SimpleActors.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <Tests/UI/UIFixture.h>
namespace EMotionFX
@@ -79,7 +80,11 @@ namespace EMotionFX
TEST_F(CopyPasteRagdollCollidersFixture, CanCopyCollider)
#endif // AZ_TRAIT_DISABLE_FAILED_EMOTION_FX_EDITOR_TESTS
{
AutoRegisteredActor actor{ActorFactory::CreateAndInit<SimpleJointChainActor>(4)};
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
AZ::Data::Asset<Integration::ActorAsset> actorAsset =
TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 4);
const Actor* actor = actorAsset->GetActor();
const Physics::RagdollConfiguration& ragdollConfig = actor->GetPhysicsSetup()->GetRagdollConfig();
const Physics::CharacterColliderConfiguration& simulatedObjectConfig = actor->GetPhysicsSetup()->GetSimulatedObjectColliderConfig();
@@ -104,8 +109,7 @@ namespace EMotionFX
{
AZStd::string result;
EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(
"Select -actorId " + AZStd::to_string(actor->GetID()),
EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand("Select -actorId " + AZStd::to_string(actor->GetID()),
result))
<< result.c_str();
}
@@ -14,7 +14,6 @@
#include <EMotionFX/CommandSystem/Source/CommandManager.h>
#include <EMotionFX/CommandSystem/Source/RagdollCommands.h>
#include <EMotionFX/Source/AutoRegisteredActor.h>
#include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#include <Editor/Plugins/Ragdoll/RagdollJointLimitWidget.h>
#include <Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h>
@@ -25,6 +24,7 @@
#include <Tests/Mocks/PhysicsSystem.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/SimpleActors.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <Tests/UI/UIFixture.h>
namespace EMotionFX
@@ -76,7 +76,9 @@ namespace EMotionFX
return AZStd::make_unique<D6JointLimitConfiguration>();
});
AutoRegisteredActor actor {ActorFactory::CreateAndInit<SimpleJointChainActor>(4)};
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
AZ::Data::Asset<Integration::ActorAsset> actorAsset = TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 4);
const Actor* actor = actorAsset->GetActor();
{
AZStd::string result;
@@ -99,28 +99,28 @@ namespace EMotionFX
"l_hand",
};
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_shldr"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftShoulder
)
);
const AZStd::string serializedBeforeHandAdded = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeHandAdded = SerializePhysicsSetup(GetActor());
// Adding l_hand should add l_upArm and l_loArm as well
commandGroup.RemoveAllCommands();
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
const AZStd::string serializedAfterHandAdded = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedAfterHandAdded = SerializePhysicsSetup(GetActor());
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftHand
@@ -129,23 +129,23 @@ namespace EMotionFX
EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftShoulder
)
);
EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedBeforeHandAdded));
EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedBeforeHandAdded));
EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftHand
)
);
EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedAfterHandAdded));
EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedAfterHandAdded));
}
TEST_F(RagdollCommandTests, AddJointHigherInHierarchy)
@@ -166,10 +166,10 @@ namespace EMotionFX
"l_hand",
};
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftHand
@@ -178,10 +178,10 @@ namespace EMotionFX
// l_shldr should already be in the ragdoll, so adding it should do nothing
commandGroup.RemoveAllCommands();
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_shldr"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftHand
@@ -190,11 +190,11 @@ namespace EMotionFX
// Undo here undoes the addition of l_hand
EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
EXPECT_TRUE(GetRagdollJointNames(m_actor.get()).empty());
EXPECT_TRUE(GetRagdollJointNames(GetActor()).empty());
EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(
StrEq(),
jointsToLeftHand
@@ -221,16 +221,16 @@ namespace EMotionFX
};
// Add a joint to the ragdoll that does not make a chain all the way to the root
EXPECT_TRUE(commandManager.ExecuteCommand(aznew CommandAddRagdollJoint(m_actor->GetID(), "l_shldr"), result)) << result.c_str();
EXPECT_TRUE(commandManager.ExecuteCommand(aznew CommandAddRagdollJoint(GetActor()->GetID(), "l_shldr"), result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(StrEq(), AZStd::vector<AZStd::string>{"l_shldr"})
);
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(StrEq(), jointsToLeftHand)
);
}
@@ -250,17 +250,17 @@ namespace EMotionFX
};
// Add joints from the root to the left hand
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
// Removing the left shoulder should remove the elbow, wrist, and hand
// as well
commandGroup.RemoveAllCommands();
CommandRagdollHelpers::RemoveJointsFromRagdoll(m_actor->GetID(), {"l_shldr"}, &commandGroup);
CommandRagdollHelpers::RemoveJointsFromRagdoll(GetActor()->GetID(), {"l_shldr"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
EXPECT_THAT(
GetRagdollJointNames(m_actor.get()),
GetRagdollJointNames(GetActor()),
testing::UnorderedPointwise(StrEq(), jointsToSpine3)
);
}
@@ -271,24 +271,24 @@ namespace EMotionFX
CommandSystem::CommandManager commandManager;
MCore::CommandGroup commandGroup;
CommandRagdollHelpers::AddJointsToRagdoll(m_actor->GetID(), {"l_hand"}, &commandGroup);
CommandRagdollHelpers::AddJointsToRagdoll(GetActor()->GetID(), {"l_hand"}, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result)) << result.c_str();
const AZStd::string serializedBeforeSphereAdded = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedBeforeSphereAdded = SerializePhysicsSetup(GetActor());
CommandColliderHelpers::AddCollider(m_actor->GetID(), "l_hand",
CommandColliderHelpers::AddCollider(GetActor()->GetID(), "l_hand",
PhysicsSetup::Ragdoll, azrtti_typeid<Physics::SphereShapeConfiguration>());
const AZStd::string serializedAfterSphereAdded = SerializePhysicsSetup(m_actor.get());
const AZStd::string serializedAfterSphereAdded = SerializePhysicsSetup(GetActor());
EXPECT_THAT(serializedAfterSphereAdded, ::testing::Not(StrEq(serializedBeforeSphereAdded)));
EXPECT_TRUE(commandManager.Undo(result)) << result.c_str();
EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedBeforeSphereAdded));
EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedBeforeSphereAdded));
EXPECT_TRUE(commandManager.Redo(result)) << result.c_str();
EXPECT_THAT(SerializePhysicsSetup(m_actor.get()), StrEq(serializedAfterSphereAdded));
EXPECT_THAT(SerializePhysicsSetup(GetActor()), StrEq(serializedAfterSphereAdded));
}
} // namespace EMotionFX
@@ -55,25 +55,25 @@ namespace EMotionFX
CommandSystem::CommandManager commandManager;
MCore::CommandGroup commandGroup;
const uint32 actorId = m_actor->GetID();
const uint32 actorId = GetActor()->GetID();
const AZStd::vector<AZStd::string> jointNames = GetTestJointNames();
// 1. Add simulated object.
const AZStd::string serializedBeforeAdd = SerializeSimulatedObjectSetup(m_actor.get());
const AZStd::string serializedBeforeAdd = SerializeSimulatedObjectSetup(GetActor());
CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup);
CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup);
CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serializedAfterAdd = SerializeSimulatedObjectSetup(m_actor.get());
EXPECT_EQ(3, CountSimulatedObjects(m_actor.get()));
const AZStd::string serializedAfterAdd = SerializeSimulatedObjectSetup(GetActor());
EXPECT_EQ(3, CountSimulatedObjects(GetActor()));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(0, CountSimulatedObjects(m_actor.get()));
EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedObjects(GetActor()));
EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(3, CountSimulatedObjects(m_actor.get()));
EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(3, CountSimulatedObjects(GetActor()));
EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(GetActor()));
// 2. Remove simulated object.
commandGroup.RemoveAllCommands();
@@ -81,22 +81,22 @@ namespace EMotionFX
CommandSimulatedObjectHelpers::RemoveSimulatedObject(actorId, 0, &commandGroup);
CommandSimulatedObjectHelpers::RemoveSimulatedObject(actorId, 0, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
EXPECT_EQ(0, CountSimulatedObjects(m_actor.get()));
EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedObjects(GetActor()));
EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(GetActor()));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(3, CountSimulatedObjects(m_actor.get()));
EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(3, CountSimulatedObjects(GetActor()));
EXPECT_EQ(serializedAfterAdd, SerializeSimulatedObjectSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(0, CountSimulatedObjects(m_actor.get()));
EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedObjects(GetActor()));
EXPECT_EQ(serializedBeforeAdd, SerializeSimulatedObjectSetup(GetActor()));
// 3. Add simulated joints.
// 3.1 Add a simulated object first to put in the simulated joints.
commandGroup.RemoveAllCommands();
CommandSimulatedObjectHelpers::AddSimulatedObject(actorId);
const AZStd::string serialized3_1 = SerializeSimulatedObjectSetup(m_actor.get());
const AZStd::string serialized3_1 = SerializeSimulatedObjectSetup(GetActor());
// 3.2 Add simulated joints.
// Joint hierarchy as follow:
@@ -105,7 +105,7 @@ namespace EMotionFX
// --l_loLeg
// --l_ankle
// --l_ball
const Skeleton* skeleton = m_actor->GetSkeleton();
const Skeleton* skeleton = GetActor()->GetSkeleton();
const size_t l_upLegIdx = skeleton->FindNodeByName("l_upLeg")->GetNodeIndex();
const size_t l_upLegRollIdx = skeleton->FindNodeByName("l_upLegRoll")->GetNodeIndex();
const size_t l_loLegIdx = skeleton->FindNodeByName("l_loLeg")->GetNodeIndex();
@@ -113,33 +113,33 @@ namespace EMotionFX
const size_t l_ballIdx = skeleton->FindNodeByName("l_ball")->GetNodeIndex();
CommandSimulatedObjectHelpers::AddSimulatedJoints(actorId, {l_upLegIdx, l_upLegRollIdx, l_loLegIdx, l_ankleIdx, l_ballIdx}, 0, false, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serialized3_2 = SerializeSimulatedObjectSetup(m_actor.get());
EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(2, CountChildJoints(m_actor.get(), 0, l_upLegIdx));
EXPECT_EQ(0, CountChildJoints(m_actor.get(), 0, l_upLegRollIdx));
EXPECT_EQ(1, CountChildJoints(m_actor.get(), 0, l_loLegIdx));
const AZStd::string serialized3_2 = SerializeSimulatedObjectSetup(GetActor());
EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(2, CountChildJoints(GetActor(), 0, l_upLegIdx));
EXPECT_EQ(0, CountChildJoints(GetActor(), 0, l_upLegRollIdx));
EXPECT_EQ(1, CountChildJoints(GetActor(), 0, l_loLegIdx));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(GetActor()));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(GetActor()));
// 4 Remove simulated joints.
// 4.1 Test sparse chain.
EXPECT_EQ(1, CountRootJoints(m_actor.get(), 0));
EXPECT_EQ(1, CountRootJoints(GetActor(), 0));
commandGroup.RemoveAllCommands();
CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, {l_loLegIdx}, 0, false, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
EXPECT_EQ(2, CountRootJoints(m_actor.get(), 0));
EXPECT_EQ(2, CountRootJoints(GetActor(), 0));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(1, CountRootJoints(m_actor.get(), 0));
EXPECT_EQ(1, CountRootJoints(GetActor(), 0));
EXPECT_TRUE(commandManager.Redo(result));
EXPECT_EQ(2, CountRootJoints(m_actor.get(), 0));
EXPECT_EQ(2, CountRootJoints(GetActor(), 0));
EXPECT_TRUE(commandManager.Undo(result));
@@ -147,23 +147,23 @@ namespace EMotionFX
commandGroup.RemoveAllCommands();
CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, { l_upLegIdx, l_upLegRollIdx, l_loLegIdx, l_ankleIdx, l_ballIdx }, 0, false, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(GetActor()));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(GetActor()));
// 4.3 Test removing the root joint and children.
commandGroup.RemoveAllCommands();
CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, { l_upLegIdx }, 0, true, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serialized3_1, SerializeSimulatedObjectSetup(GetActor()));
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(5, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(5, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serialized3_2, SerializeSimulatedObjectSetup(GetActor()));
}
TEST_F(SimulatedObjectCommandTests, SimulatedObjectCommands_UndoRemoveJointTest)
@@ -172,35 +172,35 @@ namespace EMotionFX
CommandSystem::CommandManager commandManager;
MCore::CommandGroup commandGroup;
const uint32 actorId = m_actor->GetID();
const uint32 actorId = GetActor()->GetID();
const AZStd::vector<AZStd::string> jointNames = GetTestJointNames();
// 1. Add simulated object
CommandSimulatedObjectHelpers::AddSimulatedObject(actorId, AZStd::nullopt, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
const AZStd::string serializedBase = SerializeSimulatedObjectSetup(m_actor.get());
const AZStd::string serializedBase = SerializeSimulatedObjectSetup(GetActor());
const size_t simulatedObjectIndex = 0;
// 2. Add r_upLeg simulated joints
const Skeleton* skeleton = m_actor->GetSkeleton();
const Skeleton* skeleton = GetActor()->GetSkeleton();
const size_t r_upLegIdx = skeleton->FindNodeByName("r_upLeg")->GetNodeIndex();
const size_t r_loLegIdx = skeleton->FindNodeByName("r_loLeg")->GetNodeIndex();
CommandSimulatedObjectHelpers::AddSimulatedJoints(actorId, { r_upLegIdx, r_loLegIdx }, 0, false);
EXPECT_EQ(2, CountSimulatedJoints(m_actor.get(), 0));
const AZStd::string serializedUpLeg = SerializeSimulatedObjectSetup(m_actor.get());
EXPECT_EQ(2, CountSimulatedJoints(GetActor(), 0));
const AZStd::string serializedUpLeg = SerializeSimulatedObjectSetup(GetActor());
// 3. Remove the r_loLeg simulated joint
AZStd::vector<SimulatedJoint*> jointsToBeRemoved;
commandGroup.RemoveAllCommands();
CommandSimulatedObjectHelpers::RemoveSimulatedJoints(actorId, { r_upLegIdx }, simulatedObjectIndex, true, &commandGroup);
EXPECT_TRUE(commandManager.ExecuteCommandGroup(commandGroup, result));
EXPECT_EQ(0, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serializedBase, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(0, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serializedBase, SerializeSimulatedObjectSetup(GetActor()));
// 4. Undo
// This recreates r_loLeg and r_loLeg but won't add all other children recursively as only these two joints got aded in step 3.
EXPECT_TRUE(commandManager.Undo(result));
EXPECT_EQ(2, CountSimulatedJoints(m_actor.get(), 0));
EXPECT_EQ(serializedUpLeg, SerializeSimulatedObjectSetup(m_actor.get()));
EXPECT_EQ(2, CountSimulatedJoints(GetActor(), 0));
EXPECT_EQ(serializedUpLeg, SerializeSimulatedObjectSetup(GetActor()));
}
} // namespace EMotionFX
@@ -22,17 +22,21 @@
#include <Tests/UI/UIFixture.h>
#include <Tests/TestAssetCode/SimpleActors.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
namespace EMotionFX
{
using SimulatedObjectModelTestsFixture = UIFixture;
TEST_F(SimulatedObjectModelTestsFixture, CanUndoAddSimulatedObjectAndSimulatedJointWithChildren)
{
AutoRegisteredActor actor = ActorFactory::CreateAndInit<SimpleJointChainActor>(3, "simulatedObjectModelTestActor");
AZ::Data::AssetId actorAssetId("{5060227D-B6F4-422E-BF82-41AAC5F228A5}");
AZ::Data::Asset<Integration::ActorAsset> actorAsset =
TestActorAssets::CreateActorAssetAndRegister<SimpleJointChainActor>(actorAssetId, 3, "simulatedObjectModelTestActor");
const Actor* actor = actorAsset->GetActor();
EMotionFX::SimulatedObjectWidget* simulatedObjectWidget = static_cast<EMotionFX::SimulatedObjectWidget*>(EMStudio::GetPluginManager()->FindActivePlugin(EMotionFX::SimulatedObjectWidget::CLASS_ID));
ASSERT_TRUE(simulatedObjectWidget) << "Simulated Object plugin not loaded";
simulatedObjectWidget->ActorSelectionChanged(actor.get());
simulatedObjectWidget->ActorSelectionChanged(actorAsset->GetActor());
SimulatedObjectModel* model = simulatedObjectWidget->GetSimulatedObjectModel();
@@ -20,7 +20,7 @@ namespace EMotionFX
TEST_F(SimulatedObjectSerializeTests, SerializeTest)
{
SimulatedObjectSetup* setup = m_actor->GetSimulatedObjectSetup().get();
SimulatedObjectSetup* setup = GetActor()->GetSimulatedObjectSetup().get();
// Build some setup.
SimulatedObject* object = setup->AddSimulatedObject();
@@ -29,7 +29,7 @@ namespace EMotionFX
object->SetGravityFactor(3.0f);
object->SetStiffnessFactor(4.0f);
const AZStd::vector<AZStd::string> jointNames = { "l_upArm", "l_loArm", "l_hand" };
Skeleton* skeleton = m_actor->GetSkeleton();
Skeleton* skeleton = GetActor()->GetSkeleton();
for (const AZStd::string& name : jointNames)
{
size_t skeletonJointIndex;
@@ -50,7 +50,7 @@ namespace EMotionFX
object->GetSimulatedJoint(0)->SetPinned(true);
// Serialize it and deserialize it.
const AZStd::string serialized = SerializeSimulatedObjectSetup(m_actor.get());
const AZStd::string serialized = SerializeSimulatedObjectSetup(GetActor());
AZStd::unique_ptr<SimulatedObjectSetup> loadedSetup(DeserializeSimulatedObjectSetup(serialized));
// Verify some of the contents of the deserialized version.
@@ -22,13 +22,13 @@ namespace EMotionFX
void SetUp()
{
ActorFixture::SetUp();
m_actor->AddLODLevel();
GetActor()->AddLODLevel();
DisableJointsForLOD(m_disabledJointNames, 1);
}
void DisableJointsForLOD(const std::vector<std::string>& jointNames, size_t lodLevel)
{
const Skeleton* skeleton = m_actor->GetSkeleton();
const Skeleton* skeleton = GetActor()->GetSkeleton();
for (const std::string& jointName : jointNames)
{
Node* joint = skeleton->FindNodeByName(jointName.c_str());
@@ -10,9 +10,10 @@
#include <AzCore/std/containers/vector.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <EMotionFX/Source/Actor.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/Importer/Importer.h>
#include <Tests/TestAssetCode/TestActorAssets.h>
#include <Tests/TestAssetCode/ActorFactory.h>
#include <Tests/TestAssetCode/JackActor.h>
namespace EMotionFX
{

Some files were not shown because too many files have changed in this diff Show More