merge from latest

Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com>
This commit is contained in:
chcurran
2021-07-27 15:51:35 -07:00
127 changed files with 3093 additions and 1288 deletions
+12 -1
View File
@@ -463,7 +463,7 @@ void EditorViewportWidget::Update()
if (m_updateCameraPositionNextTick)
{
auto cameraState = m_renderViewport->GetCameraState();
auto cameraState = GetCameraState();
AZ::Matrix3x4 matrix;
matrix.SetBasisAndTranslation(cameraState.m_side, cameraState.m_forward, cameraState.m_up, cameraState.m_position);
auto m = AZMatrix3x4ToLYMatrix3x4(matrix);
@@ -1138,6 +1138,17 @@ void EditorViewportWidget::OnMenuSelectCurrentCamera()
AzFramework::CameraState EditorViewportWidget::GetCameraState()
{
if (m_viewEntityId.IsValid())
{
bool cameraStateAcquired = false;
AzFramework::CameraState cameraState;
Camera::EditorCameraViewRequestBus::BroadcastResult(cameraStateAcquired,
&Camera::EditorCameraViewRequestBus::Events::GetCameraState, cameraState);
if (cameraStateAcquired)
{
return cameraState;
}
}
return m_renderViewport->GetCameraState();
}
+1 -1
View File
@@ -31,7 +31,7 @@ public:
static void Record(IUndoObject* undo);
private:
static const uint32 scDescSize = 256;
static const AZ::u32 scDescSize = 256;
char m_description[scDescSize];
bool m_bCancelled;
bool m_bStartedRecord;
@@ -84,7 +84,7 @@ namespace AZ
else
{
AZ::Debug::Trace::Instance().Assert(__FILE__, __LINE__, AZ_FUNCTION_SIGNATURE,
"Bus has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads");
"Bus %s has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads", BusType::GetName());
}
}
@@ -71,7 +71,7 @@ namespace AZ
return &out;
}
Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist)
Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist, bool reverseDepth)
{
AZ_Assert(right > left, "right should be greater than left");
// valid to have matrix invert top/bottom and far/near
@@ -83,6 +83,11 @@ namespace AZ
return nullptr;
}
if (reverseDepth)
{
AZStd::swap(nearDist, farDist);
}
out.SetRow(0, 2.f/(right - left), 0.f, 0.f, - (right + left) / (right - left) );
out.SetRow(1, 0.f, 2.f / (top - bottom), 0.f, - (top + bottom) / (top - bottom) );
out.SetRow(2, 0.f, 0.f, 1 / (nearDist - farDist), nearDist / (nearDist - farDist) );
@@ -57,8 +57,9 @@ namespace AZ
//! @param top The y coordinate of top view-plane
//! @param near Distance to the near view-plane. Must be no less than zero.
//! @param far Distance to the far view-plane. Must be greater than zero.
//! @param reverseDepth Set to true to reverse depth which means near distance maps to 1 and far distance maps to 0.
//! @return Pointer of the output matrix
Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist);
Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist, bool reverseDepth = false);
//! Transforms a position by a matrix. This function can be used with any generic cases which include projection matrices.
Vector3 MatrixTransformPosition(const Matrix4x4& matrix, const Vector3& inPosition);
+1 -1
View File
@@ -12,7 +12,7 @@
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
if(LY_ENABLE_RAD_TELEMETRY)
if(LY_RAD_TELEMETRY_ENABLED)
set(AZ_CORE_RADTELEMETRY_FILES ${common_dir}/azcore_profile_telemetry_files.cmake)
set(AZ_CORE_RADTELEMETRY_PLATFORM_INCLUDES ${pal_dir}/profile_telemetry_platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
set(AZ_CORE_RADTELEMETRY_INCLUDE_DIRECTORIES ${common_dir})
@@ -12,6 +12,6 @@
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
if(LY_ENABLE_RAD_TELEMETRY)
if(LY_RAD_TELEMETRY_ENABLED)
set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY)
endif()
@@ -12,6 +12,6 @@
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
if(LY_ENABLE_RAD_TELEMETRY)
if(LY_RAD_TELEMETRY_ENABLED)
set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY)
endif()
@@ -12,6 +12,6 @@
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
if(LY_ENABLE_RAD_TELEMETRY)
if(LY_RAD_TELEMETRY_ENABLED)
set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY)
endif()
@@ -6,6 +6,6 @@
#
#
if(LY_ENABLE_RAD_TELEMETRY)
if(LY_RAD_TELEMETRY_ENABLED)
set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY)
endif()
@@ -63,6 +63,13 @@ namespace Camera
//! @return The camera frustum's height
virtual float GetFrustumHeight() = 0;
//! Gets whether or not the camera is using an orthographic projection.
//! @return True if the camera is using an orthographic projection, or false if the camera is using a perspective projection.
virtual bool IsOrthographic() = 0;
//! @return The half width of the orthographic projection, @see SetOrthographicHalfWidth.
virtual float GetOrthographicHalfWidth() = 0;
//! Sets the camera's field of view in degrees between 0 < fov < 180 degrees
//! @param fov The camera frustum's new field of view in degrees
virtual void SetFov(float fov)
@@ -95,6 +102,15 @@ namespace Camera
//! @param height The camera frustum's new height
virtual void SetFrustumHeight(float height) = 0;
//! Sets whether or not the camera should use an orthographic projection in place of a perspective projection.
//! @param orthographic If true, the camera will use an orthographic projection
virtual void SetOrthographic(bool orthographic) = 0;
//! Sets the half-width of the orthographic projection.
//! @params halfWidth Used to calculate the bounds of the projection while in orthographic mode.
//! The height is calculated automatically based on the aspect ratio.
virtual void SetOrthographicHalfWidth(float halfWidth) = 0;
//! Makes the camera the active view
virtual void MakeActiveView() = 0;
+2 -2
View File
@@ -10,7 +10,7 @@
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
set(LY_ENABLE_STATISTICAL_PROFILING OFF CACHE BOOL "Enables statistical profiling when using AZ_PROFILE_SCOPE. If True, it takes effect only if RAD Telemetry is disabled.")
set(LY_STATISTICAL_PROFILING_ENABLED OFF CACHE BOOL "Enables statistical profiling when using AZ_PROFILE_SCOPE. If True, it takes effect only if RAD Telemetry is disabled.")
set(LY_TOUCHBENDING_LAYER_BIT 63 CACHE STRING "Use TouchBending as the collision layer. The TouchBending layer can be a number from 1 to 63 (Default=63).")
ly_add_target(
@@ -38,7 +38,7 @@ ly_add_target(
3rdParty::lz4
)
if(LY_ENABLE_STATISTICAL_PROFILING)
if(LY_STATISTICAL_PROFILING_ENABLED)
ly_add_source_properties(
SOURCES AzFramework/Debug/StatisticalProfilerProxy.h
PROPERTY COMPILE_DEFINITIONS
@@ -9,8 +9,13 @@
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/EBus/EBus.h>
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <xcb/xcb.h>
#endif // LY_COMPILE_DEFINITIONS
namespace AzFramework
{
class LinuxLifecycleEvents
@@ -25,4 +30,31 @@ namespace AzFramework
using Bus = AZ::EBus<LinuxLifecycleEvents>;
};
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class LinuxXcbConnectionManager
{
public:
AZ_RTTI(LinuxXcbConnectionManager, "{649951316-3626-4C9D-9DCA-2E7ABF84C0A9}");
virtual ~LinuxXcbConnectionManager() = default;
virtual xcb_connection_t* GetXcbConnection() const = 0;
};
class LinuxXcbConnectionManagerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LinuxXcbConnectionManagerBus = AZ::EBus<LinuxXcbConnectionManager, LinuxXcbConnectionManagerBusTraits>;
using LinuxXcbConnectionManagerInterface = AZ::Interface<LinuxXcbConnectionManager>;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -12,6 +12,32 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class LinuxXcbConnectionManagerImpl
: public LinuxXcbConnectionManagerBus::Handler
{
public:
LinuxXcbConnectionManagerImpl()
{
m_xcbConnection = xcb_connect(nullptr, nullptr);
AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
LinuxXcbConnectionManagerBus::Handler::BusConnect();
}
~LinuxXcbConnectionManagerImpl()
{
LinuxXcbConnectionManagerBus::Handler::BusDisconnect();
xcb_disconnect(m_xcbConnection);
}
xcb_connection_t* GetXcbConnection() const override
{
return m_xcbConnection;
}
private:
xcb_connection_t* m_xcbConnection = nullptr;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class ApplicationLinux
: public Application::Implementation
@@ -27,6 +53,12 @@ namespace AzFramework
// Application::Implementation
void PumpSystemEventLoopOnce() override;
void PumpSystemEventLoopUntilEmpty() override;
private:
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
AZStd::unique_ptr<LinuxXcbConnectionManager> m_xcbConnectionManager;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
};
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -39,11 +71,26 @@ namespace AzFramework
ApplicationLinux::ApplicationLinux()
{
LinuxLifecycleEvents::Bus::Handler::BusConnect();
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
m_xcbConnectionManager = AZStd::make_unique<LinuxXcbConnectionManagerImpl>();
if (LinuxXcbConnectionManagerInterface::Get() == nullptr)
{
LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux::~ApplicationLinux()
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
{
LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
}
m_xcbConnectionManager.reset();
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
LinuxLifecycleEvents::Bus::Handler::BusDisconnect();
}
@@ -5,3 +5,30 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
# Based on the linux window manager trait, perform the appropriate additional build configurations
# Only 'xcb', 'wayland', and 'xlib' are recognized
if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
find_library(XCB_LIBRARY xcb)
set(LY_BUILD_DEPENDENCIES
PRIVATE
${XCB_LIBRARY}
)
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "xlib")
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB)
else()
message(FATAL_ERROR, "Linux Window Manager ${PAL_TRAIT_LINUX_WINDOW_MANAGER} is not recognized")
endif()
@@ -22,6 +22,7 @@ namespace AzNetworking
const AZ::TimeMs deltaTimeMs = currentTimeMs - m_lastLoggedTimeMs;
m_atoms[m_activeAtom].m_bytesTransmitted += byteCount;
m_atoms[m_activeAtom].m_packetsSent++;
m_atoms[m_activeAtom].m_timeAccumulatorMs += deltaTimeMs;
if (m_atoms[m_activeAtom].m_timeAccumulatorMs >= m_maxSampleTimeMs)
@@ -32,6 +33,11 @@ namespace AzNetworking
m_lastLoggedTimeMs = currentTimeMs;
}
void DatarateMetrics::LogPacketLost()
{
m_atoms[m_activeAtom].m_packetsLost++;
}
float DatarateMetrics::GetBytesPerSecond() const
{
const uint32_t sampleAtom = 1 - m_activeAtom;
@@ -47,6 +53,18 @@ namespace AzNetworking
return (bytesLogged * 1000.0f) / sampleTime; // (* 1000) to convert from bytes per millisecond to bytes per second
}
float DatarateMetrics::GetLossRatePercent() const
{
const uint32_t sampleAtom = 1 - m_activeAtom;
if (m_atoms[sampleAtom].m_packetsSent == 0)
{
return 0.0f;
}
return float(m_atoms[sampleAtom].m_packetsLost) / float(m_atoms[sampleAtom].m_packetsSent);
}
void ConnectionComputeRtt::LogPacketSent(PacketId packetId, AZ::TimeMs currentTimeMs)
{
for (uint32_t i = 0; i < MaxTrackableEntries; i++)
@@ -19,8 +19,10 @@ namespace AzNetworking
{
DatarateAtom() = default;
AZ::TimeMs m_timeAccumulatorMs = AZ::TimeMs{ 0 };
uint32_t m_bytesTransmitted = 0;
AZ::TimeMs m_timeAccumulatorMs = AZ::TimeMs{0};
uint32_t m_packetsSent = 0;
uint32_t m_packetsLost = 0;
};
//! @class DatarateMetrics
@@ -40,19 +42,26 @@ namespace AzNetworking
//! @param currentTimeMs current process time in milliseconds
void LogPacket(uint32_t byteCount, AZ::TimeMs currentTimeMs);
//! Invoked whenever a packet has determined to be lost.
void LogPacketLost();
//! Retrieve a sample of the datarate being incurred by this connection in bytes per second.
//! @return datarate for traffic sent to or from the connection in bytes per second
float GetBytesPerSecond() const;
//! Returns the estimated packet loss rate as a percentage of packets.
//! @return the estimated percentage loss rate
float GetLossRatePercent() const;
private:
//! Used internally to swap buffers used for metric gathering.
void SwapBuffers();
static constexpr AZ::TimeMs MaxSampleTimeMs = AZ::TimeMs{500};
static constexpr AZ::TimeMs MaxSampleTimeMs = AZ::TimeMs{ 2000 };
AZ::TimeMs m_maxSampleTimeMs = MaxSampleTimeMs;
AZ::TimeMs m_lastLoggedTimeMs = MaxSampleTimeMs;
AZ::TimeMs m_maxSampleTimeMs = MaxSampleTimeMs;
AZ::TimeMs m_lastLoggedTimeMs = MaxSampleTimeMs;
uint32_t m_activeAtom = 0;
DatarateAtom m_atoms[2];
};
@@ -69,7 +78,7 @@ namespace AzNetworking
ConnectionPacketEntry(PacketId packetId, AZ::TimeMs sendTimeMs);
PacketId m_packetId = InvalidPacketId;
AZ::TimeMs m_sendTimeMs = AZ::TimeMs{0};
AZ::TimeMs m_sendTimeMs = AZ::TimeMs{0};
};
//! @class ConnectionComputeRtt
@@ -100,8 +109,8 @@ namespace AzNetworking
private:
static constexpr uint32_t MaxTrackableEntries = 4;
static constexpr float InitialRoundTripTime = 0.1f; //< Start off with a 100 millisecond estimate for Rtt
static constexpr uint32_t MaxTrackableEntries = 8;
static constexpr float InitialRoundTripTime = 0.1f; //< Start off with a 100 millisecond estimate for Rtt
float m_roundTripTime = InitialRoundTripTime;
ConnectionPacketEntry m_entries[MaxTrackableEntries];
@@ -117,6 +126,11 @@ namespace AzNetworking
//! Resets all internal metrics to defaults.
void Reset();
void LogPacketSent(uint32_t byteCount, AZ::TimeMs currentTimeMs);
void LogPacketRecv(uint32_t byteCount, AZ::TimeMs currentTimeMs);
void LogPacketLost();
void LogPacketAcked();
uint32_t m_packetsSent = 0;
uint32_t m_packetsRecv = 0;
uint32_t m_packetsLost = 0;
@@ -40,4 +40,33 @@ namespace AzNetworking
{
*this = ConnectionMetrics();
}
inline void ConnectionMetrics::LogPacketSent(uint32_t byteCount, AZ::TimeMs currentTimeMs)
{
if (byteCount > 0)
{
m_packetsSent++;
}
m_sendDatarate.LogPacket(byteCount, currentTimeMs);
}
inline void ConnectionMetrics::LogPacketRecv(uint32_t byteCount, AZ::TimeMs currentTimeMs)
{
if (byteCount > 0)
{
m_packetsRecv++;
}
m_recvDatarate.LogPacket(byteCount, currentTimeMs);
}
inline void ConnectionMetrics::LogPacketLost()
{
m_packetsLost++;
m_sendDatarate.LogPacketLost();
}
inline void ConnectionMetrics::LogPacketAcked()
{
m_packetsAcked++;
}
}
@@ -95,11 +95,6 @@ namespace AzNetworking
//! @return the max transmission unit for this connection
virtual uint32_t GetConnectionMtu() const = 0;
//! Sets connection quality values for testing poor connection conditions.
//! Currently unsupported on TcpConnections
//! @param connectionQuality simulated connection quality values to use
virtual void SetConnectionQuality(const ConnectionQuality& connectionQuality) = 0;
//! Returns the connection identifier for this connection instance.
//! @return the connection identifier for this connection instance
ConnectionId GetConnectionId() const;
@@ -128,12 +123,23 @@ namespace AzNetworking
//! @return reference to the connection metric info
ConnectionMetrics& GetMetrics();
//! Retrieves debug connection quality settings.
//! Currently unsupported on TcpConnections
//! @return connection quality structure for this connection
const ConnectionQuality& GetConnectionQuality() const;
//! Retrieves debug connection quality settings, non-const.
//! Currently unsupported on TcpConnections
//! @return connection quality structure for this connection
ConnectionQuality& GetConnectionQuality();
private:
// The following data members are here in the interface for performance reasons
ConnectionId m_connectionId = InvalidConnectionId;
IpAddress m_remoteAddress;
ConnectionMetrics m_connectionMetrics;
ConnectionQuality m_connectionQuality;
void* m_userData = nullptr;
};
}
@@ -59,4 +59,14 @@ namespace AzNetworking
{
return m_connectionMetrics;
}
inline const ConnectionQuality& IConnection::GetConnectionQuality() const
{
return m_connectionQuality;
}
inline ConnectionQuality& IConnection::GetConnectionQuality()
{
return m_connectionQuality;
}
}
@@ -122,7 +122,7 @@ namespace AzNetworking
bool TcpConnection::UpdateRecv()
{
const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
GetMetrics().m_recvDatarate.LogPacket(0, startTimeMs);
GetMetrics().LogPacketRecv(0, startTimeMs);
// Read new data off the input socket
{
@@ -261,11 +261,6 @@ namespace AzNetworking
return 0; // do nothing, unsupported on TCP connections
}
void TcpConnection::SetConnectionQuality([[maybe_unused]] const ConnectionQuality& connectionQuality)
{
; // do nothing, unsupported on TCP connections
}
bool TcpConnection::SendPacketInternal(PacketType packetType, TcpPacketEncodingBuffer& payloadBuffer, AZ::TimeMs currentTimeMs)
{
AZ_Assert(payloadBuffer.GetCapacity() < AZStd::numeric_limits<uint16_t>::max(), "Buffer capacity should be representable using 2 bytes or less");
@@ -333,8 +328,7 @@ namespace AzNetworking
}
m_sendRingbuffer.AdvanceWriteBuffer(headerSize + payloadSize);
GetMetrics().m_packetsSent++;
GetMetrics().m_sendDatarate.LogPacket(headerSize + payloadSize, currentTimeMs);
GetMetrics().LogPacketSent(headerSize + payloadSize, currentTimeMs);
m_networkInterface.GetMetrics().m_sendPackets++;
UpdateSend();
return true;
@@ -379,8 +373,7 @@ namespace AzNetworking
memcpy(dstData, srcData, packetSize);
m_recvRingbuffer.AdvanceReadBuffer(serializer.GetReadSize() + packetSize);
GetMetrics().m_packetsRecv++;
GetMetrics().m_recvDatarate.LogPacket(packetSize, currentTimeMs);
GetMetrics().LogPacketRecv(packetSize, currentTimeMs);
m_networkInterface.GetMetrics().m_recvPackets++;
return true;
}
@@ -102,7 +102,6 @@ namespace AzNetworking
bool Disconnect(DisconnectReason reason, TerminationEndpoint endpoint) override;
void SetConnectionMtu(uint32_t connectionMtu) override;
uint32_t GetConnectionMtu() const override;
void SetConnectionQuality(const ConnectionQuality& connectionQuality) override;
// @}
//! Sets the registered socket file descriptor for this TcpConnection in the associated ConnectionSet instance.
@@ -152,7 +152,7 @@ namespace AzNetworking
void UdpConnection::ProcessAcked(PacketId packetId, AZ::TimeMs currentTimeMs)
{
GetMetrics().m_packetsAcked++;
GetMetrics().LogPacketAcked();
m_reliableQueue.OnPacketAcked(m_networkInterface, *this, packetId);
// Compute Rtt adjustments
@@ -172,8 +172,7 @@ namespace AzNetworking
GetMetrics().m_connectionRtt.LogPacketSent(packetId, currentTimeMs);
}
GetMetrics().m_packetsSent++;
GetMetrics().m_sendDatarate.LogPacket(packetSize, currentTimeMs);
GetMetrics().LogPacketSent(packetSize, currentTimeMs);
m_lastSentPacketMs = currentTimeMs;
m_unackedPacketCount = 0;
}
@@ -193,7 +192,7 @@ namespace AzNetworking
return PacketTimeoutResult::Acked;
case PacketAckState::Nacked:
GetMetrics().m_packetsLost++;
GetMetrics().LogPacketLost();
if (reliability == ReliabilityType::Reliable)
{
m_reliableQueue.OnPacketLost(m_networkInterface, *this, packetId);
@@ -224,8 +223,7 @@ namespace AzNetworking
return false;
}
GetMetrics().m_packetsRecv++;
GetMetrics().m_recvDatarate.LogPacket(packetSize, currentTimeMs);
GetMetrics().LogPacketRecv(packetSize, currentTimeMs);
if (header.GetIsReliable() && !m_reliableQueue.OnPacketReceived(header))
{
@@ -66,13 +66,8 @@ namespace AzNetworking
bool Disconnect(DisconnectReason reason, TerminationEndpoint endpoint) override;
void SetConnectionMtu(uint32_t connectionMtu) override;
uint32_t GetConnectionMtu() const override;
void SetConnectionQuality(const ConnectionQuality& connectionQuality) override;
// @}
//! Gets connection quality values for testing poor connection conditions.
//! @return connection quality values for this IConnection instance
const ConnectionQuality& GetConnectionQuality() const;
//! Returns a suitable encryption endpoint for this connection type.
//! @return reference to the connections encryption endpoint
DtlsEndpoint& GetDtlsEndpoint();
@@ -146,8 +141,6 @@ namespace AzNetworking
UdpFragmentQueue m_fragmentQueue;
ConnectionState m_state = ConnectionState::Disconnected;
ConnectionRole m_connectionRole = ConnectionRole::Connector;
ConnectionQuality m_connectionQuality;
DtlsEndpoint m_dtlsEndpoint;
AZ::TimeMs m_lastSentPacketMs;
@@ -160,4 +153,3 @@ namespace AzNetworking
}
#include <AzNetworking/UdpTransport/UdpConnection.inl>
@@ -10,16 +10,6 @@
namespace AzNetworking
{
inline void UdpConnection::SetConnectionQuality(const ConnectionQuality& connectionQuality)
{
m_connectionQuality = connectionQuality;
}
inline const ConnectionQuality& UdpConnection::GetConnectionQuality() const
{
return m_connectionQuality;
}
inline DtlsEndpoint& UdpConnection::GetDtlsEndpoint()
{
return m_dtlsEndpoint;
@@ -224,8 +224,7 @@ namespace AzNetworking
continue;
}
connection->GetMetrics().m_recvDatarate.LogPacket(packet.m_receivedBytes + UdpPacketHeaderSize, currentTimeMs);
connection->GetMetrics().m_packetsRecv++;
connection->GetMetrics().LogPacketRecv(packet.m_receivedBytes + UdpPacketHeaderSize, currentTimeMs);
// Decode the packet flag bitset first since it's always uncompressed
UdpPacketHeader header;
@@ -126,7 +126,7 @@ namespace AzNetworking
#ifdef ENABLE_LATENCY_DEBUG
if (connectionQuality.m_lossPercentage > 0)
{
if (int32_t(m_random.GetRandom() % 100) < (connectionQuality.m_lossPercentage / 2))
if (int32_t(m_random.GetRandom() % 100) < (connectionQuality.m_lossPercentage))
{
// Pretend we sent, but don't actually send
return true;
@@ -157,9 +157,11 @@ namespace AzNetworking
#ifdef ENABLE_LATENCY_DEBUG
else if ((connectionQuality.m_latencyMs > AZ::TimeMs{ 0 }) || (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 }))
{
const AZ::TimeMs jitterMs = aznumeric_cast<AZ::TimeMs>(m_random.GetRandom()) % (connectionQuality.m_varianceMs / aznumeric_cast<AZ::TimeMs>(2));
const AZ::TimeMs jitterMs = aznumeric_cast<AZ::TimeMs>(m_random.GetRandom()) % (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 }
? connectionQuality.m_varianceMs
: AZ::TimeMs{ 1 });
const AZ::TimeMs currTimeMs = AZ::GetElapsedTimeMs();
const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs / aznumeric_cast<AZ::TimeMs>(2)) + jitterMs;
const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs) + jitterMs;
DeferredData deferred = DeferredData(address, data, size, encrypt, dtlsEndpoint);
AZ::Interface<AZ::IEventScheduler>::Get()->AddCallback([&, deferredData = deferred]
@@ -102,7 +102,7 @@ namespace Camera
using EditorCameraNotificationBus = AZ::EBus<EditorCameraNotifications>;
/**
* This bus is for requesting any camera-view-related changes
* This bus is for requesting any camera-view-related changes or information
*/
class EditorCameraViewRequests : public AZ::ComponentBus
{
@@ -115,6 +115,11 @@ namespace Camera
* Sets this camera as the active view in the scene, otherwise restores the default editor camera if it was already active
*/
virtual void ToggleCameraAsActiveView() = 0;
/**
* Gets the camera state associated with this view.
*/
virtual bool GetCameraState(AzFramework::CameraState& cameraState) = 0;
};
using EditorCameraViewRequestBus = AZ::EBus<EditorCameraViewRequests>;
@@ -87,7 +87,7 @@ namespace AZ
// AssImp only has one bitangentStream per mesh.
bitangentStream->SetBitangentSetIndex(0);
bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene);
bitangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
bitangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
@@ -89,7 +89,7 @@ namespace AZ
// AssImp only has one tangentStream per mesh.
tangentStream->SetTangentSetIndex(0);
tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene);
tangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
tangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
@@ -17,32 +17,24 @@ namespace AZ
class Vector3;
}
namespace AZ
namespace AZ::SceneAPI::DataTypes
{
namespace SceneAPI
class IMeshVertexBitangentData
: public IGraphObject
{
namespace DataTypes
{
public:
AZ_RTTI(IMeshVertexBitangentData, "{6C8F6109-B0BD-49D1-A998-4A4946557DF9}", IGraphObject);
class IMeshVertexBitangentData
: public IGraphObject
{
public:
AZ_RTTI(IMeshVertexBitangentData, "{6C8F6109-B0BD-49D1-A998-4A4946557DF9}", IGraphObject);
virtual ~IMeshVertexBitangentData() override = default;
virtual ~IMeshVertexBitangentData() override = default;
void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {}
void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {}
virtual size_t GetCount() const = 0;
virtual const AZ::Vector3& GetBitangent(size_t index) const = 0;
virtual void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) = 0;
virtual void SetBitangentSetIndex(size_t setIndex) = 0;
virtual size_t GetBitangentSetIndex() const = 0;
virtual TangentSpace GetTangentSpace() const = 0;
virtual void SetTangentSpace(TangentSpace space) = 0;
};
} // DataTypes
} // SceneAPI
} // AZ
virtual size_t GetCount() const = 0;
virtual const AZ::Vector3& GetBitangent(size_t index) const = 0;
virtual void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) = 0;
virtual void SetBitangentSetIndex(size_t setIndex) = 0;
virtual size_t GetBitangentSetIndex() const = 0;
virtual TangentGenerationMethod GetGenerationMethod() const = 0;
virtual void SetGenerationMethod(TangentGenerationMethod method) = 0;
};
} // AZ::SceneAPI::DataTypes
@@ -16,42 +16,36 @@ namespace AZ
class Vector4;
}
namespace AZ
namespace AZ::SceneAPI::DataTypes
{
namespace SceneAPI
enum class TangentGenerationMethod
{
namespace DataTypes
{
enum class TangentSpace
{
FromSourceScene = 0,
MikkT = 1
};
FromSourceScene = 0,
MikkT = 1
};
enum class BitangentMethod
{
UseFromTangentSpace = 0,
Orthogonal = 1
};
enum class MikkTSpaceMethod
{
TSpace = 0,
TSpaceBasic = 1
};
class IMeshVertexTangentData
: public IGraphObject
{
public:
AZ_RTTI(IMeshVertexTangentData, "{B24084FF-09B1-4EE5-BA5B-2D392E92ECC1}", IGraphObject);
class IMeshVertexTangentData
: public IGraphObject
{
public:
AZ_RTTI(IMeshVertexTangentData, "{B24084FF-09B1-4EE5-BA5B-2D392E92ECC1}", IGraphObject);
virtual ~IMeshVertexTangentData() override = default;
virtual ~IMeshVertexTangentData() override = default;
void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {}
void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {}
virtual size_t GetCount() const = 0;
virtual const AZ::Vector4& GetTangent(size_t index) const = 0;
virtual void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) = 0;
virtual void SetTangentSetIndex(size_t setIndex) = 0;
virtual size_t GetTangentSetIndex() const = 0;
virtual TangentSpace GetTangentSpace() const = 0;
virtual void SetTangentSpace(TangentSpace space) = 0;
};
} // DataTypes
} // SceneAPI
} // AZ
virtual size_t GetCount() const = 0;
virtual const AZ::Vector4& GetTangent(size_t index) const = 0;
virtual void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) = 0;
virtual void SetTangentSetIndex(size_t setIndex) = 0;
virtual size_t GetTangentSetIndex() const = 0;
virtual TangentGenerationMethod GetGenerationMethod() const = 0;
virtual void SetGenerationMethod(TangentGenerationMethod method) = 0;
};
} // AZ::SceneAPI::DataTypes
@@ -10,110 +10,95 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace AZ
namespace AZ::SceneData::GraphData
{
namespace SceneData
void MeshVertexBitangentData::Reflect(ReflectContext* context)
{
namespace GraphData
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
void MeshVertexBitangentData::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MeshVertexBitangentData>()->Version(2);
}
serializeContext->Class<MeshVertexBitangentData>()->Version(2);
}
BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<MeshVertexBitangentData>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("GetCount", &MeshVertexBitangentData::GetCount)
->Method("GetBitangent", &MeshVertexBitangentData::GetBitangent)
->Method("GetBitangentSetIndex", &MeshVertexBitangentData::GetBitangentSetIndex)
->Method("GetTangentSpace", &MeshVertexBitangentData::GetTangentSpace)
->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromSourceScene>("FromSourceScene")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::MikkT>("MikkT");
}
}
BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<MeshVertexBitangentData>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("GetCount", &MeshVertexBitangentData::GetCount)
->Method("GetBitangent", &MeshVertexBitangentData::GetBitangent)
->Method("GetBitangentSetIndex", &MeshVertexBitangentData::GetBitangentSetIndex)
->Method("GetGenerationMethod", &MeshVertexBitangentData::GetGenerationMethod)
->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene>("FromSourceScene")
->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::MikkT>("MikkT");
}
}
void MeshVertexBitangentData::CloneAttributesFrom(const IGraphObject* sourceObject)
{
IMeshVertexBitangentData::CloneAttributesFrom(sourceObject);
if (const auto* typedSource = azrtti_cast<const MeshVertexBitangentData*>(sourceObject))
{
SetTangentSpace(typedSource->GetTangentSpace());
SetBitangentSetIndex(typedSource->GetBitangentSetIndex());
}
}
void MeshVertexBitangentData::CloneAttributesFrom(const IGraphObject* sourceObject)
{
IMeshVertexBitangentData::CloneAttributesFrom(sourceObject);
if (const auto* typedSource = azrtti_cast<const MeshVertexBitangentData*>(sourceObject))
{
SetGenerationMethod(typedSource->GetGenerationMethod());
SetBitangentSetIndex(typedSource->GetBitangentSetIndex());
}
}
size_t MeshVertexBitangentData::GetCount() const
{
return m_bitangents.size();
}
size_t MeshVertexBitangentData::GetCount() const
{
return m_bitangents.size();
}
const AZ::Vector3& MeshVertexBitangentData::GetBitangent(size_t index) const
{
AZ_Assert(index < m_bitangents.size(), "Invalid index %i for mesh bitangents.", index);
return m_bitangents[index];
}
const AZ::Vector3& MeshVertexBitangentData::GetBitangent(size_t index) const
{
AZ_Assert(index < m_bitangents.size(), "Invalid index %i for mesh bitangents.", index);
return m_bitangents[index];
}
void MeshVertexBitangentData::ReserveContainerSpace(size_t numVerts)
{
m_bitangents.reserve(numVerts);
}
void MeshVertexBitangentData::Resize(size_t numVerts)
{
m_bitangents.resize(numVerts);
}
void MeshVertexBitangentData::ReserveContainerSpace(size_t numVerts)
{
m_bitangents.reserve(numVerts);
}
void MeshVertexBitangentData::AppendBitangent(const AZ::Vector3& bitangent)
{
m_bitangents.push_back(bitangent);
}
void MeshVertexBitangentData::SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent)
{
m_bitangents[vertexIndex] = bitangent;
}
void MeshVertexBitangentData::Resize(size_t numVerts)
{
m_bitangents.resize(numVerts);
}
void MeshVertexBitangentData::SetBitangentSetIndex(size_t setIndex)
{
m_setIndex = setIndex;
}
size_t MeshVertexBitangentData::GetBitangentSetIndex() const
{
return m_setIndex;
}
void MeshVertexBitangentData::AppendBitangent(const AZ::Vector3& bitangent)
{
m_bitangents.push_back(bitangent);
}
AZ::SceneAPI::DataTypes::TangentGenerationMethod MeshVertexBitangentData::GetGenerationMethod() const
{
return m_generationMethod;
}
void MeshVertexBitangentData::SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method)
{
m_generationMethod = method;
}
void MeshVertexBitangentData::SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent)
{
m_bitangents[vertexIndex] = bitangent;
}
void MeshVertexBitangentData::SetBitangentSetIndex(size_t setIndex)
{
m_setIndex = setIndex;
}
size_t MeshVertexBitangentData::GetBitangentSetIndex() const
{
return m_setIndex;
}
AZ::SceneAPI::DataTypes::TangentSpace MeshVertexBitangentData::GetTangentSpace() const
{
return m_tangentSpace;
}
void MeshVertexBitangentData::SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space)
{
m_tangentSpace = space;
}
void MeshVertexBitangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const
{
output.Write("Bitangents", m_bitangents);
output.Write("TangentSpace", aznumeric_cast<int64_t>(m_tangentSpace));
}
} // GraphData
} // SceneData
} // AZ
void MeshVertexBitangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const
{
output.Write("Bitangents", m_bitangents);
output.Write("GenerationMethod", aznumeric_cast<int64_t>(m_generationMethod));
}
} // AZ::SceneData::GraphData
@@ -10,51 +10,41 @@
#include <AzCore/Math/Vector3.h>
#include <AzCore/std/containers/vector.h>
#include <SceneAPI/SceneData/SceneDataConfiguration.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h>
namespace AZ
namespace AZ::SceneData::GraphData
{
namespace SceneData
class SCENE_DATA_CLASS MeshVertexBitangentData
: public AZ::SceneAPI::DataTypes::IMeshVertexBitangentData
{
namespace GraphData
{
public:
AZ_RTTI(MeshVertexBitangentData, "{F56FB088-4C92-4453-AFE9-4E820F03FA90}", AZ::SceneAPI::DataTypes::IMeshVertexBitangentData);
class SCENE_DATA_CLASS MeshVertexBitangentData
: public AZ::SceneAPI::DataTypes::IMeshVertexBitangentData
{
public:
AZ_RTTI(MeshVertexBitangentData, "{F56FB088-4C92-4453-AFE9-4E820F03FA90}", AZ::SceneAPI::DataTypes::IMeshVertexBitangentData);
static void Reflect(ReflectContext* context);
static void Reflect(ReflectContext* context);
SCENE_DATA_API ~MeshVertexBitangentData() override = default;
SCENE_DATA_API ~MeshVertexBitangentData() override = default;
SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override;
SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override;
SCENE_DATA_API size_t GetCount() const override;
SCENE_DATA_API const AZ::Vector3& GetBitangent(size_t index) const override;
SCENE_DATA_API void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) override;
SCENE_DATA_API size_t GetCount() const override;
SCENE_DATA_API const AZ::Vector3& GetBitangent(size_t index) const override;
SCENE_DATA_API void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) override;
SCENE_DATA_API void SetBitangentSetIndex(size_t setIndex) override;
SCENE_DATA_API size_t GetBitangentSetIndex() const override;
SCENE_DATA_API void SetBitangentSetIndex(size_t setIndex) override;
SCENE_DATA_API size_t GetBitangentSetIndex() const override;
SCENE_DATA_API void Resize(size_t numVerts);
SCENE_DATA_API void ReserveContainerSpace(size_t numVerts);
SCENE_DATA_API void AppendBitangent(const AZ::Vector3& bitangent);
SCENE_DATA_API void Resize(size_t numVerts);
SCENE_DATA_API void ReserveContainerSpace(size_t numVerts);
SCENE_DATA_API void AppendBitangent(const AZ::Vector3& bitangent);
SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentGenerationMethod GetGenerationMethod() const override;
SCENE_DATA_API void SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method) override;
SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpace() const override;
SCENE_DATA_API void SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space) override;
SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
protected:
AZStd::vector<AZ::Vector3> m_bitangents;
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene;
size_t m_setIndex = 0;
};
} // GraphData
} // SceneData
} // AZ
SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
protected:
AZStd::vector<AZ::Vector3> m_bitangents;
AZ::SceneAPI::DataTypes::TangentGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene;
size_t m_setIndex = 0;
};
} // AZ::SceneData::GraphData
@@ -10,112 +10,96 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace AZ
namespace AZ::SceneData::GraphData
{
namespace SceneData
void MeshVertexTangentData::Reflect(ReflectContext* context)
{
namespace GraphData
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
void MeshVertexTangentData::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MeshVertexTangentData>()->Version(2);
}
serializeContext->Class<MeshVertexTangentData>()->Version(2);
}
BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<MeshVertexTangentData>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("GetCount", &MeshVertexTangentData::GetCount)
->Method("GetTangent", &MeshVertexTangentData::GetTangent)
->Method("GetTangentSetIndex", &MeshVertexTangentData::GetTangentSetIndex)
->Method("GetTangentSpace", &MeshVertexTangentData::GetTangentSpace)
->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromSourceScene>("FromSourceScene")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::MikkT>("MikkT");
}
}
BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<MeshVertexTangentData>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
->Method("GetCount", &MeshVertexTangentData::GetCount)
->Method("GetTangent", &MeshVertexTangentData::GetTangent)
->Method("GetTangentSetIndex", &MeshVertexTangentData::GetTangentSetIndex)
->Method("GetGenerationMethod", &MeshVertexTangentData::GetGenerationMethod)
->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene>("FromSourceScene")
->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::MikkT>("MikkT");
}
}
void MeshVertexTangentData::CloneAttributesFrom(const IGraphObject* sourceObject)
{
IMeshVertexTangentData::CloneAttributesFrom(sourceObject);
if (const auto* typedSource = azrtti_cast<const MeshVertexTangentData*>(sourceObject))
{
SetTangentSpace(typedSource->GetTangentSpace());
SetTangentSetIndex(typedSource->GetTangentSetIndex());
}
}
void MeshVertexTangentData::CloneAttributesFrom(const IGraphObject* sourceObject)
{
IMeshVertexTangentData::CloneAttributesFrom(sourceObject);
if (const auto* typedSource = azrtti_cast<const MeshVertexTangentData*>(sourceObject))
{
SetGenerationMethod(typedSource->GetGenerationMethod());
SetTangentSetIndex(typedSource->GetTangentSetIndex());
}
}
size_t MeshVertexTangentData::GetCount() const
{
return m_tangents.size();
}
size_t MeshVertexTangentData::GetCount() const
{
return m_tangents.size();
}
const AZ::Vector4& MeshVertexTangentData::GetTangent(size_t index) const
{
AZ_Assert(index < m_tangents.size(), "Invalid index %i for mesh tangents.", index);
return m_tangents[index];
}
const AZ::Vector4& MeshVertexTangentData::GetTangent(size_t index) const
{
AZ_Assert(index < m_tangents.size(), "Invalid index %i for mesh tangents.", index);
return m_tangents[index];
}
void MeshVertexTangentData::ReserveContainerSpace(size_t numVerts)
{
m_tangents.reserve(numVerts);
}
void MeshVertexTangentData::Resize(size_t numVerts)
{
m_tangents.resize(numVerts);
}
void MeshVertexTangentData::ReserveContainerSpace(size_t numVerts)
{
m_tangents.reserve(numVerts);
}
void MeshVertexTangentData::AppendTangent(const AZ::Vector4& tangent)
{
m_tangents.push_back(tangent);
}
void MeshVertexTangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const
{
output.Write("Tangents", m_tangents);
output.Write("GenerationMethod", aznumeric_cast<int64_t>(m_generationMethod));
output.Write("SetIndex", aznumeric_cast<uint64_t>(m_setIndex));
}
void MeshVertexTangentData::Resize(size_t numVerts)
{
m_tangents.resize(numVerts);
}
void MeshVertexTangentData::SetTangent(size_t vertexIndex, const AZ::Vector4& tangent)
{
m_tangents[vertexIndex] = tangent;
}
void MeshVertexTangentData::SetTangentSetIndex(size_t setIndex)
{
m_setIndex = setIndex;
}
void MeshVertexTangentData::AppendTangent(const AZ::Vector4& tangent)
{
m_tangents.push_back(tangent);
}
size_t MeshVertexTangentData::GetTangentSetIndex() const
{
return m_setIndex;
}
void MeshVertexTangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const
{
output.Write("Tangents", m_tangents);
output.Write("TangentSpace", aznumeric_cast<int64_t>(m_tangentSpace));
output.Write("SetIndex", aznumeric_cast<uint64_t>(m_setIndex));
}
AZ::SceneAPI::DataTypes::TangentGenerationMethod MeshVertexTangentData::GetGenerationMethod() const
{
return m_generationMethod;
}
void MeshVertexTangentData::SetTangent(size_t vertexIndex, const AZ::Vector4& tangent)
{
m_tangents[vertexIndex] = tangent;
}
void MeshVertexTangentData::SetTangentSetIndex(size_t setIndex)
{
m_setIndex = setIndex;
}
size_t MeshVertexTangentData::GetTangentSetIndex() const
{
return m_setIndex;
}
AZ::SceneAPI::DataTypes::TangentSpace MeshVertexTangentData::GetTangentSpace() const
{
return m_tangentSpace;
}
void MeshVertexTangentData::SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space)
{
m_tangentSpace = space;
}
} // GraphData
} // SceneData
} // AZ
void MeshVertexTangentData::SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method)
{
m_generationMethod = method;
}
} // AZ::SceneData::GraphData
@@ -14,46 +14,39 @@
#include <SceneAPI/SceneData/SceneDataConfiguration.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h>
namespace AZ
namespace AZ::SceneData::GraphData
{
namespace SceneData
class SCENE_DATA_CLASS MeshVertexTangentData
: public AZ::SceneAPI::DataTypes::IMeshVertexTangentData
{
namespace GraphData
{
public:
AZ_RTTI(MeshVertexTangentData, "{C16F0F38-8F8F-45A2-A33B-F2758922A7C4}", AZ::SceneAPI::DataTypes::IMeshVertexTangentData);
class SCENE_DATA_CLASS MeshVertexTangentData
: public AZ::SceneAPI::DataTypes::IMeshVertexTangentData
{
public:
AZ_RTTI(MeshVertexTangentData, "{C16F0F38-8F8F-45A2-A33B-F2758922A7C4}", AZ::SceneAPI::DataTypes::IMeshVertexTangentData);
static void Reflect(ReflectContext* context);
static void Reflect(ReflectContext* context);
SCENE_DATA_API ~MeshVertexTangentData() override = default;
SCENE_DATA_API ~MeshVertexTangentData() override = default;
SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override;
SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override;
SCENE_DATA_API size_t GetCount() const override;
SCENE_DATA_API const AZ::Vector4& GetTangent(size_t index) const override;
SCENE_DATA_API void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) override;
SCENE_DATA_API size_t GetCount() const override;
SCENE_DATA_API const AZ::Vector4& GetTangent(size_t index) const override;
SCENE_DATA_API void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) override;
SCENE_DATA_API void SetTangentSetIndex(size_t setIndex) override;
SCENE_DATA_API size_t GetTangentSetIndex() const override;
SCENE_DATA_API void SetTangentSetIndex(size_t setIndex) override;
SCENE_DATA_API size_t GetTangentSetIndex() const override;
SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentGenerationMethod GetGenerationMethod() const override;
SCENE_DATA_API void SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method) override;
SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpace() const override;
SCENE_DATA_API void SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space) override;
SCENE_DATA_API void Resize(size_t numVerts);
SCENE_DATA_API void ReserveContainerSpace(size_t numVerts);
SCENE_DATA_API void AppendTangent(const AZ::Vector4& tangent);
SCENE_DATA_API void Resize(size_t numVerts);
SCENE_DATA_API void ReserveContainerSpace(size_t numVerts);
SCENE_DATA_API void AppendTangent(const AZ::Vector4& tangent);
SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
protected:
AZStd::vector<AZ::Vector4> m_tangents;
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene;
size_t m_setIndex = 0;
};
} // GraphData
} // SceneData
} // AZ
protected:
AZStd::vector<AZ::Vector4> m_tangents;
AZ::SceneAPI::DataTypes::TangentGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene;
size_t m_setIndex = 0;
};
} // AZ::SceneData::GraphData
@@ -26,13 +26,22 @@ namespace AZ
{
TangentsRule::TangentsRule()
: DataTypes::IRule()
, m_tangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::MikkT)
{
}
AZ::SceneAPI::DataTypes::TangentSpace TangentsRule::GetTangentSpace() const
AZ::SceneAPI::DataTypes::TangentGenerationMethod TangentsRule::GetGenerationMethod() const
{
return m_tangentSpace;
return m_generationMethod;
}
AZ::SceneAPI::DataTypes::MikkTSpaceMethod TangentsRule::GetMikkTSpaceMethod() const
{
return m_tSpaceMethod;
}
AZ::Crc32 TangentsRule::GetSpaceMethodVisibility() const
{
return (m_generationMethod == AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT) ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
void TangentsRule::Reflect(AZ::ReflectContext* context)
@@ -43,20 +52,29 @@ namespace AZ
return;
}
serializeContext->Class<TangentsRule, DataTypes::IRule>()->Version(3)
->Field("tangentSpace", &TangentsRule::m_tangentSpace);
serializeContext->Class<TangentsRule, DataTypes::IRule>()->Version(4)
->Field("tangentSpace", &TangentsRule::m_generationMethod)
->Field("tSpaceMethod", &TangentsRule::m_tSpaceMethod);
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<TangentsRule>("Tangents", "Specify how tangents are imported or generated.")
->ClassElement(Edit::ClassElements::EditorData, "")
->Attribute("AutoExpand", true)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_tangentSpace, "Tangent space", "Specify the tangent space used for normal map baking. Choose 'From Fbx' to extract the tangents and bitangents directly from the Fbx file. When there is no tangents rule or the Fbx has no tangents stored inside it, the 'MikkT' option will be used with orthogonal tangents of unit length, so with the normalize option enabled, using the first UV set.")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene, "From Source Scene")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::MikkT, "MikkT")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute("AutoExpand", true)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_generationMethod, "Generation Method", "Specify the tangent generation method. Choose 'From Source Scene' to extract the tangents and bitangents directly from the source scene file. When there is no tangents rule or the source scene has no tangents stored inside it, the 'MikkT' option will be used.")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene, "From Source Scene")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT, "MikkT")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_tSpaceMethod, "TSpace Method",
"TSpace generates the tangents and bitangents with their true magnitudes which can be used for relief mapping effects. "
" It calculates the 'real' bitangent which may not be perpendicular to the tangent. "
"However, both, the tangent and bitangent are perpendicular to the vertex normal. "
"TSpaceBasic calculates unit vector tangents and bitangents at pixel/vertex level which are sufficient for basic normal mapping.")
->EnumAttribute(AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace, "TSpace")
->EnumAttribute(AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpaceBasic, "TSpaceBasic")
->Attribute(AZ::Edit::Attributes::Visibility, &TangentsRule::GetSpaceMethodVisibility);
;
}
}
@@ -45,12 +45,17 @@ namespace AZ
SCENE_DATA_API TangentsRule();
SCENE_DATA_API ~TangentsRule() override = default;
SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpace() const;
SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentGenerationMethod GetGenerationMethod() const;
SCENE_DATA_API AZ::SceneAPI::DataTypes::MikkTSpaceMethod GetMikkTSpaceMethod() const;
static void Reflect(ReflectContext* context);
protected:
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace; /**< Specifies how to handle tangents. Either generate them, or import them. */
AZ::SceneAPI::DataTypes::TangentGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT; /**< Specifies how to handle tangents. Either generate them, or import them. */
// MikkT specific settings
AZ::Crc32 GetSpaceMethodVisibility() const;
AZ::SceneAPI::DataTypes::MikkTSpaceMethod m_tSpaceMethod = AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace;
};
} // SceneData
} // SceneAPI
@@ -84,7 +84,7 @@ namespace AZ
auto* bitangentData = AZStd::any_cast<AZ::SceneData::GraphData::MeshVertexBitangentData>(&data);
bitangentData->AppendBitangent(AZ::Vector3{0.12f, 0.34f, 0.56f});
bitangentData->AppendBitangent(AZ::Vector3{0.77f, 0.88f, 0.99f});
bitangentData->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene);
bitangentData->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
bitangentData->SetBitangentSetIndex(1);
return true;
}
@@ -94,7 +94,7 @@ namespace AZ
tangentData->AppendTangent(AZ::Vector4{0.12f, 0.34f, 0.56f, 0.78f});
tangentData->AppendTangent(AZ::Vector4{0.18f, 0.28f, 0.19f, 0.29f});
tangentData->AppendTangent(AZ::Vector4{0.21f, 0.43f, 0.65f, 0.87f});
tangentData->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::MikkT);
tangentData->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT);
tangentData->SetTangentSetIndex(2);
return true;
}
@@ -318,7 +318,7 @@ namespace AZ
ExpectExecute("TestExpectFloatEquals(bitangentData.y, 0.88)");
ExpectExecute("TestExpectFloatEquals(bitangentData.z, 0.99)");
ExpectExecute("TestExpectIntegerEquals(meshVertexBitangentData:GetBitangentSetIndex(), 1)");
ExpectExecute("TestExpectTrue(meshVertexBitangentData:GetTangentSpace(), MeshVertexBitangentData.FromSourceScene)");
ExpectExecute("TestExpectTrue(meshVertexBitangentData:GetGenerationMethod(), MeshVertexBitangentData.FromSourceScene)");
}
TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_MeshVertexTangentData_AccessWorks)
@@ -337,7 +337,7 @@ namespace AZ
ExpectExecute("TestExpectFloatEquals(tangentData.z, 0.19)");
ExpectExecute("TestExpectFloatEquals(tangentData.w, 0.29)");
ExpectExecute("TestExpectIntegerEquals(meshVertexTangentData:GetTangentSetIndex(), 2)");
ExpectExecute("TestExpectTrue(meshVertexTangentData:GetTangentSpace(), MeshVertexTangentData.EMotionFX)");
ExpectExecute("TestExpectTrue(meshVertexTangentData:GetGenerationMethod(), MeshVertexTangentData.EMotionFX)");
}
TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_AnimationData_AccessWorks)
@@ -10,7 +10,9 @@ ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Platf
include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(${LY_TEST_IMPACT_ACTIVE} AND PAL_TRAIT_TEST_IMPACT_FRAMEWORK_SUPPORTED)
add_subdirectory(Runtime)
add_subdirectory(Frontend)
if(PAL_TRAIT_TEST_IMPACT_FRAMEWORK_SUPPORTED)
if(LY_TEST_IMPACT_INSTRUMENTATION_BIN)
add_subdirectory(Runtime)
add_subdirectory(Frontend)
endif()
endif()
@@ -92,12 +92,16 @@ namespace AZ
float GetFarClipDistance() override;
float GetFrustumWidth() override;
float GetFrustumHeight() override;
bool IsOrthographic() override;
float GetOrthographicHalfWidth() override;
void SetFovDegrees(float fov) override;
void SetFovRadians(float fov) override;
void SetNearClipDistance(float nearClipDistance) override;
void SetFarClipDistance(float farClipDistance) override;
void SetFrustumWidth(float width) override;
void SetFrustumHeight(float height) override;
void SetOrthographic(bool orthographic) override;
void SetOrthographicHalfWidth(float halfWidth) override;
void MakeActiveView() override;
// RPI::WindowContextNotificationBus overrides...
@@ -185,6 +185,15 @@ namespace AZ
return m_componentConfig.m_depthFar * tanf(m_componentConfig.m_fovY / 2) * 2;
}
bool CameraComponent::IsOrthographic()
{
return false;
}
float CameraComponent::GetOrthographicHalfWidth()
{
return 0.0f;
}
void CameraComponent::SetFovDegrees(float fov)
{
@@ -226,6 +235,16 @@ namespace AZ
UpdateViewToClipMatrix();
}
void CameraComponent::SetOrthographic(bool orthographic)
{
AZ_Assert(!orthographic, "DebugCamera does not support orthographic projection");
}
void CameraComponent::SetOrthographicHalfWidth([[maybe_unused]] float halfWidth)
{
AZ_Assert(false, "DebugCamera does not support orthographic projection");
}
void CameraComponent::MakeActiveView()
{
// do nothing
@@ -62,6 +62,7 @@ class ProjectedShadow
float3 m_lightDirection;
float3 m_normalVector;
float3 m_shadowPosition;
float m_bias;
};
float ProjectedShadow::GetVisibility(
@@ -238,7 +239,7 @@ float ProjectedShadow::GetVisibilityEsm()
}
const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy);
const float depth = PerspectiveDepthToLinear(
m_shadowPosition.z,
m_shadowPosition.z - m_bias,
coefficients);
const float occluder = shadowmap.SampleLevel(
PassSrg::LinearSampler,
@@ -280,7 +281,7 @@ float ProjectedShadow::GetVisibilityEsmPcf()
}
const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy);
const float depth = PerspectiveDepthToLinear(
m_shadowPosition.z,
m_shadowPosition.z - m_bias,
coefficients);
const float occluder = shadowmap.SampleLevel(
PassSrg::LinearSampler,
@@ -346,7 +347,7 @@ float ProjectedShadow::SamplePcfBicubic()
param.shadowPos = float3(atlasPosition.xy * ViewSrg::m_invShadowmapAtlasSize, atlasPosition.z);
param.shadowMapSize = ViewSrg::m_shadowmapAtlasSize;
param.invShadowMapSize = ViewSrg::m_invShadowmapAtlasSize;
param.comparisonValue = m_shadowPosition.z - ViewSrg::m_projectedShadows[m_shadowIndex].m_bias;
param.comparisonValue = m_shadowPosition.z - m_bias;
param.samplerState = SceneSrg::m_hwPcfSampler;
if (filteringSampleCount <= 4)
@@ -384,8 +385,8 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition)
PassSrg::LinearSampler,
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z), /*LOD=*/0).r;
const float depthDiff = depthInShadowmap - shadowPosition.z;
float bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias;
if (depthDiff < -bias)
if (depthDiff < -m_bias)
{
return true;
}
@@ -428,6 +429,8 @@ void ProjectedShadow::SetShadowPosition()
const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix;
float4 shadowPositionHomogeneous = mul(depthBiasMatrix, float4(m_worldPosition, 1));
m_shadowPosition = shadowPositionHomogeneous.xyz / shadowPositionHomogeneous.w;
m_bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias / shadowPositionHomogeneous.w;
}
float3 ProjectedShadow::GetAtlasPosition(float2 texturePosition)
@@ -84,6 +84,8 @@ namespace AZ
//! Sets if shadows are enabled
virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0;
//! Sets the shadow bias
virtual void SetShadowBias(LightHandle handle, float bias) = 0;
//! Sets the shadowmap size (width and height) of the light.
virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
//! Specifies filter method of shadows.
@@ -66,6 +66,8 @@ namespace AZ
virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0;
//! Sets the shadowmap size (width and height) of the light.
virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
//! Sets the shadow bias
virtual void SetShadowBias(LightHandle handle, float bias) = 0;
//! Specifies filter method of shadows.
virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0;
//! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on
@@ -50,6 +50,8 @@ namespace AZ::Render
virtual void SetFieldOfViewY(ShadowId id, float fieldOfView) = 0;
//! Sets the maximum resolution of the shadow map
virtual void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) = 0;
//! Sets the shadow bias
virtual void SetShadowBias(ShadowId id, float bias) = 0;
//! Sets the shadowmap Pcf method.
virtual void SetPcfMethod(ShadowId id, PcfMethod method) = 0;
//! Sets the shadow filter method
@@ -308,6 +308,11 @@ namespace AZ
AZStd::invoke(AZStd::forward<Functor>(functor), m_shadowFeatureProcessor, shadowId, AZStd::forward<ParamType>(param));
}
}
void DiskLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias);
}
void DiskLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
{
@@ -50,6 +50,7 @@ namespace AZ
void SetConstrainToConeLight(LightHandle handle, bool useCone) override;
void SetConeAngles(LightHandle handle, float innerDegrees, float outerDegrees) override;
void SetShadowsEnabled(LightHandle handle, bool enabled) override;
void SetShadowBias(LightHandle handle, float bias) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
@@ -277,6 +277,11 @@ namespace AZ
}
}
}
void PointLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias);
}
void PointLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
{
@@ -47,6 +47,7 @@ namespace AZ
void SetAttenuationRadius(LightHandle handle, float attenuationRadius) override;
void SetBulbRadius(LightHandle handle, float bulbRadius) override;
void SetShadowsEnabled(LightHandle handle, bool enabled) override;
void SetShadowBias(LightHandle handle, float bias) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
@@ -143,7 +143,15 @@ namespace AZ::Render
shadowProperty.m_desc.m_fieldOfViewYRadians = fieldOfViewYRadians;
UpdateShadowView(shadowProperty);
}
void ProjectedShadowFeatureProcessor::SetShadowBias(ShadowId id, float bias)
{
AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBias().");
ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
shadowProperty.m_bias = bias;
}
void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size)
{
AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution().");
@@ -265,23 +273,20 @@ namespace AZ::Render
view->SetCameraTransform(Matrix3x4::CreateFromTransform(desc.m_transform));
ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(shadowProperty.m_shadowId.GetIndex());
shadowData.m_bias = (nearDist / farDist) * 0.1f;
// Adjust the manually set bias to a more appropriate range for the shader. Scale the bias by the
// near plane so that the bias appears consistent as other light properties change.
shadowData.m_bias = nearDist * shadowProperty.m_bias * 0.01f;
FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
if (FilterMethodIsEsm(shadowData))
{
// Set parameters to calculate linear depth if ESM is used.
m_filterParameterNeedsUpdate = true;
esmData.m_isEnabled = true;
esmData.m_n_f_n = nearDist / (farDist - nearDist);
esmData.m_n_f = nearDist - farDist;
esmData.m_f = farDist;
}
else
{
// Reset enabling flag if ESM is not used.
esmData.m_isEnabled = false;
}
// Set parameters to calculate linear depth if ESM is used.
esmData.m_n_f_n = nearDist / (farDist - nearDist);
esmData.m_n_f = nearDist - farDist;
esmData.m_f = farDist;
esmData.m_isEnabled = FilterMethodIsEsm(shadowData);
m_filterParameterNeedsUpdate = m_filterParameterNeedsUpdate || esmData.m_isEnabled;
for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses)
{
@@ -47,6 +47,7 @@ namespace AZ::Render
void SetAspectRatio(ShadowId id, float aspectRatio) override;
void SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians) override;
void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override;
void SetShadowBias(ShadowId id, float bias) override;
void SetPcfMethod(ShadowId id, PcfMethod method);
void SetEsmExponent(ShadowId id, float exponent);
void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override;
@@ -79,6 +80,7 @@ namespace AZ::Render
{
ProjectedShadowDescriptor m_desc;
RPI::ViewPtr m_shadowmapView;
float m_bias = 0.1f;
ShadowId m_shadowId;
};
@@ -6,6 +6,18 @@
#
#
set(GLAD_VULKAN_COMPILE_DEFINITIONS
VK_USE_PLATFORM_XCB_KHR
)
if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
set(GLAD_VULKAN_COMPILE_DEFINITIONS
VK_USE_PLATFORM_XCB_KHR
)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")
set(GLAD_VULKAN_COMPILE_DEFINITIONS
VK_USE_PLATFORM_WAYLAND_KHR
)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "xlib")
set(GLAD_VULKAN_COMPILE_DEFINITIONS
VK_USE_PLATFORM_XLIB_KHR
)
else()
message(FATAL_ERROR, "Linux Window Manager ${PAL_TRAIT_LINUX_WINDOW_MANAGER} is not recognized")
endif()
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <RHI/Conversion.h>
#include <RHI/Instance.h>
#include <RHI/WSISurface.h>
@@ -17,15 +18,36 @@ namespace AZ
{
Instance& instance = Instance::GetInstance();
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
xcb_connection_t* xcb_connection = nullptr;
if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get();
xcbConnectionManager != nullptr)
{
xcb_connection = xcbConnectionManager->GetXcbConnection();
}
AZ_Error("AtomVulkan_RHI", xcb_connection!=nullptr, "Unable to get XCB Connection");
VkXcbSurfaceCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.connection = xcb_connection;
createInfo.window = static_cast<xcb_window_t>(m_descriptor.m_windowHandle.GetIndex());
const VkResult result = vkCreateXcbSurfaceKHR(instance.GetNativeInstance(), &createInfo, nullptr, &m_nativeSurface);
AssertSuccess(result);
return ConvertResult(result);
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return RHI::ResultCode::Unimplemented;
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB
#error "Linux Window Manager XLIB not supported."
return RHI::ResultCode::Unimplemented;
#else
#error "Linux Window Manager not recognized."
return RHI::ResultCode::Unimplemented;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
}
}
@@ -30,7 +30,7 @@
},
"normal": {
"flipY": true,
"textureMap": "Objects/Lucy/Lucy_normal.png"
"textureMap": "Objects/Lucy/Lucy_Normal.png"
},
"subsurfaceScattering": {
"enableSubsurfaceScattering": true,
@@ -29,7 +29,7 @@
},
"normal": {
"flipY": true,
"textureMap": "Objects/Lucy/Lucy_normal.png"
"textureMap": "Objects/Lucy/Lucy_Normal.png"
},
"subsurfaceScattering": {
"enableSubsurfaceScattering": true,
@@ -101,6 +101,12 @@ namespace AZ
//! Sets if shadows should be enabled.
virtual void SetEnableShadow(bool enabled) = 0;
//! Returns the shadow bias.
virtual float GetShadowBias() const = 0;
//! Sets the shadow bias.
virtual void SetShadowBias(float bias) = 0;
//! Returns the maximum width and height of shadowmap.
virtual ShadowmapSize GetShadowmapMaxSize() const = 0;
@@ -56,6 +56,7 @@ namespace AZ
// Shadows (only used for supported shapes)
bool m_enableShadow = false;
float m_bias = 0.1f;
ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256;
ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None;
PcfMethod m_pcfMethod = PcfMethod::Bicubic;
@@ -18,7 +18,7 @@ namespace AZ
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AreaLightComponentConfig, ComponentConfig>()
->Version(6) // ATOM-15654
->Version(7) // ATOM-16034
->Field("LightType", &AreaLightComponentConfig::m_lightType)
->Field("Color", &AreaLightComponentConfig::m_color)
->Field("IntensityMode", &AreaLightComponentConfig::m_intensityMode)
@@ -33,6 +33,7 @@ namespace AZ
->Field("OuterShutterAngleDegrees", &AreaLightComponentConfig::m_outerShutterAngleDegrees)
// Shadows
->Field("Enable Shadow", &AreaLightComponentConfig::m_enableShadow)
->Field("Shadow Bias", &AreaLightComponentConfig::m_bias)
->Field("Shadowmap Max Size", &AreaLightComponentConfig::m_shadowmapMaxSize)
->Field("Shadow Filter Method", &AreaLightComponentConfig::m_shadowFilterMethod)
->Field("Softening Boundary Width", &AreaLightComponentConfig::m_boundaryWidthInDegrees)
@@ -68,6 +68,8 @@ namespace AZ::Render
->Event("GetEnableShadow", &AreaLightRequestBus::Events::GetEnableShadow)
->Event("SetEnableShadow", &AreaLightRequestBus::Events::SetEnableShadow)
->Event("GetShadowBias", &AreaLightRequestBus::Events::GetShadowBias)
->Event("SetShadowBias", &AreaLightRequestBus::Events::SetShadowBias)
->Event("GetShadowmapMaxSize", &AreaLightRequestBus::Events::GetShadowmapMaxSize)
->Event("SetShadowmapMaxSize", &AreaLightRequestBus::Events::SetShadowmapMaxSize)
->Event("GetShadowFilterMethod", &AreaLightRequestBus::Events::GetShadowFilterMethod)
@@ -94,6 +96,7 @@ namespace AZ::Render
->VirtualProperty("OuterShutterAngle", "GetOuterShutterAngle", "SetOuterShutterAngle")
->VirtualProperty("ShadowsEnabled", "GetEnableShadow", "SetEnableShadow")
->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias")
->VirtualProperty("ShadowmapMaxSize", "GetShadowmapMaxSize", "SetShadowmapMaxSize")
->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod")
->VirtualProperty("SofteningBoundaryWidthAngle", "GetSofteningBoundaryWidthAngle", "SetSofteningBoundaryWidthAngle")
@@ -307,6 +310,7 @@ namespace AZ::Render
m_lightShapeDelegate->SetEnableShadow(m_configuration.m_enableShadow);
if (m_configuration.m_enableShadow)
{
m_lightShapeDelegate->SetShadowBias(m_configuration.m_bias);
m_lightShapeDelegate->SetShadowmapMaxSize(m_configuration.m_shadowmapMaxSize);
m_lightShapeDelegate->SetShadowFilterMethod(m_configuration.m_shadowFilterMethod);
m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(m_configuration.m_boundaryWidthInDegrees);
@@ -467,6 +471,20 @@ namespace AZ::Render
m_lightShapeDelegate->SetEnableShadow(enabled);
}
}
float AreaLightComponentController::GetShadowBias() const
{
return m_configuration.m_bias;
}
void AreaLightComponentController::SetShadowBias(float bias)
{
m_configuration.m_bias = bias;
if (m_lightShapeDelegate)
{
m_lightShapeDelegate->SetShadowBias(bias);
}
}
ShadowmapSize AreaLightComponentController::GetShadowmapMaxSize() const
{
@@ -76,6 +76,8 @@ namespace AZ
bool GetEnableShadow() const override;
void SetEnableShadow(bool enabled) override;
float GetShadowBias() const override;
void SetShadowBias(float bias) override;
ShadowmapSize GetShadowmapMaxSize() const override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
ShadowFilterMethod GetShadowFilterMethod() const override;
@@ -123,6 +123,14 @@ namespace AZ::Render
}
}
void DiskLightDelegate::SetShadowBias(float bias)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowBias(GetLightHandle(), bias);
}
}
void DiskLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
@@ -41,6 +41,7 @@ namespace AZ
void SetShutterAngles(float innerAngleDegrees, float outerAngleDegrees) override;
void SetEnableShadow(bool enabled) override;
void SetShadowBias(float bias) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
@@ -131,6 +131,15 @@ namespace AZ
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled)
->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_bias, "Bias", "How deep in shadow a surface must be before being affected by it.")
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::Min, 0.0f)
->Attribute(Edit::Attributes::Max, 100.0f)
->Attribute(Edit::Attributes::SoftMin, 0.0f)
->Attribute(Edit::Attributes::SoftMax, 1.0f)
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled)
->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowFilterMethod, "Shadow filter method",
"Filtering method of edge-softening of shadows.\n"
" None: no filtering\n"
@@ -53,6 +53,7 @@ namespace AZ
void SetShutterAngles([[maybe_unused]]float innerAngleDegrees, [[maybe_unused]]float outerAngleDegrees) override {};
void SetEnableShadow(bool enabled) override { m_shadowsEnabled = enabled; };
void SetShadowBias([[maybe_unused]] float bias) override {};
void SetShadowmapMaxSize([[maybe_unused]] ShadowmapSize size) override {};
void SetShadowFilterMethod([[maybe_unused]] ShadowFilterMethod method) override {};
void SetSofteningBoundaryWidthAngle([[maybe_unused]] float widthInDegrees) override {};
@@ -67,8 +67,10 @@ namespace AZ
// Shadows
//! Sets if shadows should be enabled
//! Sets if shadows should be enabled
virtual void SetEnableShadow(bool enabled) = 0;
//! Sets the shadow bias
virtual void SetShadowBias(float bias) = 0;
//! Sets the maximum resolution of the shadow map
virtual void SetShadowmapMaxSize(ShadowmapSize size) = 0;
//! Sets the filter method for the shadow
@@ -11,121 +11,124 @@
#include <Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h>
#include <AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h>
namespace AZ
namespace AZ::Render
{
namespace Render
SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible)
: LightDelegateBase<PointLightFeatureProcessorInterface>(entityId, isVisible)
, m_shapeBus(shapeBus)
{
SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible)
: LightDelegateBase<PointLightFeatureProcessorInterface>(entityId, isVisible)
, m_shapeBus(shapeBus)
{
InitBase(entityId);
}
InitBase(entityId);
}
float SphereLightDelegate::CalculateAttenuationRadius(float lightThreshold) const
{
// Calculate the radius at which the irradiance will be equal to cutoffIntensity.
float intensity = GetPhotometricValue().GetCombinedIntensity(PhotometricUnit::Lumen);
return sqrt(intensity / lightThreshold);
}
float SphereLightDelegate::CalculateAttenuationRadius(float lightThreshold) const
{
// Calculate the radius at which the irradiance will be equal to cutoffIntensity.
float intensity = GetPhotometricValue().GetCombinedIntensity(PhotometricUnit::Lumen);
return sqrt(intensity / lightThreshold);
}
void SphereLightDelegate::HandleShapeChanged()
void SphereLightDelegate::HandleShapeChanged()
{
if (GetLightHandle().IsValid())
{
if (GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetPosition(GetLightHandle(), GetTransform().GetTranslation());
GetFeatureProcessor()->SetBulbRadius(GetLightHandle(), GetRadius());
}
GetFeatureProcessor()->SetPosition(GetLightHandle(), GetTransform().GetTranslation());
GetFeatureProcessor()->SetBulbRadius(GetLightHandle(), GetRadius());
}
}
float SphereLightDelegate::GetSurfaceArea() const
{
float radius = GetRadius();
return 4.0f * Constants::Pi * radius * radius;
}
float SphereLightDelegate::GetSurfaceArea() const
{
float radius = GetRadius();
return 4.0f * Constants::Pi * radius * radius;
}
float SphereLightDelegate::GetRadius() const
{
return m_shapeBus->GetRadius() * GetTransform().GetUniformScale();
}
float SphereLightDelegate::GetRadius() const
{
return m_shapeBus->GetRadius() * GetTransform().GetUniformScale();
}
void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const
void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const
{
if (isSelected)
{
if (isSelected)
{
debugDisplay.SetColor(color);
debugDisplay.SetColor(color);
// Draw a sphere for the attenuation radius
debugDisplay.DrawWireSphere(transform.GetTranslation(), GetConfig()->m_attenuationRadius);
}
// Draw a sphere for the attenuation radius
debugDisplay.DrawWireSphere(transform.GetTranslation(), GetConfig()->m_attenuationRadius);
}
}
void SphereLightDelegate::SetEnableShadow(bool enabled)
void SphereLightDelegate::SetEnableShadow(bool enabled)
{
Base::SetEnableShadow(enabled);
if (GetLightHandle().IsValid())
{
Base::SetEnableShadow(enabled);
if (GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled);
}
GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled);
}
void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
}
void SphereLightDelegate::SetShadowBias(float bias)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size);
}
GetFeatureProcessor()->SetShadowBias(GetLightHandle(), bias);
}
}
void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method)
void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method);
}
GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size);
}
}
void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
}
GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method);
}
}
void SphereLightDelegate::SetPredictionSampleCount(uint32_t count)
void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count);
}
GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
}
}
void SphereLightDelegate::SetFilteringSampleCount(uint32_t count)
void SphereLightDelegate::SetPredictionSampleCount(uint32_t count)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count);
}
GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count);
}
}
void SphereLightDelegate::SetPcfMethod(PcfMethod method)
void SphereLightDelegate::SetFilteringSampleCount(uint32_t count)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method);
}
GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count);
}
}
void SphereLightDelegate::SetEsmExponent(float esmExponent)
void SphereLightDelegate::SetPcfMethod(PcfMethod method)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetEsmExponent(GetLightHandle(), esmExponent);
}
GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method);
}
}
} // namespace Render
} // namespace AZ
void SphereLightDelegate::SetEsmExponent(float esmExponent)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetEsmExponent(GetLightHandle(), esmExponent);
}
}
} // namespace AZ::Render
@@ -31,6 +31,7 @@ namespace AZ
float GetSurfaceArea() const override;
float GetEffectiveSolidAngle() const override { return PhotometricValue::OmnidirectionalSteradians; }
void SetEnableShadow(bool enabled) override;
void SetShadowBias(float bias) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
@@ -16,9 +16,6 @@ set(AUDIOENGINEWWISE_COMPILEDEFINITIONS
)
find_package(Wwise MODULE)
if (NOT Wwise_FOUND)
message(STATUS "** Update the LY_WWISE_INSTALL_PATH cache variable if you intend to use Wwise.")
endif()
################################################################################
# Server / Unsupported
@@ -19,10 +19,7 @@
#include <AudioSystemControl_wwise.h>
#include <Common_wwise.h>
#include <ISystem.h>
#include <CryFile.h>
#include <CryPath.h>
#include <Util/PathUtil.h>
#include <QDir>
void InitWwiseResources()
{
@@ -217,28 +214,34 @@ namespace AudioControls
}
//-------------------------------------------------------------------------------------------//
TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType)
TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(AZ::rapidxml::xml_node<char>* node, EACEControlType atlControlType)
{
if (node)
{
const AZStd::string tag(node->getTag());
TImplControlType type = TagToType(tag);
AZStd::string_view element(node->name());
TImplControlType type = TagToType(element);
if (type != AUDIO_IMPL_INVALID_TYPE)
{
AZStd::string name(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute));
AZStd::string localized(node->getAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute));
AZStd::string name;
AZStd::string_view localized;
// Legacy Preload support
if (localized.empty())
if (auto nameAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false);
nameAttr != nullptr)
{
localized = node->getAttr(Audio::WwiseXmlTags::Legacy::WwiseLocalizedAttribute);
name = nameAttr->value();
}
bool isLocalized = AZ::StringFunc::Equal(localized.c_str(), "true");
if (auto localizedAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseLocalizedAttribute, 0, false);
localizedAttr != nullptr)
{
localized = localizedAttr->value();
}
// If control not found, create a placeholder.
// We want to keep that connection even if it's not in the middleware.
// The user could be using the engine without the wwise project
bool isLocalized = AZ::StringFunc::Equal(localized, "true");
// If the control wasn't found, create a placeholder.
// We want to see that connection even if it's not in the middleware.
// User could be viewing the editor without a middleware project.
IAudioSystemControl* control = GetControlByName(name, isLocalized);
if (!control)
{
@@ -250,27 +253,26 @@ namespace AudioControls
}
}
// If it's a switch we actually connect to one of the states within the switch
// If it's a switch we connect to one of the states within the switch
if (type == eWCT_WWISE_SWITCH_GROUP || type == eWCT_WWISE_GAME_STATE_GROUP)
{
if (node->getChildCount() == 1)
if (auto childNode = node->first_node();
childNode != nullptr)
{
node = node->getChild(0);
if (node)
AZStd::string childName;
if (auto childNameAttr = childNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false);
childNameAttr != nullptr)
{
AZStd::string childName(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute));
IAudioSystemControl* childControl = GetControlByName(childName, false, control);
if (!childControl)
{
childControl = CreateControl(SControlDef(childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control));
}
control = childControl;
childName = childNameAttr->value();
}
}
else
{
CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Audio Controls Editor (Wwise): Error reading connection to Wwise control %s", name.c_str());
IAudioSystemControl* childControl = GetControlByName(childName, false, control);
if (!childControl)
{
childControl = CreateControl(SControlDef(
childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control));
}
control = childControl;
}
}
@@ -289,16 +291,19 @@ namespace AudioControls
float mult = 1.0f;
float shift = 0.0f;
if (node->haveAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute))
if (auto multAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseMutiplierAttribute, 0, false);
multAttr != nullptr)
{
const AZStd::string multProperty(node->getAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute));
mult = AZStd::stof(multProperty);
mult = AZStd::stof(AZStd::string(multAttr->value()));
}
if (node->haveAttr(Audio::WwiseXmlTags::WwiseShiftAttribute))
if (auto shiftAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseShiftAttribute, 0, false);
shiftAttr != nullptr)
{
const AZStd::string shiftProperty(node->getAttr(Audio::WwiseXmlTags::WwiseShiftAttribute));
shift = AZStd::stof(shiftProperty);
shift = AZStd::stof(AZStd::string(shiftAttr->value()));
}
connection->m_mult = mult;
connection->m_shift = shift;
return connection;
@@ -308,11 +313,12 @@ namespace AudioControls
TStateConnectionPtr connection = AZStd::make_shared<CStateToRtpcConnection>(control->GetId());
float value = 0.0f;
if (node->haveAttr(Audio::WwiseXmlTags::WwiseValueAttribute))
if (auto valueAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseValueAttribute, 0, false);
valueAttr != nullptr)
{
const AZStd::string valueProperty(node->getAttr(Audio::WwiseXmlTags::WwiseValueAttribute));
value = AZStd::stof(valueProperty);
value = AZStd::stof(AZStd::string(valueAttr->value()));
}
connection->m_value = value;
return connection;
}
@@ -329,28 +335,50 @@ namespace AudioControls
}
//-------------------------------------------------------------------------------------------//
XmlNodeRef CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType)
AZ::rapidxml::xml_node<char>* CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType)
{
const IAudioSystemControl* control = GetControl(connection->GetID());
if (control)
{
XmlAllocator& xmlAllocator(AudioControls::s_xmlAllocator);
switch (control->GetType())
{
case AudioControls::eWCT_WWISE_SWITCH:
[[fallthrough]];
case AudioControls::eWCT_WWISE_SWITCH_GROUP:
[[fallthrough]];
case AudioControls::eWCT_WWISE_GAME_STATE:
[[fallthrough]];
case AudioControls::eWCT_WWISE_GAME_STATE_GROUP:
{
const IAudioSystemControl* parent = control->GetParent();
if (parent)
{
XmlNodeRef switchNode = GetISystem()->CreateXmlNode(TypeToTag(parent->GetType()).data());
switchNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, parent->GetName().c_str());
AZStd::string_view parentType = TypeToTag(parent->GetType());
auto switchNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(parentType.data())
);
XmlNodeRef stateNode = switchNode->createNode(Audio::WwiseXmlTags::WwiseValueTag);
stateNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
switchNode->addChild(stateNode);
auto switchNameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(parent->GetName().c_str())
);
auto stateNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
Audio::WwiseXmlTags::WwiseValueTag
);
auto stateNameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
switchNode->append_attribute(switchNameAttr);
stateNode->append_attribute(stateNameAttr);
switchNode->append_node(stateNode);
return switchNode;
}
break;
@@ -358,51 +386,98 @@ namespace AudioControls
case AudioControls::eWCT_WWISE_RTPC:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
auto connectionNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
);
auto nameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
connectionNode->append_attribute(nameAttr);
if (atlControlType == eACET_RTPC)
{
AZStd::shared_ptr<const CRtpcConnection> rtpcConnection = AZStd::static_pointer_cast<const CRtpcConnection>(connection);
if (rtpcConnection->m_mult != 1.0f)
if (rtpcConnection->m_mult != 1.f)
{
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute, rtpcConnection->m_mult);
auto multAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseMutiplierAttribute,
xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_mult).c_str())
);
connectionNode->append_attribute(multAttr);
}
if (rtpcConnection->m_shift != 0.0f)
if (rtpcConnection->m_shift != 0.f)
{
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseShiftAttribute, rtpcConnection->m_shift);
auto shiftAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseShiftAttribute,
xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_shift).c_str())
);
connectionNode->append_attribute(shiftAttr);
}
}
else if (atlControlType == eACET_SWITCH_STATE)
{
AZStd::shared_ptr<const CStateToRtpcConnection> stateConnection = AZStd::static_pointer_cast<const CStateToRtpcConnection>(connection);
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseValueAttribute, stateConnection->m_value);
auto valueAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseValueAttribute,
xmlAllocator.allocate_string(AZStd::to_string(stateConnection->m_value).c_str())
);
connectionNode->append_attribute(valueAttr);
}
return connectionNode;
}
case AudioControls::eWCT_WWISE_EVENT:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
return connectionNode;
}
[[fallthrough]];
case AudioControls::eWCT_WWISE_AUX_BUS:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
auto connectionNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
);
auto nameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
connectionNode->append_attribute(nameAttr);
return connectionNode;
}
case AudioControls::eWCT_WWISE_SOUND_BANK:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
auto connectionNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
);
auto nameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
connectionNode->append_attribute(nameAttr);
if (control->IsLocalized())
{
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute, "true");
auto locAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseLocalizedAttribute,
xmlAllocator.allocate_string("true")
);
connectionNode->append_attribute(locAttr);
}
return connectionNode;
}
}
@@ -77,8 +77,8 @@ namespace AudioControls
EACEControlType ImplTypeToATLType(TImplControlType type) const override;
TImplControlTypeMask GetCompatibleTypes(EACEControlType atlControlType) const override;
TConnectionPtr CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl) override;
TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) override;
XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override;
TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node<char>* node, EACEControlType atlControlType) override;
AZ::rapidxml::xml_node<char>* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override;
const AZStd::string_view GetTypeIcon(TImplControlType type) const override;
const AZStd::string_view GetTypeIconSelected(TImplControlType type) const override;
AZStd::string GetName() const override;
@@ -9,20 +9,12 @@
#include <AudioWwiseLoader.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <AudioSystemEditor_wwise.h>
#include <AudioFileUtils.h>
#include <Config_wwise.h>
#include <ISystem.h>
#include <CryFile.h>
#include <CryPath.h>
#include <Util/PathUtil.h>
using namespace PathUtil;
namespace AudioControls
{
@@ -68,8 +60,7 @@ namespace AudioControls
for (const auto& filePath : foundFiles)
{
AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str());
AZStd::string fileName;
AZ::StringFunc::Path::GetFullFileName(filePath.c_str(), fileName);
AZ::IO::PathView fileName = filePath.Filename();
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str()))
{
@@ -79,15 +70,15 @@ namespace AudioControls
// we load only one as all of them should have the
// same content (in the future we want to have a
// consistency report to highlight if this is not the case)
m_localizationFolder = fileName;
m_localizationFolder.assign(fileName.Native().data(), fileName.Native().size());
LoadSoundBanks(rootFolder, m_localizationFolder, true);
isLocalizedLoaded = true;
}
}
else if (AZ::StringFunc::Find(fileName.c_str(), Audio::Wwise::BankExtension) != AZStd::string::npos
&& !AZ::StringFunc::Equal(fileName.c_str(), Audio::Wwise::InitBank))
else if (fileName.Extension() == Audio::Wwise::BankExtension && fileName != Audio::Wwise::InitBank)
{
m_audioSystemImpl->CreateControl(SControlDef(fileName, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath));
m_audioSystemImpl->CreateControl(
SControlDef(AZStd::string{ fileName.Native() }, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath));
}
}
}
@@ -103,14 +94,14 @@ namespace AudioControls
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str()))
{
LoadControlsInFolder(filePath);
LoadControlsInFolder(filePath.Native());
}
else
{
// Open the file, read into an xmlDoc, and call LoadControls with the root xml node...
AZ_TracePrintf("AudioWwiseLoader", "Loading Xml from '%s'", filePath.c_str());
Audio::ScopedXmlLoader xmlFileLoader(filePath);
Audio::ScopedXmlLoader xmlFileLoader(filePath.Native());
if (!xmlFileLoader.HasError())
{
LoadControl(xmlFileLoader.GetRootNode());
@@ -7,18 +7,16 @@
*/
#include <AzCore/PlatformIncl.h>
#include <FileIOHandler_wwise.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/IStreamer.h>
#include <IAudioInterfacesCommonData.h>
#include <AkPlatformFuncs_Platform.h>
#include <AudioEngineWwise_Traits_Platform.h>
#include <platform.h>
#include <ISystem.h>
#include <AzFramework/Archive/IArchive.h>
#include <cinttypes>
#define MAX_NUMBER_STRING_SIZE (10) // 4G
#define ID_TO_STRING_FORMAT_BANK AKTEXT("%u.bnk")
@@ -90,34 +88,36 @@ namespace Audio
bool CBlockingDevice_wwise::Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc)
{
const char* openModeString = nullptr;
AZ::IO::OpenMode azOpenMode = AZ::IO::OpenMode::ModeBinary;
switch (openMode)
{
case AK_OpenModeRead:
openModeString = "rbx";
azOpenMode |= AZ::IO::OpenMode::ModeRead;
break;
case AK_OpenModeWrite:
openModeString = "wbx";
azOpenMode |= AZ::IO::OpenMode::ModeWrite;
break;
case AK_OpenModeWriteOvrwr:
openModeString = "w+bx";
azOpenMode |= (AZ::IO::OpenMode::ModeUpdate | AZ::IO::OpenMode::ModeWrite);
break;
case AK_OpenModeReadWrite:
openModeString = "abx";
azOpenMode |= (AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeWrite);
break;
default:
AZ_Assert(false, "Unknown Wwise file open mode.");
return false;
}
const size_t fileSize = gEnv->pCryPak->FGetSize(filename);
if (fileSize > 0)
auto fileIO = AZ::IO::FileIOBase::GetInstance();
if (AZ::u64 fileSize = 0;
fileIO->Size(filename, fileSize) && fileSize != 0)
{
AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(filename, openModeString, AZ::IO::IArchive::FOPEN_HINT_DIRECT_OPERATION);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
fileIO->Open(filename, azOpenMode, fileHandle);
if (fileHandle != AZ::IO::InvalidHandle)
{
fileDesc.hFile = GetAkFileHandle(fileHandle);
fileDesc.iFileSize = static_cast<AkInt64>(fileSize);
fileDesc.iFileSize = aznumeric_cast<AkInt64>(fileSize);
fileDesc.uSector = 0;
fileDesc.deviceID = m_deviceID;
fileDesc.pCustomParam = nullptr;
@@ -132,50 +132,58 @@ namespace Audio
AKRESULT CBlockingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics&, void* buffer, AkIOTransferInfo& transferInfo)
{
AZ_Assert(buffer, "Wwise didn't provide a valid buffer to write to.");
AZ_Assert(buffer, "Wwise didn't provide a valid desination buffer to Read into.");
AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
const uint64_t currentFileReadPos = gEnv->pCryPak->FTell(fileHandle);
const uint64_t wantedFileReadPos = static_cast<uint64_t>(transferInfo.uFilePosition);
auto fileIO = AZ::IO::FileIOBase::GetInstance();
if (currentFileReadPos != wantedFileReadPos)
AZ::u64 currentFileReadPos = 0;
fileIO->Tell(fileHandle, currentFileReadPos);
if (currentFileReadPos != transferInfo.uFilePosition)
{
gEnv->pCryPak->FSeek(fileHandle, wantedFileReadPos, SEEK_SET);
fileIO->Seek(fileHandle, aznumeric_cast<AZ::s64>(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart);
}
const size_t bytesRead = gEnv->pCryPak->FReadRaw(buffer, 1, transferInfo.uRequestedSize, fileHandle);
AZ_Assert(bytesRead == static_cast<size_t>(transferInfo.uRequestedSize),
"Number of bytes read (%zu) for Wwise request doesn't match the requested size (%u).", bytesRead, transferInfo.uRequestedSize);
return (bytesRead > 0) ? AK_Success : AK_Fail;
AZ::u64 bytesRead = 0;
fileIO->Read(fileHandle, buffer, aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize), &bytesRead);
const bool readOk = (bytesRead == aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize));
AZ_Assert(readOk,
"Number of bytes read (%" PRIu64 ") for read request doesn't match the requested size (%u).",
bytesRead, transferInfo.uRequestedSize);
return readOk ? AK_Success : AK_Fail;
}
AKRESULT CBlockingDevice_wwise::Write(AkFileDesc& fileDesc, const AkIoHeuristics&, void* data, AkIOTransferInfo& transferInfo)
{
AZ_Assert(data, "Wwise didn't provide a valid buffer to read from.");
AZ_Assert(data, "Wwise didn't provide a valid source buffer to Write from.");
AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
auto fileIO = AZ::IO::FileIOBase::GetInstance();
const uint64_t currentFileWritePos = gEnv->pCryPak->FTell(fileHandle);
const uint64_t wantedFileWritePos = static_cast<uint64_t>(transferInfo.uFilePosition);
AZ::u64 currentFileWritePos = 0;
fileIO->Tell(fileHandle, currentFileWritePos);
if (currentFileWritePos != wantedFileWritePos)
if (currentFileWritePos != transferInfo.uFilePosition)
{
gEnv->pCryPak->FSeek(fileHandle, wantedFileWritePos, SEEK_SET);
fileIO->Seek(fileHandle, aznumeric_cast<AZ::s64>(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart);
}
const size_t bytesWritten = gEnv->pCryPak->FWrite(data, 1, static_cast<size_t>(transferInfo.uRequestedSize), fileHandle);
if (bytesWritten != static_cast<size_t>(transferInfo.uRequestedSize))
{
AZ_Error("Wwise", false, "Number of bytes written (%zu) for Wwise request doesn't match the requested size (%u).",
AZ::u64 bytesWritten = 0;
fileIO->Write(fileHandle, data, aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize), &bytesWritten);
const bool writeOk = (bytesWritten == aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize));
AZ_Error("Wwise", writeOk,
"Number of bytes written (%" PRIu64 ") for write request doesn't match the requested size (%u).",
bytesWritten, transferInfo.uRequestedSize);
return AK_Fail;
}
return AK_Success;
return writeOk ? AK_Success : AK_Fail;
}
AKRESULT CBlockingDevice_wwise::Close(AkFileDesc& fileDesc)
{
return gEnv->pCryPak->FClose(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail;
auto fileIO = AZ::IO::FileIOBase::GetInstance();
return fileIO->Close(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail;
}
AkUInt32 CBlockingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
@@ -189,7 +197,7 @@ namespace Audio
deviceDesc.bCanRead = true;
deviceDesc.bCanWrite = true;
deviceDesc.deviceID = m_deviceID;
AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "CryPak", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IArchive", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName);
}
@@ -231,12 +239,13 @@ namespace Audio
bool CStreamingDevice_wwise::Open(const char* filename, [[maybe_unused]] AkOpenMode openMode, AkFileDesc& fileDesc)
{
AZ_Assert(openMode == AK_OpenModeRead, "Wwise Async File IO - Only supports opening files for reading.\n");
const size_t fileSize = gEnv->pCryPak->FGetSize(filename);
if (fileSize)
auto fileIO = AZ::IO::FileIOBase::GetInstance();
if (AZ::u64 fileSize = 0;
fileIO->Size(filename, fileSize) && fileSize != 0)
{
AZStd::string* filenameStore = azcreate(AZStd::string, (filename));
fileDesc.hFile = AkFileHandle();
fileDesc.iFileSize = static_cast<AkInt64>(fileSize);
fileDesc.iFileSize = aznumeric_cast<AkInt64>(fileSize);
fileDesc.uSector = 0;
fileDesc.deviceID = m_deviceID;
fileDesc.pCustomParam = filenameStore;
@@ -326,7 +335,7 @@ namespace Audio
deviceDesc.bCanRead = true;
deviceDesc.bCanWrite = false;
deviceDesc.deviceID = m_deviceID;
AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "Streamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IStreamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName);
}
@@ -13,6 +13,7 @@
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/XML/rapidxml.h>
namespace AudioControls
{
@@ -39,4 +40,7 @@ namespace AudioControls
using FilepathSet = AZStd::set<AZStd::string>;
using XmlAllocator = AZ::rapidxml::memory_pool<>;
inline XmlAllocator s_xmlAllocator;
} // namespace AudioControls
@@ -12,12 +12,10 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/XML/rapidxml.h>
#include <ACETypes.h>
#include <platform.h>
#include <IXml.h>
namespace AudioControls
{
class IAudioSystemEditor;
@@ -117,14 +115,14 @@ namespace AudioControls
//! @param node XML node where the connection is defined.
//! @param atlControlType The type of the ATL control you are connecting to.
//! @return A pointer to the newly created connection.
virtual TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) = 0;
virtual TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node<char>* node, EACEControlType atlControlType) = 0;
//! When serializing connections between controls this function will be called once per connection to serialize its properties.
//! This function should be in sync with CreateConnectionToControl as whatever it's written here will have to be read there.
//! @param connection Connection to serialize.
//! @param atlControlType Type of the ATL control that has this connection.
//! @return XML node with the connection serialized.
virtual XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) = 0;
virtual AZ::rapidxml::xml_node<char>* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) = 0;
//! Whenever a connection is removed from an ATL control this function should be called.
//! To keep the system informed of which controls have been connected and which ones haven't.
@@ -47,6 +47,7 @@ namespace Audio
static constexpr const char* ATLInternalNameAttribute = "atl_internal_name";
static constexpr const char* ATLTypeAttribute = "atl_type";
static constexpr const char* ATLConfigGroupAttribute = "atl_config_group_name";
static constexpr const char* ATLPathAttribute = "path";
static constexpr const char* ATLDataLoadType = "AutoLoad";
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/string.h>
@@ -20,22 +21,26 @@ namespace Audio
/*!
* FindFilesInPath
*/
static AZStd::vector<AZStd::string> FindFilesInPath(const AZStd::string_view folderPath, const char* filter)
static AZStd::vector<AZ::IO::FixedMaxPath> FindFilesInPath(const AZStd::string_view folderPath, const char* filter)
{
AZStd::vector<AZStd::string> foundFiles;
AZStd::vector<AZ::IO::FixedMaxPath> foundFiles;
AZ::IO::FileIOBase::FindFilesCallbackType findFilesCallback = [&foundFiles](const char* file) -> bool
{
foundFiles.emplace_back(file);
foundFiles.emplace_back(AZ::IO::PathView{ file }.LexicallyNormal());
return true;
};
auto fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
if (auto fileIO = AZ::IO::FileIOBase::GetInstance();
fileIO != nullptr)
{
AZ::IO::Result result = fileIO->FindFiles(folderPath.data(), filter, findFilesCallback);
if (result == AZ::IO::ResultCode::Success)
{
return AZStd::move(foundFiles);
}
}
return foundFiles;
return {};
}
/*!
@@ -16,13 +16,8 @@
#include <ATLControlsModel.h>
#include <AudioControl.h>
#include <AudioControlsEditorPlugin.h>
#include <CryFile.h>
#include <CryPath.h>
#include <Cry_Camera.h>
#include <IAudioSystem.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <IEditor.h>
#include <QAudioControlEditorIcons.h>
#include <QWidgetAction>
@@ -14,7 +14,6 @@
#include <AudioControlsEditorPlugin.h>
#include <AudioControlsEditorUndo.h>
#include <IAudioSystemControl.h>
#include <IEditor.h>
#include <ImplementationManager.h>
namespace AudioControls
@@ -346,7 +345,7 @@ namespace AudioControls
{
for (auto& connectionNode : m_connectionNodes)
{
if (TConnectionPtr connection = audioSystemImpl->CreateConnectionFromXMLNode(connectionNode.m_xmlNode, m_type))
if (TConnectionPtr connection = audioSystemImpl->CreateConnectionFromXMLNode(connectionNode.m_xmlNode.get(), m_type))
{
AddConnection(connection);
connectionNode.m_isValid = true;
@@ -11,13 +11,11 @@
#include <ACETypes.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/XML/rapidxml.h>
#include <IAudioConnection.h>
#include <IAudioSystemControl.h>
#include <ISystem.h>
#include <IXml.h>
namespace AudioControls
{
class CATLControlsModel;
@@ -25,15 +23,52 @@ namespace AudioControls
//-------------------------------------------------------------------------------------------//
struct SRawConnectionData
{
SRawConnectionData(XmlNodeRef node, bool isValid)
: m_xmlNode(node)
, m_isValid(isValid)
{}
SRawConnectionData(AZ::rapidxml::xml_node<char>* node, bool isValid)
{
m_xmlNode = AZStd::move(DeepCopyNode(node));
m_isValid = isValid;
}
XmlNodeRef m_xmlNode;
AZStd::unique_ptr<AZ::rapidxml::xml_node<char>> m_xmlNode{};
// indicates if the connection is valid for the currently loaded middleware
bool m_isValid;
bool m_isValid{ false };
// Rapid XML provides a 'clone_node' utility that will copy an entire node tree,
// but it only copies pointers of any strings in the node names and values.
// This causes problems with storing raw xml nodes as this class does because strings
// will be pointing into the memory pool of an xml document that has gone out of scope.
// This function is a rewritten version of 'clone_node' that does the deep copy of strings
// into the new destination tree.
[[nodiscard]] static AZStd::unique_ptr<AZ::rapidxml::xml_node<char>> DeepCopyNode(AZ::rapidxml::xml_node<char>* srcNode)
{
AZStd::unique_ptr<AZ::rapidxml::xml_node<char>> destNode;
if (srcNode)
{
XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator);
destNode.reset(xmlAlloc.allocate_node(srcNode->type()));
destNode->name(xmlAlloc.allocate_string(srcNode->name(), srcNode->name_size()), srcNode->name_size());
destNode->value(xmlAlloc.allocate_string(srcNode->value(), srcNode->value_size()), srcNode->value_size());
for (AZ::rapidxml::xml_node<char>* child = srcNode->first_node(); child != nullptr; child = child->next_sibling())
{
destNode->append_node(DeepCopyNode(child).release());
}
for (AZ::rapidxml::xml_attribute<char>* attr = srcNode->first_attribute(); attr != nullptr; attr = attr->next_attribute())
{
destNode->append_attribute(xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(attr->name(), attr->name_size()),
xmlAlloc.allocate_string(attr->value(), attr->value_size()),
attr->name_size(),
attr->value_size()
));
}
}
return destNode;
}
};
using TXmlNodeList = AZStd::vector<SRawConnectionData>;
@@ -14,9 +14,6 @@
#include <AudioControlsLoader.h>
#include <AudioControlsWriter.h>
#include <CryFile.h>
#include <CryPath.h>
#include <Cry_Camera.h>
#include <Include/IResourceSelectorHost.h>
#include <IAudioSystem.h>
@@ -28,7 +25,6 @@
using namespace AudioControls;
using namespace PathUtil;
CATLControlsModel CAudioControlsEditorPlugin::ms_ATLModel;
QATLTreeModel CAudioControlsEditorPlugin::ms_layoutModel;
@@ -152,20 +148,18 @@ void CAudioControlsEditorPlugin::ExecuteTrigger(const AZStd::string_view sTrigge
Audio::AudioSystemRequestBus::BroadcastResult(ms_nAudioTriggerID, &Audio::AudioSystemRequestBus::Events::GetAudioTriggerID, sTriggerName.data());
if (ms_nAudioTriggerID != INVALID_AUDIO_CONTROL_ID)
{
const CCamera& camera = GetIEditor()->GetSystem()->GetViewCamera();
Audio::SAudioRequest request;
request.nFlags = Audio::eARF_PRIORITY_NORMAL;
const AZ::Matrix3x4 cameraMatrix = LYTransformToAZMatrix3x4(camera.GetMatrix());
const AZ::Matrix3x4 listenerTxfm = AZ::Matrix3x4::CreateIdentity();
Audio::SAudioListenerRequestData<Audio::eALRT_SET_POSITION> requestData(cameraMatrix);
Audio::SAudioListenerRequestData<Audio::eALRT_SET_POSITION> requestData(listenerTxfm);
requestData.oNewPosition.NormalizeForwardVec();
requestData.oNewPosition.NormalizeUpVec();
request.pData = &requestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, request);
ms_pIAudioProxy->SetPosition(cameraMatrix);
ms_pIAudioProxy->SetPosition(listenerTxfm);
ms_pIAudioProxy->ExecuteTrigger(ms_nAudioTriggerID);
}
}
@@ -9,21 +9,22 @@
#include <AudioControlsEditorWindow.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <ATLControlsModel.h>
#include <ATLControlsPanel.h>
#include <AudioControlsEditorPlugin.h>
#include <AudioControlsEditorUndo.h>
#include <AudioFileUtils.h>
#include <AudioSystemPanel.h>
#include <CryFile.h>
#include <CryPath.h>
#include <DockTitleBarWidget.h>
#include <IAudioSystem.h>
#include <ImplementationManager.h>
#include <InspectorPanel.h>
#include <ISystem.h>
#include <QAudioControlEditorIcons.h>
#include <Util/PathUtil.h>
#include <DockTitleBarWidget.h>
#include <QPaintEvent>
#include <QPushButton>
@@ -31,6 +32,7 @@
#include <QPainter>
#include <QMessageBox>
void InitACEResources()
{
Q_INIT_RESOURCE(AudioControlsEditorUI);
@@ -106,25 +108,16 @@ namespace AudioControls
{
m_fileSystemWatcher.addPath(folder.data());
AZStd::string search;
AZ::StringFunc::Path::Join(folder.data(), "*", search, true, false);
auto pCryPak = gEnv->pCryPak;
AZ::IO::ArchiveFileIterator handle = pCryPak->FindFirst(search.c_str());
if (handle)
auto fileIO = AZ::IO::FileIOBase::GetInstance();
auto foundFiles = Audio::FindFilesInPath(folder, "*");
for (auto& file : foundFiles)
{
do
if (fileIO->IsDirectory(file.c_str()))
{
AZStd::string sName = static_cast<AZStd::string_view>(handle.m_filename);
if (!sName.empty() && sName[0] != '.')
{
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
{
AZ::StringFunc::Path::Join(folder.data(), sName.c_str(), sName);
StartWatchingFolder(sName);
}
}
} while (handle = pCryPak->FindNext(handle));
pCryPak->FindClose(handle);
AZ::IO::FixedMaxPath resolvedPath;
fileIO->ReplaceAlias(resolvedPath, file);
StartWatchingFolder(file.Native());
}
}
}
@@ -318,19 +311,24 @@ namespace AudioControls
// once we can listen to delete messages from Asset system, this can be changed to an EBus handler.
const char* controlsPath = nullptr;
Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath);
AZStd::string sControlsPath(Path::GetEditingGameDataFolder());
AZ::StringFunc::Path::Join(sControlsPath.c_str(), controlsPath, sControlsPath);
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_CONTROLS_DATA> oParseGlobalRequestData(sControlsPath.c_str(), Audio::eADS_GLOBAL);
AZ::IO::FixedMaxPath controlsFolder{ controlsPath };
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_CONTROLS_DATA> oParseGlobalRequestData(controlsFolder.c_str(), Audio::eADS_GLOBAL);
oConfigDataRequest.pData = &oParseGlobalRequestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest);
// parse the AudioSystem level-specific config data
AZStd::string levelName{ GetIEditor()->GetLevelName().toUtf8().data() };
AZ::StringFunc::Path::Join(sControlsPath.c_str(), "levels", sControlsPath);
AZ::StringFunc::Path::Join(sControlsPath.c_str(), levelName.c_str(), sControlsPath);
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_CONTROLS_DATA> oParseLevelRequestData(sControlsPath.c_str(), Audio::eADS_LEVEL_SPECIFIC);
oConfigDataRequest.pData = &oParseLevelRequestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest);
AZStd::string levelName;
AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequests::GetLevelName);
if (!levelName.empty() && levelName != "Untitled")
{
controlsFolder /= "levels";
controlsFolder /= levelName;
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_CONTROLS_DATA> oParseLevelRequestData(controlsFolder.c_str(), Audio::eADS_LEVEL_SPECIFIC);
oConfigDataRequest.pData = &oParseLevelRequestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest);
}
// inform the middleware specific plugin that the data has been saved
// to disk (in case it needs to update something)
@@ -10,27 +10,21 @@
#include <AudioControlsLoader.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <ACEEnums.h>
#include <ATLCommon.h>
#include <ATLControlsModel.h>
#include <AudioFileUtils.h>
#include <IAudioSystem.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <QAudioControlTreeWidget.h>
#include <CryFile.h>
#include <CryPath.h>
#include <IEditor.h>
#include <ISystem.h>
#include <StringUtils.h>
#include <Util/PathUtil.h>
#include <Util/UndoUtil.h>
#include <QStandardItem>
using namespace PathUtil;
namespace AudioControls
{
//-------------------------------------------------------------------------------------------//
@@ -91,100 +85,81 @@ namespace AudioControls
{
const CUndoSuspend suspendUndo;
// Get the partial path (relative under asset root) where the controls live.
// Get the relative path (under asset root) where the controls live.
const char* controlsPath = nullptr;
Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath);
// Get the full path up to asset root.
AZStd::string controlsFullPath(Path::GetEditingGameDataFolder());
AZ::StringFunc::Path::Join(controlsFullPath.c_str(), controlsPath, controlsFullPath);
AZ::IO::FixedMaxPath controlsFullPath = AZ::Utils::GetProjectPath();
controlsFullPath /= controlsPath;
// load the global controls
LoadAllLibrariesInFolder(controlsFullPath, "");
LoadAllLibrariesInFolder(controlsFullPath.Native(), "");
// load the level specific controls
auto cryPak = gEnv->pCryPak;
AZ::IO::FixedMaxPath searchPath = controlsFullPath / LoaderStrings::LevelsSubFolder;
AZStd::string searchMask;
AZ::StringFunc::Path::Join(controlsFullPath.c_str(), LoaderStrings::LevelsSubFolder, searchMask);
AZ::StringFunc::Path::Join(searchMask.c_str(), "*", searchMask, true, false);
AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(searchMask.c_str());
if (handle)
auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*");
for (const auto& file : foundFiles)
{
do
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(file.c_str()))
{
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
AZStd::string levelName{ file.Filename().Native() };
LoadAllLibrariesInFolder(controlsFullPath.Native(), levelName);
if (!m_atlControlsModel->ScopeExists(levelName))
{
AZStd::string_view name = handle.m_filename;
if (name != "." && name != "..")
{
LoadAllLibrariesInFolder(controlsFullPath, name);
if (!m_atlControlsModel->ScopeExists(name))
{
// if the control doesn't exist it
// means it is not a real level in the
// project so it is flagged as LocalOnly
m_atlControlsModel->AddScope(name, true);
}
}
// If the scope doesn't exist it means it is not a real
// level in the project so it's flagged as LocalOnly
m_atlControlsModel->AddScope(levelName, true);
}
}
while (handle = cryPak->FindNext(handle));
cryPak->FindClose(handle);
}
CreateDefaultControls();
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsLoader::LoadAllLibrariesInFolder(const AZStd::string_view folderPath, const AZStd::string_view level)
{
AZStd::string path(folderPath);
if (path.back() != AZ_CORRECT_FILESYSTEM_SEPARATOR)
{
path.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
}
AZ::IO::FixedMaxPath searchPath{ folderPath };
if (!level.empty())
{
path.append(LoaderStrings::LevelsSubFolder);
path.append(GetSlash());
path.append(level);
path.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
searchPath /= LoaderStrings::LevelsSubFolder;
searchPath /= level;
}
AZStd::string searchPath = path + "*.xml";
auto cryPak = gEnv->pCryPak;
AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(searchPath.c_str());
if (handle)
auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*.xml");
for (auto& file : foundFiles)
{
do
Audio::ScopedXmlLoader xmlLoader(file.Native());
if (xmlLoader.HasError())
{
AZStd::string filename = path + AZStd::string{ static_cast<AZStd::string_view>(handle.m_filename) };
AZ::StringFunc::Path::Normalize(filename);
XmlNodeRef root = GetISystem()->LoadXmlFromFile(filename.c_str());
if (root)
AZ_Warning("AudioControlsLoader", false, "Unable to load the xml file '%s'", file.c_str());
continue;
}
auto xmlRootNode = xmlLoader.GetRootNode();
if (xmlRootNode && azstricmp(xmlRootNode->name(), Audio::ATLXmlTags::RootNodeTag) == 0)
{
AZ::IO::PathView fileName = file.Filename();
AZStd::to_lower(file.Native().begin(), file.Native().end());
m_loadedFilenames.insert(file.c_str());
if (auto nameAttr = xmlRootNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute, 0, false); nameAttr != nullptr)
{
AZStd::string tag = root->getTag();
if (tag == Audio::ATLXmlTags::RootNodeTag)
{
AZStd::to_lower(filename.begin(), filename.end());
m_loadedFilenames.insert(filename.c_str());
AZStd::string file = static_cast<AZStd::string_view>(handle.m_filename);
if (root->haveAttr(Audio::ATLXmlTags::ATLNameAttribute))
{
file = root->getAttr(Audio::ATLXmlTags::ATLNameAttribute);
}
AZ::StringFunc::Path::StripExtension(file);
LoadControlsLibrary(root, folderPath, level, file);
}
fileName = nameAttr->value();
}
else
{
CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "(Audio Controls Editor) Failed parsing ATL Library '%s'", filename.c_str());
fileName = fileName.Stem();
}
} while (handle = cryPak->FindNext(handle));
cryPak->FindClose(handle);
LoadControlsLibrary(xmlRootNode, folderPath, level, fileName.Native());
}
}
}
@@ -233,74 +208,93 @@ namespace AudioControls
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsLoader::LoadControlsLibrary(XmlNodeRef rootNode, [[maybe_unused]] const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName)
void CAudioControlsLoader::LoadControlsLibrary(
const AZ::rapidxml::xml_node<char>* rootNode,
[[maybe_unused]] const AZStd::string_view filePath,
const AZStd::string_view level,
const AZStd::string_view fileName)
{
QStandardItem* rootFolderItem = AddUniqueFolderPath(m_layoutModel->invisibleRootItem(), QString(fileName.data()));
if (rootFolderItem && rootNode)
{
const int numControlTypes = rootNode->getChildCount();
for (int i = 0; i < numControlTypes; ++i)
auto controlTypeNode = rootNode->first_node(); // e.g. "AudioTriggers", "AudioRtpcs", etc
while (controlTypeNode)
{
XmlNodeRef node = rootNode->getChild(i);
const int numControls = node->getChildCount();
for (int j = 0; j < numControls; ++j)
auto controlNode = controlTypeNode->first_node(); // e.g. "ATLTrigger", "ATLRtpc", etc
while (controlNode)
{
LoadControl(node->getChild(j), rootFolderItem, level);
LoadControl(controlNode, rootFolderItem, level);
controlNode = controlNode->next_sibling();
}
controlTypeNode = controlTypeNode->next_sibling();
}
}
}
//-------------------------------------------------------------------------------------------//
CATLControl* CAudioControlsLoader::LoadControl(XmlNodeRef node, QStandardItem* folderItem, const AZStd::string_view scope)
CATLControl* CAudioControlsLoader::LoadControl(AZ::rapidxml::xml_node<char>* node, QStandardItem* folderItem, const AZStd::string_view scope)
{
CATLControl* control = nullptr;
if (node)
AZStd::string controlPath;
if (auto controlPathAttr = node->first_attribute("path", 0, false);
controlPathAttr != nullptr)
{
QStandardItem* parentItem = AddUniqueFolderPath(folderItem, QString(node->getAttr(LoaderStrings::PathAttribute)));
if (parentItem)
controlPath = controlPathAttr->value();
}
QStandardItem* parentItem = AddUniqueFolderPath(folderItem, QString(controlPath.c_str()));
if (parentItem)
{
AZStd::string name;
if (auto nameAttr = node->first_attribute(Audio::ATLXmlTags::ATLNameAttribute, 0, false);
nameAttr != nullptr)
{
const AZStd::string name = node->getAttr(Audio::ATLXmlTags::ATLNameAttribute);
const EACEControlType controlType = TagToType(node->getTag());
name = nameAttr->value();
}
control = m_atlControlsModel->CreateControl(name, controlType);
if (control)
const EACEControlType controlType = TagToType(node->name());
control = m_atlControlsModel->CreateControl(name, controlType);
if (control)
{
QStandardItem* item = new QAudioControlItem(QString(control->GetName().c_str()), control);
if (item)
{
QStandardItem* item = new QAudioControlItem(QString(control->GetName().c_str()), control);
if (item)
{
parentItem->appendRow(item);
}
switch (controlType)
{
case eACET_SWITCH:
{
const int numStates = node->getChildCount();
for (int i = 0; i < numStates; ++i)
{
CATLControl* stateControl = LoadControl(node->getChild(i), item, scope);
if (stateControl)
{
stateControl->SetParent(control);
control->AddChild(stateControl);
}
}
break;
}
case eACET_PRELOAD:
{
LoadPreloadConnections(node, control);
break;
}
default:
{
LoadConnections(node, control);
break;
}
}
control->SetScope(scope);
parentItem->appendRow(item);
}
switch (controlType)
{
case eACET_SWITCH:
{
auto switchStateNode = node->first_node();
while (switchStateNode)
{
CATLControl* stateControl = LoadControl(switchStateNode, item, scope);
if (stateControl)
{
stateControl->SetParent(control);
control->AddChild(stateControl);
}
switchStateNode = switchStateNode->next_sibling();
}
break;
}
case eACET_PRELOAD:
{
LoadPreloadConnections(node, control);
break;
}
default:
{
LoadConnections(node, control);
break;
}
}
control->SetScope(scope);
}
}
@@ -310,44 +304,37 @@ namespace AudioControls
//-------------------------------------------------------------------------------------------//
void CAudioControlsLoader::LoadScopes()
{
AZStd::string levelsFolderPath;
AZ::StringFunc::Path::Join(Path::GetEditingGameDataFolder().c_str(), LoaderStrings::LevelsSubFolder, levelsFolderPath);
LoadScopesImpl(levelsFolderPath);
AZ::IO::FixedMaxPath levelsFolderPath = AZ::Utils::GetProjectPath();
levelsFolderPath /= "Levels";
LoadScopesImpl(levelsFolderPath.Native());
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsLoader::LoadScopesImpl(const AZStd::string_view levelsFolder)
{
AZStd::string search;
AZ::StringFunc::Path::Join(levelsFolder.data(), "*", search, true, false);
auto cryPak = gEnv->pCryPak;
AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(search.c_str());
if (handle)
auto fileIO = AZ::IO::FileIOBase::GetInstance();
AZ::IO::FixedMaxPath searchPath{ levelsFolder };
auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*");
for (auto& file : foundFiles)
{
do
AZ::IO::PathView filePath{ file };
AZ::IO::PathView fileName = filePath.Filename();
if (fileIO->IsDirectory(filePath.Native().data()))
{
AZStd::string name = static_cast<AZStd::string_view>(handle.m_filename);
if (name != "." && name != ".." && !name.empty())
LoadScopesImpl((searchPath / fileName).Native());
}
else
{
AZ::IO::PathView fileExt = filePath.Extension();
if (fileExt == ".ly" || fileExt == ".cry" || fileExt == ".prefab")
{
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
{
AZ::StringFunc::Path::Join(levelsFolder.data(), name.c_str(), search);
LoadScopesImpl(search);
}
else
{
AZStd::string extension;
AZ::StringFunc::Path::GetExtension(name.c_str(), extension, false);
if (extension.compare("cry") == 0 || extension.compare("ly") == 0)
{
AZ::StringFunc::Path::StripExtension(name);
m_atlControlsModel->AddScope(name);
}
}
AZ::IO::PathView fileStem = filePath.Stem();
// May need to verify that .prefabs are the actual "level" prefab
// i.e. that it matches levels/<levelname>/<levelname>.prefab
m_atlControlsModel->AddScope(fileStem.Native());
}
}
while (handle = cryPak->FindNext(handle));
cryPak->FindClose(handle);
}
}
@@ -475,100 +462,80 @@ namespace AudioControls
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsLoader::LoadConnections(XmlNodeRef rootNode, CATLControl* control)
void CAudioControlsLoader::LoadConnections(AZ::rapidxml::xml_node<char>* rootNode, CATLControl* control)
{
if (!rootNode || !control)
if (control && rootNode && m_audioSystemImpl)
{
return;
}
const int numChildren = rootNode->getChildCount();
for (int i = 0; i < numChildren; ++i)
{
XmlNodeRef node = rootNode->getChild(i);
const AZStd::string tag = node->getTag();
if (m_audioSystemImpl)
auto childNode = rootNode->first_node();
while (childNode)
{
TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(node, control->GetType());
TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(childNode, control->GetType());
if (connection)
{
control->AddConnection(connection);
}
control->m_connectionNodes.push_back(SRawConnectionData(node, connection != nullptr));
control->m_connectionNodes.push_back(SRawConnectionData(childNode, connection != nullptr));
childNode = childNode->next_sibling();
}
}
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsLoader::LoadPreloadConnections(XmlNodeRef node, CATLControl* control)
void CAudioControlsLoader::LoadPreloadConnections(AZ::rapidxml::xml_node<char>* node, CATLControl* control)
{
if (!node || !control)
if (!control || !node || !m_audioSystemImpl)
{
return;
}
AZStd::string type = node->getAttr(Audio::ATLXmlTags::ATLTypeAttribute);
if (type.compare(Audio::ATLXmlTags::ATLDataLoadType) == 0)
AZStd::string type;
if (auto typeAttr = node->first_attribute(Audio::ATLXmlTags::ATLTypeAttribute, 0, false);
typeAttr != nullptr)
{
control->SetAutoLoad(true);
}
else
{
control->SetAutoLoad(false);
type = typeAttr->value();
}
// Legacy Preload XML parsing...
// Read all the platform definitions for this control
XmlNodeRef platformsGroupNode = node->findChild(Audio::ATLXmlTags::ATLPlatformsTag);
if (platformsGroupNode)
control->SetAutoLoad(type == Audio::ATLXmlTags::ATLDataLoadType);
auto platformGroupNode = node->first_node(Audio::ATLXmlTags::ATLPlatformsTag, 0, false);
if (platformGroupNode)
{
// Legacy preload parsing...
// Don't parse the platform groups xml chunk anymore.
// Read the connection information for all connected preloads...
const int numChildren = node->getChildCount();
for (int i = 0; i < numChildren; ++i)
auto configGroupNode = node->first_node(Audio::ATLXmlTags::ATLConfigGroupTag, 0, false);
while (configGroupNode)
{
XmlNodeRef groupNode = node->getChild(i);
const AZStd::string tag = groupNode->getTag();
if (tag.compare(Audio::ATLXmlTags::ATLConfigGroupTag) != 0)
{
continue;
}
const AZStd::string groupName = groupNode->getAttr(Audio::ATLXmlTags::ATLNameAttribute);
const int numConnections = groupNode->getChildCount();
for (int j = 0; j < numConnections; ++j)
{
XmlNodeRef connectionNode = groupNode->getChild(j);
if (connectionNode && m_audioSystemImpl)
{
TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType());
if (connection)
{
control->AddConnection(connection);
}
control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr));
}
}
}
}
else
{
// New Preload XML parsing...
const int numChildren = node->getChildCount();
for (int i = 0; i < numChildren; ++i)
{
XmlNodeRef connectionNode = node->getChild(i);
if (connectionNode && m_audioSystemImpl)
auto connectionNode = configGroupNode->first_node();
while (connectionNode)
{
TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType());
if (connection)
{
control->AddConnection(connection);
}
control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr));
connectionNode = connectionNode->next_sibling();
}
configGroupNode = configGroupNode->next_sibling();
}
}
else
{
// New format preload parsing...
auto connectionNode = node->first_node();
while (connectionNode)
{
TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType());
if (connection)
{
control->AddConnection(connection);
}
control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr));
connectionNode = connectionNode->next_sibling();
}
}
}
@@ -590,11 +557,24 @@ namespace AudioControls
{
CATLControl* childControl = m_atlControlsModel->CreateControl(stateName, eACET_SWITCH_STATE, parentControl);
XmlNodeRef requestNode = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::ATLSwitchRequestTag);
requestNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, switchName.c_str());
XmlNodeRef valueNode = requestNode->createNode(Audio::ATLXmlTags::ATLValueTag);
valueNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, stateName.c_str());
requestNode->addChild(valueNode);
XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator);
AZ::rapidxml::xml_node<char>* requestNode =
xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLSwitchRequestTag));
AZ::rapidxml::xml_attribute<char>* switchNameAttr = xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(switchName.c_str()));
requestNode->append_attribute(switchNameAttr);
AZ::rapidxml::xml_node<char>* valueNode =
xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLValueTag));
AZ::rapidxml::xml_attribute<char>* stateNameAttr = xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(stateName.c_str()));
valueNode->append_attribute(stateNameAttr);
requestNode->append_node(valueNode);
childControl->m_connectionNodes.push_back(SRawConnectionData(requestNode, false));
return childControl;
@@ -10,12 +10,11 @@
#pragma once
#include <AzCore/std/containers/set.h>
#include <AzCore/XML/rapidxml.h>
#include <ACETypes.h>
#include <AudioControl.h>
#include <IXml.h>
#include <QString>
class QStandardItemModel;
@@ -38,11 +37,11 @@ namespace AudioControls
private:
void LoadAllLibrariesInFolder(const AZStd::string_view folderPath, const AZStd::string_view level);
void LoadControlsLibrary(XmlNodeRef rootNode, const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName);
CATLControl* LoadControl(XmlNodeRef node, QStandardItem* folderItem, const AZStd::string_view scope);
void LoadControlsLibrary(const AZ::rapidxml::xml_node<char>* rootNode, const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName);
CATLControl* LoadControl(AZ::rapidxml::xml_node<char>* node, QStandardItem* folderItem, const AZStd::string_view scope);
void LoadPreloadConnections(XmlNodeRef node, CATLControl* control);
void LoadConnections(XmlNodeRef rootNode, CATLControl* control);
void LoadPreloadConnections(AZ::rapidxml::xml_node<char>* node, CATLControl* control);
void LoadConnections(AZ::rapidxml::xml_node<char>* rootNode, CATLControl* control);
void CreateDefaultControls();
QStandardItem* AddControl(CATLControl* control, QStandardItem* folderItem);
@@ -9,29 +9,28 @@
#include <AudioControlsWriter.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/TextStreamWriters.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/XML/rapidxml_print.h>
#include <ACEEnums.h>
#include <ATLControlsModel.h>
#include <CryFile.h>
#include <IAudioSystem.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <IEditor.h>
#include <Include/IFileUtil.h>
#include <Include/ISourceControl.h>
#include <ISystem.h>
#include <StringUtils.h>
#include <Util/PathUtil.h>
#include <QModelIndex>
#include <QStandardItemModel>
#include <QFileInfo>
using namespace PathUtil;
namespace AudioControls
{
namespace WriterStrings
@@ -80,6 +79,18 @@ namespace AudioControls
index = index.sibling(++i, 0);
}
auto fileIO = AZ::IO::FileIOBase::GetInstance();
AZStd::for_each(
m_foundLibraryPaths.begin(), m_foundLibraryPaths.end(),
[fileIO](AZStd::string& libraryPath) -> void
{
if (auto newPathOpt = fileIO->ConvertToAlias(AZ::IO::PathView{ libraryPath });
newPathOpt.has_value())
{
libraryPath = newPathOpt.value().Native();
}
AZStd::to_lower(libraryPath.begin(), libraryPath.end());
});
// Delete libraries that don't exist anymore from disk
FilepathSet librariesToDelete;
@@ -103,7 +114,10 @@ namespace AudioControls
//-------------------------------------------------------------------------------------------//
void CAudioControlsWriter::WriteLibrary(const AZStd::string_view libraryName, QModelIndex root)
{
if (root.isValid())
const char* controlsPath = nullptr;
Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath);
if (root.isValid() && controlsPath)
{
TLibraryStorage library;
int i = 0;
@@ -114,68 +128,63 @@ namespace AudioControls
child = root.model()->index(++i, 0, root);
}
const char* controlsPath = nullptr;
Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath);
for (auto& libraryPair : library)
{
AZStd::string libraryPath;
AZ::IO::FixedMaxPath libraryPath{ controlsPath };
const AZStd::string& scope = libraryPair.first;
if (scope.empty())
{
// no scope, file at the root level
libraryPath.append(controlsPath);
AZ::StringFunc::Path::Join(libraryPath.c_str(), libraryName.data(), libraryPath);
libraryPath.append(WriterStrings::LibraryExtension);
libraryPath /= libraryName;
libraryPath.ReplaceExtension(WriterStrings::LibraryExtension);
}
else
{
// with scope, inside level folder
libraryPath.append(controlsPath);
libraryPath.append(WriterStrings::LevelsSubFolder);
AZ::StringFunc::Path::Join(libraryPath.c_str(), scope.c_str(), libraryPath);
AZ::StringFunc::Path::Join(libraryPath.c_str(), libraryName.data(), libraryPath);
libraryPath.append(WriterStrings::LibraryExtension);
libraryPath /= AZ::IO::FixedMaxPath{ WriterStrings::LevelsSubFolder } / scope / libraryName;
libraryPath.ReplaceExtension(WriterStrings::LibraryExtension);
}
// should be able to change this back to GamePathToFullPath once a path normalization bug has been fixed:
AZStd::string fullFilePath;
AZ::StringFunc::Path::Join(Path::GetEditingGameDataFolder().c_str(), libraryPath.c_str(), fullFilePath);
AZStd::to_lower(fullFilePath.begin(), fullFilePath.end());
AZ::IO::FixedMaxPath fullFilePath = AZ::Utils::GetProjectPath();
fullFilePath /= libraryPath;
m_foundLibraryPaths.insert(fullFilePath.c_str());
const SLibraryScope& libScope = libraryPair.second;
if (libScope.m_isDirty)
{
XmlNodeRef fileNode = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::RootNodeTag);
fileNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, libraryName.data());
XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator);
AZ::rapidxml::xml_node<char>* fileNode =
xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::RootNodeTag));
AZ::rapidxml::xml_attribute<char>* nameAttr = xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(libraryName.data()));
fileNode->append_attribute(nameAttr);
for (int ii = 0; ii < eACET_NUM_TYPES; ++ii)
{
if (ii != eACET_SWITCH_STATE) // switch_states are written inside the switches
if (libScope.m_nodes[ii] && libScope.m_nodes[ii]->first_node() != nullptr)
{
if (libScope.m_nodes[ii]->getChildCount() > 0)
{
fileNode->addChild(libScope.m_nodes[ii]);
}
fileNode->append_node(libScope.m_nodes[ii]);
}
}
if (QFileInfo::exists(fullFilePath.c_str()))
if (auto fileInfo = QFileInfo(fullFilePath.c_str());
fileInfo.exists())
{
const DWORD fileAttributes = GetFileAttributes(fullFilePath.c_str());
if (fileAttributes & FILE_ATTRIBUTE_READONLY)
if (!fileInfo.isWritable())
{
// file is read-only
CheckOutFile(fullFilePath);
// file exists and is read-only
CheckOutFile(fullFilePath.Native());
}
fileNode->saveToFile(fullFilePath.c_str());
[[maybe_unused]] bool writeOk = WriteXmlToFile(fullFilePath.Native(), fileNode);
}
else
{
// save the file, CheckOutFile will add it, since it's new
fileNode->saveToFile(fullFilePath.c_str());
CheckOutFile(fullFilePath);
// since it's a new file, save the file first, CheckOutFile will add it
[[maybe_unused]] bool writeOk = WriteXmlToFile(fullFilePath.Native(), fileNode);
CheckOutFile(fullFilePath.Native());
}
}
}
@@ -249,14 +258,63 @@ namespace AudioControls
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsWriter::WriteControlToXml(XmlNodeRef node, CATLControl* control, const AZStd::string_view path)
bool CAudioControlsWriter::WriteXmlToFile(const AZStd::string_view filepath, AZ::rapidxml::xml_node<char>* rootNode)
{
if (!rootNode)
{
return false;
}
using namespace AZ::IO;
AZStd::string docString;
ByteContainerStream stringStream(&docString);
AZ::rapidxml::xml_document<char> xmlDoc;
xmlDoc.append_node(rootNode);
RapidXMLStreamWriter streamWriter(&stringStream);
AZ::rapidxml::print(streamWriter.Iterator(), xmlDoc);
streamWriter.FlushCache();
constexpr int openMode =
(SystemFile::SF_OPEN_WRITE_ONLY | SystemFile::SF_OPEN_CREATE | SystemFile::SF_OPEN_CREATE_PATH);
if (SystemFile fileOut;
fileOut.Open(filepath.data(), openMode))
{
auto bytesWritten = fileOut.Write(docString.data(), docString.size());
return (bytesWritten == docString.size());
}
return false;
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsWriter::WriteControlToXml(AZ::rapidxml::xml_node<char>* node, CATLControl* control, const AZStd::string_view path)
{
if (!node || !control)
{
return;
}
XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator);
const EACEControlType type = control->GetType();
XmlNodeRef childNode = node->createNode(TypeToTag(type).data());
childNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, control->GetName().c_str());
AZStd::string_view typeName = TypeToTag(type);
AZ::rapidxml::xml_node<char>* childNode =
xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(typeName.data()));
AZ::rapidxml::xml_attribute<char>* nameAttr = xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(control->GetName().c_str()));
childNode->append_attribute(nameAttr);
if (!path.empty())
{
childNode->setAttr("path", path.data());
AZ::rapidxml::xml_attribute<char>* pathAttr = xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLPathAttribute), xmlAlloc.allocate_string(path.data()));
childNode->append_attribute(pathAttr);
}
if (type == eACET_SWITCH)
@@ -271,7 +329,11 @@ namespace AudioControls
{
if (control->IsAutoLoad())
{
childNode->setAttr(Audio::ATLXmlTags::ATLTypeAttribute, Audio::ATLXmlTags::ATLDataLoadType);
AZ::rapidxml::xml_attribute<char>* loadAttr = xmlAlloc.allocate_attribute(
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLTypeAttribute),
xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLDataLoadType));
childNode->append_attribute(loadAttr);
}
// New Preloads XML...
@@ -282,38 +344,33 @@ namespace AudioControls
WriteConnectionsToXml(childNode, control);
}
node->addChild(childNode);
node->append_node(childNode);
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsWriter::WriteConnectionsToXml(XmlNodeRef node, CATLControl* control)
void CAudioControlsWriter::WriteConnectionsToXml(AZ::rapidxml::xml_node<char>* node, CATLControl* control)
{
if (control && m_audioSystemImpl)
if (node && control && m_audioSystemImpl)
{
TXmlNodeList otherNodes = control->m_connectionNodes;
auto end = AZStd::remove_if(otherNodes.begin(), otherNodes.end(),
[](const SRawConnectionData& node)
{
return node.m_isValid;
}
);
otherNodes.erase(end, otherNodes.end());
for (auto& connectionNode : otherNodes)
for (auto& connectionNode : control->m_connectionNodes)
{
node->addChild(connectionNode.m_xmlNode);
if (!connectionNode.m_isValid)
{
auto nodeCopy = SRawConnectionData::DeepCopyNode(connectionNode.m_xmlNode.get());
node->append_node(nodeCopy.release());
}
}
const size_t size = control->ConnectionCount();
for (size_t i = 0; i < size; ++i)
{
TConnectionPtr connection = control->GetConnectionAt(i);
if (connection)
if (TConnectionPtr connection = control->GetConnectionAt(i);
connection != nullptr)
{
XmlNodeRef childNode = m_audioSystemImpl->CreateXMLNodeFromConnection(connection, control->GetType());
if (childNode)
if (auto childNode = m_audioSystemImpl->CreateXMLNodeFromConnection(connection, control->GetType());
childNode != nullptr)
{
node->addChild(childNode);
node->append_node(childNode);
control->m_connectionNodes.push_back(SRawConnectionData(childNode, true));
}
}
@@ -322,24 +379,24 @@ namespace AudioControls
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsWriter::CheckOutFile(const AZStd::string& filepath)
void CAudioControlsWriter::CheckOutFile(const AZStd::string_view filepath)
{
IEditor* editor = GetIEditor();
IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr;
if (fileUtil)
{
fileUtil->CheckoutFile(filepath.c_str(), nullptr);
fileUtil->CheckoutFile(AZ::IO::FixedMaxPath{ filepath }.c_str(), nullptr);
}
}
//-------------------------------------------------------------------------------------------//
void CAudioControlsWriter::DeleteLibraryFile(const AZStd::string& filepath)
void CAudioControlsWriter::DeleteLibraryFile(const AZStd::string_view filepath)
{
IEditor* editor = GetIEditor();
IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr;
if (fileUtil)
{
fileUtil->DeleteFromSourceControl(filepath.c_str(), nullptr);
fileUtil->DeleteFromSourceControl(AZ::IO::FixedMaxPath{ filepath }.c_str(), nullptr);
}
}
@@ -16,10 +16,9 @@
#include <ACETypes.h>
#include <ATLCommon.h>
#include <AudioControl.h>
#include <ISystem.h>
#include <QModelIndex>
#include <IXml.h>
class QStandardItemModel;
@@ -33,15 +32,16 @@ namespace AudioControls
{
SLibraryScope()
{
m_nodes[eACET_TRIGGER] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::TriggersNodeTag);
m_nodes[eACET_RTPC] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::RtpcsNodeTag);
m_nodes[eACET_SWITCH] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::SwitchesNodeTag);
XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator);
m_nodes[eACET_TRIGGER] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::TriggersNodeTag);
m_nodes[eACET_RTPC] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::RtpcsNodeTag);
m_nodes[eACET_SWITCH] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::SwitchesNodeTag);
m_nodes[eACET_SWITCH_STATE] = nullptr;
m_nodes[eACET_ENVIRONMENT] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::EnvironmentsNodeTag);
m_nodes[eACET_PRELOAD] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::PreloadsNodeTag);
m_nodes[eACET_ENVIRONMENT] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::EnvironmentsNodeTag);
m_nodes[eACET_PRELOAD] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::PreloadsNodeTag);
}
XmlNodeRef m_nodes[eACET_NUM_TYPES];
AZ::rapidxml::xml_node<char>* m_nodes[eACET_NUM_TYPES];
bool m_isDirty = false;
};
@@ -56,12 +56,13 @@ namespace AudioControls
private:
void WriteLibrary(const AZStd::string_view libraryName, QModelIndex root);
void WriteItem(QModelIndex index, const AZStd::string& path, TLibraryStorage& library, bool isParentModified);
void WriteControlToXml(XmlNodeRef node, CATLControl* control, const AZStd::string_view path);
void WriteConnectionsToXml(XmlNodeRef node, CATLControl* control);
void WriteControlToXml(AZ::rapidxml::xml_node<char>* node, CATLControl* control, const AZStd::string_view path);
void WriteConnectionsToXml(AZ::rapidxml::xml_node<char>* node, CATLControl* control);
bool IsItemModified(QModelIndex index);
void CheckOutFile(const AZStd::string& filepath);
void DeleteLibraryFile(const AZStd::string& filepath);
bool WriteXmlToFile(const AZStd::string_view filepath, AZ::rapidxml::xml_node<char>* rootNode);
void CheckOutFile(const AZStd::string_view filepath);
void DeleteLibraryFile(const AZStd::string_view filepath);
CATLControlsModel* m_atlModel;
QStandardItemModel* m_layoutModel;
@@ -9,7 +9,6 @@
#include <ATLControlsResourceDialog.h>
#include <AudioControlsEditorPlugin.h>
#include <IEditor.h>
#include <Include/IResourceSelectorHost.h>
#include <QAudioControlEditorIcons.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
@@ -12,10 +12,7 @@
#include <ATLControlsModel.h>
#include <AudioControl.h>
#include <AudioControlsEditorPlugin.h>
#include <CryFile.h>
#include <CryPath.h>
#include <IAudioSystemEditor.h>
#include <IEditor.h>
#include <QAudioControlEditorIcons.h>
#include <QWidgetAction>
@@ -14,8 +14,6 @@
#include <ATLControlsModel.h>
#include <AudioControlsEditorPlugin.h>
#include <IAudioSystemEditor.h>
#include <IConsole.h>
#include <IEditor.h>
//-----------------------------------------------------------------------------------------------//
@@ -10,9 +10,6 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <QObject>
#endif
@@ -12,7 +12,6 @@
#include <ACETypes.h>
#include <AudioControlsEditorPlugin.h>
#include <IAudioSystemControl.h>
#include <IEditor.h>
#include <QAudioControlEditorIcons.h>
#include <QMessageBox>
@@ -31,7 +31,7 @@ namespace AudioControls
iconFile = ":/Icons/Switch_Icon.svg";
break;
case AudioControls::eACET_SWITCH_STATE:
iconFile = ":/Icons/Property_Icon.svg";
iconFile = ":/Icons/Property_Icon.png";
break;
case AudioControls::eACET_ENVIRONMENT:
iconFile = ":/Icons/Environment_Icon.svg";
@@ -41,7 +41,7 @@ namespace AudioControls
break;
default:
// should make a "default"/empty icon...
iconFile = ":/Icons/RTPC_Icon.svg";
iconFile = ":/Icons/Unassigned.svg";
}
QIcon icon(iconFile);
@@ -14,7 +14,6 @@
#include <AudioControlsEditorPlugin.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <IEditor.h>
#include <ImplementationManager.h>
#include <QDropEvent>
@@ -1005,14 +1005,14 @@ namespace Audio
AZStd::string searchPath;
AZ::StringFunc::Path::Join(m_rootPath.c_str(), folderPath, searchPath);
AZStd::vector<AZStd::string> foundFiles = Audio::FindFilesInPath(searchPath, "*.xml");
auto foundFiles = Audio::FindFilesInPath(searchPath, "*.xml");
for (const auto& file : foundFiles)
{
AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(file.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", file.c_str());
g_audioLogger.Log(eALT_ALWAYS, "Loading Audio Controls Library: '%s'", file.c_str());
Audio::ScopedXmlLoader xmlFileLoader(file);
Audio::ScopedXmlLoader xmlFileLoader(file.Native());
if (xmlFileLoader.HasError())
{
continue;
@@ -1053,14 +1053,14 @@ namespace Audio
AZStd::string searchPath;
AZ::StringFunc::Path::Join(m_rootPath.c_str(), folderPath, searchPath);
AZStd::vector<AZStd::string> foundFiles = Audio::FindFilesInPath(searchPath, "*.xml");
auto foundFiles = Audio::FindFilesInPath(searchPath, "*.xml");
for (const auto& file : foundFiles)
{
AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(file.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", file.c_str());
g_audioLogger.Log(eALT_ALWAYS, "Loading Audio Preloads Library: '%s'", file.c_str());
Audio::ScopedXmlLoader xmlFileLoader(file);
Audio::ScopedXmlLoader xmlFileLoader(file.Native());
if (xmlFileLoader.HasError())
{
continue;
@@ -23,6 +23,9 @@ namespace Audio
extern CAudioLogger g_audioLogger;
static constexpr const char AudioControlsBasePath[]{ "libs/gameaudio/" };
// Save off the threadId of the "Main Thread" that was used to connect EBuses.
AZStd::thread_id g_mainThreadId;
///////////////////////////////////////////////////////////////////////////////////////////////////
// CAudioThread
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,6 +80,8 @@ namespace Audio
CAudioSystem::CAudioSystem()
: m_bSystemInitialized(false)
{
g_mainThreadId = AZStd::this_thread::get_id();
m_apAudioProxies.reserve(Audio::CVars::s_AudioObjectPoolSize);
m_apAudioProxiesToBeFreed.reserve(16);
m_controlsPath.assign(Audio::AudioControlsBasePath);
@@ -99,7 +104,7 @@ namespace Audio
{
CAudioRequestInternal request(audioRequestData);
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::PushRequest - called from non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::PushRequest - called from non-Main thread!");
AZ_Assert(0 == (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequest - called with flag THREAD_SAFE_PUSH!");
AZ_Assert(0 == (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequest - called with flag EXECUTE_BLOCKING!");
@@ -114,7 +119,7 @@ namespace Audio
CAudioRequestInternal request(audioRequestData);
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::PushRequestBlocking - called from non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::PushRequestBlocking - called from non-Main thread!");
AZ_Assert(0 != (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequestBlocking - called without EXECUTE_BLOCKING flag!");
AZ_Assert(0 == (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequestBlocking - called with THREAD_SAFE_PUSH flag!");
@@ -139,7 +144,7 @@ namespace Audio
const EAudioRequestType requestType,
const TATLEnumFlagsType specificRequestMask)
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::AddRequestListener - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::AddRequestListener - called from a non-Main thread!");
if (func)
{
@@ -155,7 +160,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
void CAudioSystem::RemoveRequestListener(AudioRequestCallbackType func, void* const callbackOwner)
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::RemoveRequestListener - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RemoveRequestListener - called from a non-Main thread!");
SAudioEventListener listener;
listener.m_callbackOwner = callbackOwner;
@@ -167,7 +172,7 @@ namespace Audio
void CAudioSystem::ExternalUpdate()
{
// Main Thread!
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::ExternalUpdate - called from non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ExternalUpdate - called from non-Main thread!");
// Notify callbacks on the pending callbacks queue...
// These are requests that were completed then queued for callback processing to happen here.
@@ -242,7 +247,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CAudioSystem::Initialize()
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::Initialize - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Initialize - called from a non-Main thread!");
if (!m_bSystemInitialized)
{
@@ -265,7 +270,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
void CAudioSystem::Release()
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::Release - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Release - called from a non-Main thread!");
for (auto audioProxy : m_apAudioProxies)
{
@@ -331,14 +336,14 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CAudioSystem::ReserveAudioListenerID(TAudioObjectID& rAudioObjectID)
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::ReserveAudioListenerID - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReserveAudioListenerID - called from a non-Main thread!");
return m_oATL.ReserveAudioListenerID(rAudioObjectID);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
bool CAudioSystem::ReleaseAudioListenerID(TAudioObjectID const nAudioObjectID)
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::ReleaseAudioListenerID - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReleaseAudioListenerID - called from a non-Main thread!");
return m_oATL.ReleaseAudioListenerID(nAudioObjectID);
}
@@ -385,7 +390,7 @@ namespace Audio
void CAudioSystem::RefreshAudioSystem([[maybe_unused]] const char* const levelName)
{
#if !defined(AUDIO_RELEASE)
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::RefreshAudioSystem - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RefreshAudioSystem - called from a non-Main thread!");
// Get the controls path and a level-specific preload Id first.
// This will be passed with the request so that it doesn't have to lookup this data
@@ -409,7 +414,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
IAudioProxy* CAudioSystem::GetFreeAudioProxy()
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::GetFreeAudioProxy - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetFreeAudioProxy - called from a non-Main thread!");
CAudioProxy* audioProxy = nullptr;
if (!m_apAudioProxies.empty())
@@ -435,7 +440,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
void CAudioSystem::FreeAudioProxy(IAudioProxy* const audioProxyI)
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::FreeAudioProxy - called from a non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::FreeAudioProxy - called from a non-Main thread!");
auto const audioProxy = static_cast<CAudioProxy*>(audioProxyI);
if (AZStd::find(m_apAudioProxiesToBeFreed.begin(), m_apAudioProxiesToBeFreed.end(), audioProxy) != m_apAudioProxiesToBeFreed.end() || AZStd::find(m_apAudioProxies.begin(), m_apAudioProxies.end(), audioProxy) != m_apAudioProxies.end())
@@ -469,7 +474,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
const char* CAudioSystem::GetAudioControlName([[maybe_unused]] const EAudioControlType controlType, [[maybe_unused]] const TATLIDType atlID) const
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::GetAudioControlName - called from non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioControlName - called from non-Main thread!");
const char* sResult = nullptr;
#if !defined(AUDIO_RELEASE)
@@ -524,7 +529,7 @@ namespace Audio
///////////////////////////////////////////////////////////////////////////////////////////////////
const char* CAudioSystem::GetAudioSwitchStateName([[maybe_unused]] const TAudioControlID switchID, [[maybe_unused]] const TAudioSwitchStateID stateID) const
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::GetAudioSwitchStateName - called from non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioSwitchStateName - called from non-Main thread!");
const char* sResult = nullptr;
#if !defined(AUDIO_RELEASE)
@@ -638,7 +643,7 @@ namespace Audio
AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::Audio, "Normal Request: %s", request.ToString().c_str());
AZ_Assert(gEnv->mMainThreadId != CryGetCurrentThreadId(), "AudioSystem::ProcessRequestByPriority - called from Main thread!");
AZ_Assert(g_mainThreadId != AZStd::this_thread::get_id(), "AudioSystem::ProcessRequestByPriority - called from Main thread!");
if (m_oATL.CanProcessRequests())
{
@@ -698,7 +703,7 @@ namespace Audio
#if !defined(AUDIO_RELEASE)
void CAudioSystem::DrawAudioDebugData()
{
AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::DrawAudioDebugData - called from non-Main thread!");
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::DrawAudioDebugData - called from non-Main thread!");
if (CVars::s_debugDrawOptions.GetRawFlags() != 0)
{
@@ -9,12 +9,12 @@
#include <FileCacheManager.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/IStreamer.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Archive/IArchive.h>
#include <AudioAllocators.h>
#include <AudioInternalInterfaces.h>
@@ -115,9 +115,9 @@ namespace Audio
newAudioFileEntry->m_dataScope = dataScope;
AZStd::to_lower(newAudioFileEntry->m_filePath.begin(), newAudioFileEntry->m_filePath.end());
const size_t fileSize = gEnv->pCryPak->FGetSize(newAudioFileEntry->m_filePath.c_str());
if (fileSize > 0)
auto fileIO = AZ::IO::FileIOBase::GetInstance();
if (AZ::u64 fileSize = 0;
fileIO->Size(newAudioFileEntry->m_filePath.c_str(), fileSize) && fileSize != 0)
{
newAudioFileEntry->m_fileSize = fileSize;
newAudioFileEntry->m_flags.ClearFlags(eAFF_NOTFOUND);
@@ -770,9 +770,12 @@ namespace Audio
}
AZStd::to_lower(audioFileEntry->m_filePath.begin(), audioFileEntry->m_filePath.end());
audioFileEntry->m_fileSize = gEnv->pCryPak->FGetSize(audioFileEntry->m_filePath.c_str());
AZ::u64 fileSize = 0;
auto fileIO = AZ::IO::FileIOBase::GetInstance();
fileIO->Size(audioFileEntry->m_filePath.c_str(), fileSize);
audioFileEntry->m_fileSize = fileSize;
AZ_Assert(audioFileEntry->m_fileSize > 0, "FileCacheManager - UpdateLocalizedFileEntryData expected file size to be greater than zero!");
AZ_Assert(audioFileEntry->m_fileSize != 0, "FileCacheManager - UpdateLocalizedFileEntryData expected file size to be greater than zero!");
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -10,54 +10,45 @@
#include <AzCore/base.h>
#include <AzCore/Memory/AllocatorScope.h>
#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
#include <AudioControlsLoader.h>
#include <ATLControlsModel.h>
#include <platform.h>
#include <ISystem.h>
#include <Mocks/ICryPakMock.h>
using ::testing::NiceMock;
using namespace AudioControls;
namespace CustomMocks
{
class AudioControlsEditorTest_CryPakMock
: public CryPakMock
class AudioControlsEditorTest_FileIOMock
: public AZ::IO::MockFileIOBase
{
public:
AZ_TEST_CLASS_ALLOCATOR(AudioControlsEditorTest_CryPakMock)
AZ_TEST_CLASS_ALLOCATOR(AudioControlsEditorTest_FileIOMock);
AudioControlsEditorTest_CryPakMock(const char* levelName)
: m_levelName(levelName)
{}
AZ::IO::ArchiveFileIterator FindFirst([[maybe_unused]] AZStd::string_view dir, AZ::IO::IArchive::EFileSearchType) override
AudioControlsEditorTest_FileIOMock()
{
AZ::IO::FileDesc fileDesc;
fileDesc.nSize = sizeof(AZ::IO::FileDesc);
// Add a filename and file description reference to the TestFindData map to make sure the file iterator is valid
m_findData = new TestFindData();
m_findData->m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ static_cast<AZ::IO::FindData*>(m_findData.get()), m_levelName, fileDesc });
return m_findData->Fetch();
}
AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator iter) override
bool IsDirectory([[maybe_unused]] const char* path) override
{
return ++iter;
return false;
}
AZ::IO::Result FindFiles(
[[maybe_unused]] const char* path,
[[maybe_unused]] const char* filter,
AZ::IO::FileIOBase::FindFilesCallbackType callback) override
{
if (callback)
{
callback(m_levelName.c_str());
return AZ::IO::ResultCode::Success;
}
return AZ::IO::ResultCode::Error;
}
// public: for easy resetting...
AZStd::string m_levelName;
// Add an inherited FindData class to control the adding of a mapfile which indicates that a FileIterator is valid
struct TestFindData
: AZ::IO::FindData
{
using AZ::IO::FindData::m_fileSet;
};
AZStd::intrusive_ptr<TestFindData> m_findData;
};
} // namespace CustomMocks
@@ -75,10 +66,6 @@ protected:
void SetupEnvironment() override
{
m_allocatorScope.ActivateAllocators();
m_stubEnv.pCryPak = nullptr;
m_stubEnv.pFileIO = nullptr;
gEnv = &m_stubEnv;
}
void TeardownEnvironment() override
@@ -87,30 +74,68 @@ protected:
}
private:
AZ::AllocatorScope<AZ::OSAllocator, AZ::SystemAllocator, AZ::LegacyAllocator, CryStringAllocator> m_allocatorScope;
SSystemGlobalEnvironment m_stubEnv;
AZ::AllocatorScope<AZ::OSAllocator, AZ::SystemAllocator> m_allocatorScope;
};
AZ_UNIT_TEST_HOOK(new AudioControlsEditorTestEnvironment);
TEST(AudioControlsEditorTest, AudioControlsLoader_LoadScopes_ScopesAreAdded)
class AudioControlsEditorTest
: public ::testing::Test
{
ASSERT_TRUE(gEnv != nullptr);
ASSERT_TRUE(gEnv->pCryPak == nullptr);
public:
void SetUp() override
{
// Store and remove the existing fileIO...
m_prevFileIO = AZ::IO::FileIOBase::GetInstance();
if (m_prevFileIO)
{
AZ::IO::FileIOBase::SetInstance(nullptr);
}
NiceMock<CustomMocks::AudioControlsEditorTest_CryPakMock> m_cryPakMock("ly_extension.ly");
gEnv->pCryPak = &m_cryPakMock;
// Replace with a new FileIO Mock...
m_fileIO = AZStd::make_unique<CustomMocks::AudioControlsEditorTest_FileIOMock>();
AZ::IO::FileIOBase::SetInstance(m_fileIO.get());
}
void TearDown() override
{
// Destroy our LocalFileIO...
m_fileIO.reset();
// Replace the old fileIO (set instance to null first)...
AZ::IO::FileIOBase::SetInstance(nullptr);
if (m_prevFileIO)
{
AZ::IO::FileIOBase::SetInstance(m_prevFileIO);
m_prevFileIO = nullptr;
}
}
protected:
AZ::IO::FileIOBase* m_prevFileIO = nullptr;
AZStd::unique_ptr<CustomMocks::AudioControlsEditorTest_FileIOMock> m_fileIO;
};
TEST_F(AudioControlsEditorTest, AudioControlsLoader_LoadScopes_ScopesAreAdded)
{
CATLControlsModel atlModel;
CAudioControlsLoader loader(&atlModel, nullptr, nullptr);
m_fileIO->m_levelName = "ly_extension.ly";
loader.LoadScopes();
EXPECT_TRUE(atlModel.ScopeExists("ly_extension"));
m_cryPakMock.m_levelName = "cry_extension.cry";
m_fileIO->m_levelName = "cry_extension.cry";
loader.LoadScopes();
EXPECT_TRUE(atlModel.ScopeExists("cry_extension"));
m_fileIO->m_levelName = "prefab_extension.prefab";
loader.LoadScopes();
EXPECT_TRUE(atlModel.ScopeExists("prefab_extension"));
m_fileIO->m_levelName = "spawnable_extension.spawnable";
loader.LoadScopes();
EXPECT_FALSE(atlModel.ScopeExists("spawnable_extension"));
atlModel.ClearScopes();
gEnv->pCryPak = nullptr;
}
@@ -103,9 +103,15 @@ namespace Camera
->Event("SetNearClipDistance", &CameraRequestBus::Events::SetNearClipDistance)
->Event("SetFarClipDistance", &CameraRequestBus::Events::SetFarClipDistance)
->Event("MakeActiveView", &CameraRequestBus::Events::MakeActiveView)
->Event("IsOrthographic", &CameraRequestBus::Events::IsOrthographic)
->Event("SetOrthographic", &CameraRequestBus::Events::SetOrthographic)
->Event("GetOrthographicHalfWidth", &CameraRequestBus::Events::GetOrthographicHalfWidth)
->Event("SetOrthographicHalfWidth", &CameraRequestBus::Events::SetOrthographicHalfWidth)
->VirtualProperty("FieldOfView","GetFovDegrees","SetFovDegrees")
->VirtualProperty("NearClipDistance", "GetNearClipDistance", "SetNearClipDistance")
->VirtualProperty("FarClipDistance", "GetFarClipDistance", "SetFarClipDistance")
->VirtualProperty("Orthographic", "IsOrthographic", "SetOrthographic")
->VirtualProperty("OrthographicHalfWidth", "GetOrthographicHalfWidth", "SetOrthographicHalfWidth")
;
behaviorContext->Class<CameraComponent>()->RequestBus("CameraRequestBus");
@@ -24,7 +24,9 @@ namespace Camera
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CameraComponentConfig, AZ::ComponentConfig>()
->Version(2)
->Version(3)
->Field("Orthographic", &CameraComponentConfig::m_orthographic)
->Field("Orthographic Half Width", &CameraComponentConfig::m_orthographicHalfWidth)
->Field("Field of View", &CameraComponentConfig::m_fov)
->Field("Near Clip Plane Distance", &CameraComponentConfig::m_nearClipDistance)
->Field("Far Clip Plane Distance", &CameraComponentConfig::m_farClipDistance)
@@ -42,25 +44,33 @@ namespace Camera
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_makeActiveViewOnActivation,
"Make active camera on activation?", "If true, this camera will become the active render camera when it activates")
->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_orthographic, "Orthographic",
"If set, this camera will use an orthographic projection instead of a perspective one. Objects will appear as the same size, regardless of distance from the camera.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_orthographicHalfWidth, "Orthographic Half-width", "The half-width used to calculate the orthographic projection. The height will be determined by the aspect ratio.")
->Attribute(AZ::Edit::Attributes::Visibility, &CameraComponentConfig::GetOrthographicParameterVisibility)
->Attribute(AZ::Edit::Attributes::Min, 0.001f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_fov, "Field of view", "Vertical field of view in degrees")
->Attribute(AZ::Edit::Attributes::Min, MIN_FOV)
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
->Attribute(AZ::Edit::Attributes::Step, 1.f)
->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::Pi) - 0.0001f) //We assert at fovs >= Pi so set the max for this field to be just under that
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshValues", 0x28e720d4))
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(AZ::Edit::Attributes::Visibility, &CameraComponentConfig::GetPerspectiveParameterVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_nearClipDistance, "Near clip distance",
"Distance to the near clip plane of the view Frustum")
->Attribute(AZ::Edit::Attributes::Min, CAMERA_MIN_NEAR)
->Attribute(AZ::Edit::Attributes::Suffix, " m")
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
->Attribute(AZ::Edit::Attributes::Max, &CameraComponentConfig::GetFarClipDistance)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c))
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_farClipDistance, "Far clip distance",
"Distance to the far clip plane of the view Frustum")
->Attribute(AZ::Edit::Attributes::Min, &CameraComponentConfig::GetNearClipDistance)
->Attribute(AZ::Edit::Attributes::Suffix, " m")
->Attribute(AZ::Edit::Attributes::Step, 10.f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c))
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
;
}
}
@@ -81,6 +91,16 @@ namespace Camera
return AZ::EntityId(m_editorEntityId);
}
AZ::u32 CameraComponentConfig::GetPerspectiveParameterVisibility() const
{
return m_orthographic ? AZ::Edit::PropertyVisibility::Hide : AZ::Edit::PropertyVisibility::Show;
}
AZ::u32 CameraComponentConfig::GetOrthographicParameterVisibility() const
{
return m_orthographic ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
CameraComponentController::CameraComponentController(const CameraComponentConfig& config)
{
SetConfiguration(config);
@@ -289,6 +309,16 @@ namespace Camera
return m_config;
}
AZ::RPI::ViewportContextPtr CameraComponentController::GetViewportContext()
{
auto atomViewportRequests = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
if (m_atomCamera && atomViewportRequests)
{
return atomViewportRequests->GetDefaultViewportContext();
}
return nullptr;
}
AZ::EntityId CameraComponentController::GetCameras()
{
return m_entityId;
@@ -324,6 +354,16 @@ namespace Camera
return m_config.m_frustumHeight;
}
bool CameraComponentController::IsOrthographic()
{
return m_config.m_orthographic;
}
float CameraComponentController::GetOrthographicHalfWidth()
{
return m_config.m_orthographicHalfWidth;
}
void CameraComponentController::SetFovDegrees(float fov)
{
m_config.m_fov = AZ::GetClamp(fov, MinFoV, MaxFoV);
@@ -359,6 +399,18 @@ namespace Camera
UpdateCamera();
}
void CameraComponentController::SetOrthographic(bool orthographic)
{
m_config.m_orthographic = orthographic;
UpdateCamera();
}
void CameraComponentController::SetOrthographicHalfWidth(float halfWidth)
{
m_config.m_orthographicHalfWidth = halfWidth;
UpdateCamera();
}
void CameraComponentController::MakeActiveView()
{
// Set Legacy Cry view, if it exists
@@ -423,30 +475,38 @@ namespace Camera
m_view->SetCurrentParams(viewParams);
}
auto atomViewportRequests = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
if (m_atomCamera && atomViewportRequests)
if (auto viewportContext = GetViewportContext())
{
AZ::Matrix4x4 viewToClipMatrix;
float aspectRatio = m_view ? m_view->GetCamera().GetPixelAspectRatio() : 1.f;
auto viewportContext = atomViewportRequests->GetViewportContextByName(
atomViewportRequests->GetDefaultViewportContextName());
if (viewportContext)
if (!m_atomAuxGeom)
{
if (!m_atomAuxGeom)
{
SetupAtomAuxGeom(viewportContext);
}
auto windowSize = viewportContext->GetViewportSize();
aspectRatio = aznumeric_cast<float>(windowSize.m_width) / aznumeric_cast<float>(windowSize.m_height);
SetupAtomAuxGeom(viewportContext);
}
auto windowSize = viewportContext->GetViewportSize();
aspectRatio = aznumeric_cast<float>(windowSize.m_width) / aznumeric_cast<float>(windowSize.m_height);
// This assumes a reversed depth buffer, in line with other LY Atom integration
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix,
AZ::DegToRad(m_config.m_fov),
aspectRatio,
m_config.m_nearClipDistance,
m_config.m_farClipDistance,
true);
if (m_config.m_orthographic)
{
AZ::MakeOrthographicMatrixRH(viewToClipMatrix,
-m_config.m_orthographicHalfWidth,
m_config.m_orthographicHalfWidth,
-m_config.m_orthographicHalfWidth / aspectRatio,
m_config.m_orthographicHalfWidth / aspectRatio,
m_config.m_nearClipDistance,
m_config.m_farClipDistance,
true);
}
else
{
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix,
AZ::DegToRad(m_config.m_fov),
aspectRatio,
m_config.m_nearClipDistance,
m_config.m_farClipDistance,
true);
}
m_updatingTransformFromEntity = true;
m_atomCamera->SetViewToClipMatrix(viewToClipMatrix);
m_updatingTransformFromEntity = false;
@@ -40,6 +40,9 @@ namespace Camera
float GetNearClipDistance() const;
AZ::EntityId GetEditorEntityId() const;
AZ::u32 GetPerspectiveParameterVisibility() const;
AZ::u32 GetOrthographicParameterVisibility() const;
// Reflected members
float m_fov = DefaultFoV;
float m_nearClipDistance = DefaultNearPlaneDistance;
@@ -49,6 +52,8 @@ namespace Camera
bool m_specifyFrustumDimensions = false;
AZ::u64 m_editorEntityId = AZ::EntityId::InvalidEntityId;
bool m_makeActiveViewOnActivation = true;
bool m_orthographic = false;
float m_orthographicHalfWidth = 5.f;
};
class CameraComponentController
@@ -78,6 +83,7 @@ namespace Camera
void Deactivate();
void SetConfiguration(const CameraComponentConfig& config);
const CameraComponentConfig& GetConfiguration() const;
AZ::RPI::ViewportContextPtr GetViewportContext();
// CameraBus::Handler interface
AZ::EntityId GetCameras() override;
@@ -89,12 +95,17 @@ namespace Camera
float GetFarClipDistance() override;
float GetFrustumWidth() override;
float GetFrustumHeight() override;
bool IsOrthographic() override;
float GetOrthographicHalfWidth() override;
void SetFovDegrees(float fov) override;
void SetFovRadians(float fov) override;
void SetNearClipDistance(float nearClipDistance) override;
void SetFarClipDistance(float farClipDistance) override;
void SetFrustumWidth(float width) override;
void SetFrustumHeight(float height) override;
void SetOrthographic(bool orthographic) override;
void SetOrthographicHalfWidth(float halfWidth) override;
void MakeActiveView() override;
// AZ::TransformNotificationBus::Handler interface

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