Merge branch 'main' into non-uniform-scale-visibility

This commit is contained in:
greerdv
2021-04-15 14:17:11 +01:00
2683 changed files with 45449 additions and 543490 deletions
@@ -82,9 +82,6 @@
static const char* s_azFrameworkWarningWindow = "AzFramework";
static const char* s_engineConfigFileName = "engine.json";
static const char* s_engineConfigEngineVersionKey = "LumberyardVersion";
namespace AzFramework
{
namespace ApplicationInternal
@@ -264,24 +261,8 @@ namespace AzFramework
void Application::PreModuleLoad()
{
// Calculate the engine root by reading the engine.json file
AZStd::string engineJsonPath = AZStd::string_view{ m_engineRoot };
engineJsonPath += s_engineConfigFileName;
AzFramework::StringFunc::Path::Normalize(engineJsonPath);
AZ::IO::LocalFileIO localFileIO;
auto readJsonResult = AzFramework::FileFunc::ReadJsonFile(engineJsonPath, &localFileIO);
if (readJsonResult.IsSuccess())
{
SetRootPath(RootPathType::EngineRoot, m_engineRoot.c_str());
AZ_TracePrintf(s_azFrameworkWarningWindow, "Engine Path: %s\n", m_engineRoot.c_str());
}
else
{
// If there is any problem reading the engine.json file, then default to engine root to the app root
AZ_Warning(s_azFrameworkWarningWindow, false, "Unable to read engine.json file '%s' (%s). Defaulting the engine root to '%s'", engineJsonPath.c_str(), readJsonResult.GetError().c_str(), m_appRoot.c_str());
SetRootPath(RootPathType::EngineRoot, m_appRoot.c_str());
}
SetRootPath(RootPathType::EngineRoot, m_engineRoot.c_str());
AZ_TracePrintf(s_azFrameworkWarningWindow, "Engine Path: %s\n", m_engineRoot.c_str());
}
@@ -504,13 +485,13 @@ namespace AzFramework
void Application::ResolveEnginePath(AZStd::string& engineRelativePath) const
{
AZStd::string fullPath = AZStd::string(m_engineRoot) + AZStd::string(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING) + engineRelativePath;
engineRelativePath = fullPath;
AZ::IO::FixedMaxPath fullPath = m_engineRoot / engineRelativePath;
engineRelativePath = fullPath.String();
}
void Application::CalculateBranchTokenForEngineRoot(AZStd::string& token) const
{
AzFramework::StringFunc::AssetPath::CalculateBranchToken(AZStd::string(m_engineRoot), token);
AzFramework::StringFunc::AssetPath::CalculateBranchToken(m_engineRoot.String(), token);
}
////////////////////////////////////////////////////////////////////////////
@@ -648,37 +629,21 @@ namespace AzFramework
void Application::SetRootPath(RootPathType type, const char* source)
{
size_t sourceLen = strlen(source);
constexpr AZStd::string_view pathSeparators{ AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR };
// Determine if we need to append a trailing path separator
bool appendTrailingPathSep = sourceLen > 0 && pathSeparators.find_first_of(source[sourceLen - 1]) == AZStd::string_view::npos;
const size_t sourceLen = strlen(source);
// Copy the source path to the intended root path and correct the path separators as well
switch (type)
{
case RootPathType::AppRoot:
{
AZ_Assert(sourceLen < m_appRoot.max_size(), "String overflow for App Root: %s", source);
m_appRoot = source;
AZStd::replace(std::begin(m_appRoot), std::end(m_appRoot), AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (appendTrailingPathSep)
{
m_appRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
AZ_Assert(sourceLen < m_appRoot.Native().max_size(), "String overflow for App Root: %s", source);
m_appRoot = AZ::IO::PathView(source).LexicallyNormal();
}
break;
case RootPathType::EngineRoot:
{
AZ_Assert(sourceLen < m_engineRoot.max_size(), "String overflow for Engine Root: %s", source);
m_engineRoot = source;
AZStd::replace(std::begin(m_engineRoot), std::end(m_engineRoot), AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (appendTrailingPathSep)
{
m_engineRoot.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
}
AZ_Assert(sourceLen < m_engineRoot.Native().max_size(), "String overflow for Engine Root: %s", source);
m_engineRoot = AZ::IO::PathView(source).LexicallyNormal();
}
break;
default:
@@ -1188,8 +1188,8 @@ namespace AZ::IO
if (az_archive_verbosity)
{
char fileNameBuffer[AZ_MAX_PATH_LEN];
const char* fileName = AZ::IO::FileIOBase::GetDirectInstance()->GetFilename(fileHandle, fileNameBuffer, AZ_ARRAY_SIZE(fileNameBuffer))
? fileNameBuffer : "unknown";
[[maybe_unused]] const char* fileName = AZ::IO::FileIOBase::GetDirectInstance()->GetFilename(fileHandle, fileNameBuffer,
AZ_ARRAY_SIZE(fileNameBuffer)) ? fileNameBuffer : "unknown";
AZ_TracePrintf("Archive", R"(Perf Warning: First call to read file "%s" made from multiple threads concurrently)" "\n",
fileName);
}
@@ -1914,7 +1914,6 @@ namespace AZ::IO
AZ::IO::StackString pathStr{ szPathIn };
// Determine if there is a period ('.') after the last slash to determine if the path contains a file.
// This used to be a strchr on the whole path which could contain a period in a path, such as network domain paths (domain.user).
bool bPathContainsFile = false;
size_t findDotFromPos = pathStr.rfind(AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (findDotFromPos == AZ::IO::StackString::npos)
{
@@ -2046,7 +2045,6 @@ namespace AZ::IO
uint8_t pMem[dwChunkSize];
uint32_t dwSize = 0;
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (!fileIO)
@@ -2453,11 +2451,13 @@ namespace AZ::IO
ArchiveLocationPriority Archive::GetPakPriority() const
{
int pakPriority = aznumeric_cast<int>(ArchiveVars{}.nPriority);
#if defined(AZ_ENABLE_TRACING)
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
{
AZ::GetValueResult getCvarResult = console->GetCvarValue("sys_PakPriority", pakPriority);
AZ_Error("Archive", getCvarResult == AZ::GetValueResult::Success, "Lookup of 'sys_PakPriority console variable failed with error %s", AZ::GetEnumString(getCvarResult));
}
#endif
return static_cast<ArchiveLocationPriority>(pakPriority);
}
@@ -2580,10 +2580,6 @@ namespace AZ::IO
return static_cast<EStreamSourceMediaType>(0);
}
ZipDir::CachePtr pZip;
uint32_t archFlags;
ZipDir::FileEntry* pFileEntry = FindPakFileEntry(szFullPath->Native(), archFlags, &pZip, false);
enum StreamMediaType : int32_t
{
TypeUnknown = 0,
@@ -28,7 +28,7 @@ namespace AZ::IO::Internal
static bool IsIgnored(const char* szPath);
// Do not report missing LOD files if no CGF files depend on them
// Do not report missing .cgfm files since they're not actually created and used in Lumberyard
// Do not report missing .cgfm files since they're not actually created and used in Open 3D Engine
// This checking prevents our missing dependency scanner from having a lot of false positives on these files
static bool IgnoreCGFDependencies(const char* szPath);
@@ -831,7 +831,7 @@ namespace AZ::IO::ZipDir
//arrFiles.SortByFileOffset();
size_t nSizeCDR = arrFiles.GetStats().nSizeCDR;
void* pCDR = m_allocator->Allocate(nSizeCDR, alignof(uint8_t), 0, "Cache::WriteCDR");
size_t nSizeCDRSerialized = arrFiles.MakeZipCDR(m_lCDROffset, pCDR);
[[maybe_unused]] size_t nSizeCDRSerialized = arrFiles.MakeZipCDR(m_lCDROffset, pCDR);
AZ_Assert(nSizeCDRSerialized == nSizeCDR, "Serialized CDR size %zu does not match size in memory %zu", nSizeCDRSerialized, nSizeCDR);
if (m_encryptedHeaders == ZipFile::HEADERS_ENCRYPTED_TEA)
{
@@ -343,7 +343,6 @@ namespace AZ::IO::ZipDir
{
AZ::IO::HandleType realFileHandle = m_fileHandle;
size_t nFileSize = ~0;
int64_t offset = 0;
AZ::u64 fileSize = 0;
if (!m_fileIOBase->Size(realFileHandle, fileSize))
@@ -21,7 +21,7 @@ namespace AZ
namespace AzFramework
{
// Class to describe metadata about an AssetBundle in Lumberyard
// Class to describe metadata about an AssetBundle in Open 3D Engine
class AssetBundleManifest
{
public:
@@ -617,9 +617,9 @@ namespace AzFramework
// won't free the mutex until the load is complete.
// So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also
// so that the entire AssetCatalog initialization is complete.
AZ::TickBus::QueueFunction([catalogRegistryFile]()
AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]()
{
AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryFile);
AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
});
}
}
@@ -1014,7 +1014,6 @@ namespace AzFramework
behaviorContext->Constant("EditorTransformComponentTypeId", BehaviorConstant(AZ::EditorTransformComponentTypeId));
behaviorContext->Class<AZ::TransformConfig>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::ConstructorOverride, &AZ::TransformConfigConstructor)
->Enum<(int)AZ::TransformConfig::ParentActivationTransformMode::MaintainOriginalRelativeTransform>("MaintainOriginalRelativeTransform")
->Enum<(int)AZ::TransformConfig::ParentActivationTransformMode::MaintainCurrentWorldTransform>("MaintainCurrentWorldTransform")
@@ -167,8 +167,6 @@ namespace AzFramework
for (auto && bound : m_bounds)
{
bool satisfies = false;
if (bound.m_comparison == Comp::TwiddleWakka)
{
// Lower bound
@@ -33,7 +33,6 @@ namespace AzFramework
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<BehaviorComponentId>("ComponentId")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Constructor()
->Method("IsValid", &BehaviorComponentId::IsValid)
@@ -136,7 +135,6 @@ namespace AzFramework
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<BehaviorEntity>("Entity")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor)
->Constructor()
@@ -597,9 +597,9 @@ namespace AZ
{
// here we are making sure that the buffer being passed in has enough space to include the alias in it.
// we are trying to find the LONGEST match, meaning of the following two examples, the second should 'win'
// File: g:/lumberyard/dev/files/morefiles/blah.xml
// Alias1 links to 'g:/lumberyard/dev/'
// Alias2 links to 'g:/lumberyard/dev/files/morefiles'
// File: g:/O3DE/dev/files/morefiles/blah.xml
// Alias1 links to 'g:/O3DE/dev/'
// Alias2 links to 'g:/O3DE/dev/files/morefiles'
// so returning Alias2 is preferred as it is more specific, even though alias1 includes it.
// note that its not possible for this to be matched if the string is shorter than the length of the alias itself so we skip
// strings that are shorter than the alias's mapped path without checking.
@@ -643,8 +643,6 @@ namespace AZ
return ResultCode::Error;
}
bool bSourceExcluded = false;
bool bDestinationExcluded = false;
//else both are remote so just issue the remote copy command
AzFramework::AssetSystem::FileCopyRequest request(sourceFilePath, destinationFilePath);
@@ -697,8 +695,6 @@ namespace AZ
}
//we are going to access shared memory so lock and copy the results into our memory
bool bSourceExcluded = false;
bool bDestinationExcluded = false;
//if the source and destination are the same, shortcut
if (!strcmp(sourceFilePath, destinationFilePath))
@@ -126,19 +126,22 @@ namespace AzFramework
return;
}
}
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
else
{
FlushCache(args.m_path);
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
{
FlushCache(args.m_path);
}
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
{
FlushEntireCache();
}
else if constexpr (AZStd::is_same_v<Command, FileRequest::ReportData>)
{
Report(args);
}
StreamStackEntry::QueueRequest(request);
}
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
{
FlushEntireCache();
}
else if constexpr (AZStd::is_same_v<Command, FileRequest::ReportData>)
{
Report(args);
}
StreamStackEntry::QueueRequest(request);
}, request->GetCommand());
}
@@ -130,13 +130,11 @@ namespace AzFramework
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<InputSystemNotificationBus>("InputSystemNotificationBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Handler<InputSystemNotificationBusBehaviorHandler>()
;
behaviorContext->EBus<InputSystemRequestBus>("InputSystemRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Event("RecreateEnabledInputDevices", &InputSystemRequestBus::Events::RecreateEnabledInputDevices)
;
@@ -56,7 +56,6 @@ namespace AzFramework
if (behaviorContext)
{
behaviorContext->EBus<NetBindingHandlerBus>("NetBindingHandlerBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::Preview)
->Event("IsEntityBoundToNetwork", &NetBindingHandlerBus::Events::IsEntityBoundToNetwork)
->Event("IsEntityAuthoritative", &NetBindingHandlerBus::Events::IsEntityAuthoritative)
@@ -22,7 +22,6 @@ namespace Physics
behaviorContext->EBus<Physics::CollisionFilteringRequestBus>("CollisionFilteringBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::Preview)
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("SetCollisionLayer", &Physics::CollisionFilteringRequestBus::Events::SetCollisionLayer)
->Event("GetCollisionLayerName", &Physics::CollisionFilteringRequestBus::Events::GetCollisionLayerName)
@@ -168,7 +168,7 @@ namespace Physics
MaterialId m_id;
};
/// An asset that holds a list of materials to be edited and assigned in Lumberyard Editor
/// An asset that holds a list of materials to be edited and assigned in Open 3D Engine Editor
/// ======================================================================================
///
/// Use Asset Editor to create a MaterialLibraryAsset and add materials to it.\n
@@ -0,0 +1,238 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
namespace
{
class ReflectContext;
}
namespace Physics
{
class ShapeConfiguration;
class World;
class Shape;
/// Default values used for initializing RigidBodySettings.
/// These can be modified by Physics Implementation gems. // O3DE_DEPRECATED(LY-114472) - DefaultRigidBodyConfiguration values are not shared across modules.
// Use RigidBodyConfiguration default values.
struct DefaultRigidBodyConfiguration
{
static float m_mass;
static bool m_computeInertiaTensor;
static float m_linearDamping;
static float m_angularDamping;
static float m_sleepMinEnergy;
static float m_maxAngularVelocity;
};
enum class MassComputeFlags : AZ::u8
{
NONE = 0,
//! Flags indicating whether a certain mass property should be auto-computed or not.
COMPUTE_MASS = 1,
COMPUTE_INERTIA = 1 << 1,
COMPUTE_COM = 1 << 2,
//! If set, non-simulated shapes will also be included in the mass properties calculation.
INCLUDE_ALL_SHAPES = 1 << 3,
DEFAULT = COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS
};
class RigidBodyConfiguration
: public WorldBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(RigidBodyConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBodyConfiguration, "{ACFA8900-8530-4744-AF00-AA533C868A8E}", WorldBodyConfiguration);
static void Reflect(AZ::ReflectContext* context);
enum PropertyVisibility : AZ::u16
{
InitialVelocities = 1 << 0, ///< Whether the initial linear and angular velocities are visible.
InertiaProperties = 1 << 1, ///< Whether the whole category of inertia properties (mass, compute inertia,
///< inertia tensor etc) is visible.
Damping = 1 << 2, ///< Whether linear and angular damping are visible.
SleepOptions = 1 << 3, ///< Whether the sleep threshold and start asleep options are visible.
Interpolation = 1 << 4, ///< Whether the interpolation option is visible.
Gravity = 1 << 5, ///< Whether the effected by gravity option is visible.
Kinematic = 1 << 6, ///< Whether the option to make the body kinematic is visible.
ContinuousCollisionDetection = 1 << 7, ///< Whether the option to enable continuous collision detection is visible.
MaxVelocities = 1 << 8 ///< Whether upper limits on velocities are visible.
};
RigidBodyConfiguration() = default;
RigidBodyConfiguration(const RigidBodyConfiguration& settings) = default;
// Visibility functions.
AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
AZ::Crc32 GetInitialVelocitiesVisibility() const;
/// Returns whether the whole category of inertia settings (mass, inertia, center of mass offset etc) is visible.
AZ::Crc32 GetInertiaSettingsVisibility() const;
/// Returns whether the individual inertia tensor field is visible or is hidden because the compute inertia option is selected.
AZ::Crc32 GetInertiaVisibility() const;
/// Returns whether the mass field is visible or is hidden because compute mass option is selected.
AZ::Crc32 GetMassVisibility() const;
/// Returns whether the individual centre of mass offset field is visible or is hidden because compute CoM option is selected.
AZ::Crc32 GetCoMVisibility() const;
AZ::Crc32 GetDampingVisibility() const;
AZ::Crc32 GetSleepOptionsVisibility() const;
AZ::Crc32 GetInterpolationVisibility() const;
AZ::Crc32 GetGravityVisibility() const;
AZ::Crc32 GetKinematicVisibility() const;
AZ::Crc32 GetCCDVisibility() const;
AZ::Crc32 GetMaxVelocitiesVisibility() const;
MassComputeFlags GetMassComputeFlags() const;
void SetMassComputeFlags(MassComputeFlags flags);
bool IsCCDEnabled() const;
// Basic initial settings.
AZ::Vector3 m_initialLinearVelocity = AZ::Vector3::CreateZero();
AZ::Vector3 m_initialAngularVelocity = AZ::Vector3::CreateZero();
AZ::Vector3 m_centerOfMassOffset = AZ::Vector3::CreateZero();
// Simulation parameters.
float m_mass = DefaultRigidBodyConfiguration::m_mass;
AZ::Matrix3x3 m_inertiaTensor = AZ::Matrix3x3::CreateIdentity();
float m_linearDamping = DefaultRigidBodyConfiguration::m_linearDamping;
float m_angularDamping = DefaultRigidBodyConfiguration::m_angularDamping;
float m_sleepMinEnergy = DefaultRigidBodyConfiguration::m_sleepMinEnergy;
float m_maxAngularVelocity = DefaultRigidBodyConfiguration::m_maxAngularVelocity;
// Visibility settings.
AZ::u16 m_propertyVisibilityFlags = (std::numeric_limits<AZ::u16>::max)();
bool m_startAsleep = false;
bool m_interpolateMotion = false;
bool m_gravityEnabled = true;
bool m_simulated = true;
bool m_kinematic = false;
bool m_ccdEnabled = false; ///< Whether continuous collision detection is enabled.
float m_ccdMinAdvanceCoefficient = 0.15f; ///< Coefficient affecting how granularly time is subdivided in CCD.
bool m_ccdFrictionEnabled = false; ///< Whether friction is applied when resolving CCD collisions.
bool m_computeCenterOfMass = true;
bool m_computeInertiaTensor = true;
bool m_computeMass = true;
//! If set, non-simulated shapes will also be included in the mass properties calculation.
bool m_includeAllShapesInMassCalculation = false;
};
/// Dynamic rigid body.
class RigidBody
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RigidBody, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBody, "{156E459F-7BB7-4B4E-ADA0-2130D96B7E80}", WorldBody);
public:
RigidBody() = default;
explicit RigidBody(const RigidBodyConfiguration& settings);
virtual void AddShape(AZStd::shared_ptr<Shape> shape) = 0;
virtual void RemoveShape(AZStd::shared_ptr<Shape> shape) = 0;
virtual AZ::u32 GetShapeCount() { return 0; }
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
virtual AZ::Vector3 GetCenterOfMassWorld() const = 0;
virtual AZ::Vector3 GetCenterOfMassLocal() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0;
virtual float GetMass() const = 0;
virtual float GetInverseMass() const = 0;
virtual void SetMass(float mass) = 0;
virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0;
/// Retrieves the velocity at center of mass; only linear velocity, no rotational velocity contribution.
virtual AZ::Vector3 GetLinearVelocity() const = 0;
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
virtual AZ::Vector3 GetAngularVelocity() const = 0;
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
virtual float GetLinearDamping() const = 0;
virtual void SetLinearDamping(float damping) = 0;
virtual float GetAngularDamping() const = 0;
virtual void SetAngularDamping(float damping) = 0;
virtual bool IsAwake() const = 0;
virtual void ForceAsleep() = 0;
virtual void ForceAwake() = 0;
virtual float GetSleepThreshold() const = 0;
virtual void SetSleepThreshold(float threshold) = 0;
virtual bool IsKinematic() const = 0;
virtual void SetKinematic(bool kinematic) = 0;
virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0;
virtual bool IsGravityEnabled() const = 0;
virtual void SetGravityEnabled(bool enabled) = 0;
virtual void SetSimulationEnabled(bool enabled) = 0;
virtual void SetCCDEnabled(bool enabled) = 0;
//! Recalculates mass, inertia and center of mass based on the flags passed.
//! @param flags MassComputeFlags specifying which properties should be recomputed.
//! @param centerOfMassOffsetOverride Optional override of the center of mass. Note: This parameter will be ignored if COMPUTE_COM is passed in flags.
//! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags.
//! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags.
virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) = 0;
};
/// Bitwise operators for MassComputeFlags
inline MassComputeFlags operator|(MassComputeFlags lhs, MassComputeFlags rhs)
{
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) | aznumeric_cast<AZ::u8>(rhs));
}
inline MassComputeFlags operator&(MassComputeFlags lhs, MassComputeFlags rhs)
{
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) & aznumeric_cast<AZ::u8>(rhs));
}
/// Static rigid body.
class RigidBodyStatic
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RigidBodyStatic, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBodyStatic, "{13A677BB-7085-4EDB-BCC8-306548238692}", WorldBody);
virtual void AddShape(const AZStd::shared_ptr<Shape>& shape) = 0;
virtual AZ::u32 GetShapeCount() { return 0; }
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
};
} // namespace Physics
@@ -93,9 +93,9 @@ namespace AzFramework
"DataSet31","DataSet32"
};
if (s_chunkIndex > AZ_ARRAY_SIZE(s_nameArray) && AZ_ARRAY_SIZE(s_nameArray) >= 0)
if ((s_chunkIndex >= AZ_ARRAY_SIZE(s_nameArray)) && (AZ_ARRAY_SIZE(s_nameArray) >= 0))
{
s_chunkIndex = s_chunkIndex%AZ_ARRAY_SIZE(s_nameArray);
s_chunkIndex = s_chunkIndex % AZ_ARRAY_SIZE(s_nameArray);
}
return s_nameArray[s_chunkIndex++];
@@ -0,0 +1,531 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "CameraInput.h"
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Plane.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Windowing/WindowBus.h>
namespace AzFramework
{
void CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor_motion = AZStd::get_if<CursorMotionEvent>(&event))
{
m_currentCursorPosition = cursor_motion->m_position;
}
else if (const auto& scroll = AZStd::get_if<ScrollEvent>(&event))
{
m_scrollDelta = scroll->m_delta;
}
m_cameras.HandleEvents(event);
}
Camera CameraSystem::StepCamera(const Camera& targetCamera, float deltaTime)
{
const auto cursorDelta = m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
: ScreenVector(0, 0);
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
const auto nextCamera = m_cameras.StepCamera(targetCamera, cursorDelta, m_scrollDelta, deltaTime);
m_scrollDelta = 0.0f;
return nextCamera;
}
void Cameras::AddCamera(AZStd::shared_ptr<CameraInput> camera_input)
{
m_idleCameraInputs.push_back(AZStd::move(camera_input));
}
void Cameras::HandleEvents(const InputEvent& event)
{
for (auto& camera_input : m_activeCameraInputs)
{
camera_input->HandleEvents(event);
}
for (auto& camera_input : m_idleCameraInputs)
{
camera_input->HandleEvents(event);
}
}
Camera Cameras::StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, const float deltaTime)
{
for (int i = 0; i < m_idleCameraInputs.size();)
{
auto& camera_input = m_idleCameraInputs[i];
const bool can_begin = camera_input->Beginning() &&
std::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(),
[](const auto& input) { return !input->Exclusive(); }) &&
(!camera_input->Exclusive() || (camera_input->Exclusive() && m_activeCameraInputs.empty()));
if (can_begin)
{
m_activeCameraInputs.push_back(camera_input);
using AZStd::swap;
swap(m_idleCameraInputs[i], m_idleCameraInputs[m_idleCameraInputs.size() - 1]);
m_idleCameraInputs.pop_back();
}
else
{
i++;
}
}
// accumulate
Camera nextCamera = targetCamera;
for (auto& camera_input : m_activeCameraInputs)
{
nextCamera = camera_input->StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
for (int i = 0; i < m_activeCameraInputs.size();)
{
auto& camera_input = m_activeCameraInputs[i];
if (camera_input->Ending())
{
camera_input->ClearActivation();
m_idleCameraInputs.push_back(camera_input);
using AZStd::swap;
swap(m_activeCameraInputs[i], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
m_activeCameraInputs.pop_back();
}
else
{
camera_input->ContinueActivation();
i++;
}
}
return nextCamera;
}
void Cameras::Reset()
{
for (int i = 0; i < m_activeCameraInputs.size();)
{
m_activeCameraInputs[i]->Reset();
m_idleCameraInputs.push_back(m_activeCameraInputs[i]);
m_activeCameraInputs[i] = m_activeCameraInputs[m_activeCameraInputs.size() - 1];
m_activeCameraInputs.pop_back();
}
}
void RotateCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_channelId)
{
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
}
Camera RotateCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_pitch += float(cursorDelta.m_y) * m_props.m_rotateSpeed;
nextCamera.m_yaw += float(cursorDelta.m_x) * m_props.m_rotateSpeed;
auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoOverPi, AZ::Constants::TwoOverPi); };
nextCamera.m_yaw = clamp_rotation(nextCamera.m_yaw);
// clamp pitch to be +-90 degrees
nextCamera.m_pitch = AZ::GetClamp(nextCamera.m_pitch, -AZ::Constants::Pi * 0.5f, AZ::Constants::Pi * 0.5f);
return nextCamera;
}
void PanCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Middle)
{
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
}
Camera PanCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
const auto pan_axes = m_panAxesFn(nextCamera);
const auto delta_pan_x = float(cursorDelta.m_x) * pan_axes.m_horizontalAxis * m_props.m_panSpeed;
const auto delta_pan_y = float(cursorDelta.m_y) * pan_axes.m_verticalAxis * m_props.m_panSpeed;
const auto inv = [](const bool invert) {
constexpr float Dir[] = {1.0f, -1.0f};
return Dir[static_cast<int>(invert)];
};
nextCamera.m_lookAt += delta_pan_x * inv(m_props.m_panInvertX);
nextCamera.m_lookAt += delta_pan_y * -inv(m_props.m_panInvertY);
return nextCamera;
}
TranslateCameraInput::TranslationType TranslateCameraInput::translationFromKey(InputChannelId channelId)
{
// note: remove hard-coded InputDevice keys
if (channelId == InputDeviceKeyboard::Key::AlphanumericW)
{
return TranslationType::Forward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericS)
{
return TranslationType::Backward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericA)
{
return TranslationType::Left;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericD)
{
return TranslationType::Right;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericQ)
{
return TranslationType::Down;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericE)
{
return TranslationType::Up;
}
return TranslationType::Nil;
}
void TranslateCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_state == InputChannel::State::Began)
{
if (input->m_state == InputChannel::State::Updated)
{
return;
}
m_translation |= translationFromKey(input->m_channelId);
if (m_translation != TranslationType::Nil)
{
BeginActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
m_boost = true;
}
}
else if (input->m_state == InputChannel::State::Ended)
{
m_translation ^= translationFromKey(input->m_channelId);
if (m_translation == TranslationType::Nil)
{
EndActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
m_boost = false;
}
}
}
}
Camera TranslateCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
const float deltaTime)
{
Camera nextCamera = targetCamera;
const auto translation_basis = m_translationAxesFn(nextCamera);
const auto axisX = translation_basis.GetBasisX();
const auto axisY = translation_basis.GetBasisY();
const auto axisZ = translation_basis.GetBasisZ();
const float speed = [boost = m_boost, props = m_props]() {
return props.m_translateSpeed * (boost ? props.m_boostMultiplier : 1.0f);
}();
if ((m_translation & TranslationType::Forward) == TranslationType::Forward)
{
nextCamera.m_lookAt += axisY * speed * deltaTime;
}
if ((m_translation & TranslationType::Backward) == TranslationType::Backward)
{
nextCamera.m_lookAt -= axisY * speed * deltaTime;
}
if ((m_translation & TranslationType::Left) == TranslationType::Left)
{
nextCamera.m_lookAt -= axisX * speed * deltaTime;
}
if ((m_translation & TranslationType::Right) == TranslationType::Right)
{
nextCamera.m_lookAt += axisX * speed * deltaTime;
}
if ((m_translation & TranslationType::Up) == TranslationType::Up)
{
nextCamera.m_lookAt += axisZ * speed * deltaTime;
}
if ((m_translation & TranslationType::Down) == TranslationType::Down)
{
nextCamera.m_lookAt -= axisZ * speed * deltaTime;
}
if (Ending())
{
m_translation = TranslationType::Nil;
}
return nextCamera;
}
void TranslateCameraInput::ResetImpl()
{
m_translation = TranslationType::Nil;
m_boost = false;
}
void OrbitCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierAltL)
{
if (input->m_state == InputChannel::State::Updated)
{
goto end;
}
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
end:
if (Active())
{
m_orbitCameras.HandleEvents(event);
}
}
Camera OrbitCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, float deltaTime)
{
Camera nextCamera = targetCamera;
if (Beginning())
{
float hit_distance = 0.0f;
if (AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateZero())
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY() * m_props.m_maxOrbitDistance, hit_distance))
{
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -m_props.m_defaultOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * m_props.m_defaultOrbitDistance;
}
}
if (Active())
{
// todo: need to return nested cameras to idle state when ending
nextCamera = m_orbitCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
if (Ending())
{
m_orbitCameras.Reset();
nextCamera.m_lookAt = nextCamera.Translation();
nextCamera.m_lookDist = 0.0f;
}
return nextCamera;
}
void OrbitDollyScrollCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
{
BeginActivation();
}
}
Camera OrbitDollyScrollCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta,
[[maybe_unused]] float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * m_props.m_dollySpeed, 0.0f);
EndActivation();
return nextCamera;
}
void OrbitDollyCursorMoveCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Right)
{
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
}
Camera OrbitDollyCursorMoveCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * m_props.m_dollySpeed, 0.0f);
return nextCamera;
}
void ScrollTranslationCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
{
BeginActivation();
}
}
Camera ScrollTranslationCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
const auto translation_basis = LookTranslation(nextCamera);
const auto axisY = translation_basis.GetBasisY();
nextCamera.m_lookAt += axisY * scrollDelta * m_props.m_translateSpeed;
EndActivation();
return nextCamera;
}
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, const float deltaTime)
{
const auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
// keep yaw in 0 - 360 range
float target_yaw = clamp_rotation(targetCamera.m_yaw);
const float current_yaw = clamp_rotation(currentCamera.m_yaw);
auto sign = [](const float value) { return static_cast<float>((0.0f < value) - (value < 0.0f)); };
// ensure smooth transition when moving across 0 - 360 boundary
const float yaw_delta = target_yaw - current_yaw;
if (std::abs(yaw_delta) >= AZ::Constants::Pi)
{
target_yaw -= AZ::Constants::TwoPi * sign(yaw_delta);
}
Camera camera;
// note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent
// article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php
const float lookRate = std::exp2(props.m_lookSmoothness);
const float lookT = std::exp2(-lookRate * deltaTime);
camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookT);
camera.m_yaw = AZ::Lerp(target_yaw, current_yaw, lookT);
const float moveRate = std::exp2(props.m_moveSmoothness);
const float moveT = std::exp2(-moveRate * deltaTime);
camera.m_lookDist = AZ::Lerp(targetCamera.m_lookDist, currentCamera.m_lookDist, moveT);
camera.m_lookAt = targetCamera.m_lookAt.Lerp(currentCamera.m_lookAt, moveT);
return camera;
}
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize)
{
const auto& inputChannelId = inputChannel.GetInputChannelId();
const auto& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
if (inputChannelId == InputDeviceMouse::SystemCursorPosition)
{
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
InputSystemCursorRequestBus::EventResult(
systemCursorPositionNormalized, inputDeviceId, &InputSystemCursorRequestBus::Events::GetSystemCursorPositionNormalized);
return CursorMotionEvent{ScreenPoint(
systemCursorPositionNormalized.GetX() * windowSize.m_width, systemCursorPositionNormalized.GetY() * windowSize.m_height)};
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
return ScrollEvent{inputChannel.GetValue()};
}
else if (InputDeviceMouse::IsMouseDevice(inputDeviceId) || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
return DiscreteInputEvent{inputChannelId, inputChannel.GetState()};
}
return AZStd::monostate{};
}
} // namespace AzFramework
@@ -0,0 +1,420 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AzFramework
{
struct WindowSize;
struct Camera
{
AZ::Vector3 m_lookAt = AZ::Vector3::CreateZero(); //!< Position of camera when m_lookDist is zero,
//!< or position of m_lookAt when m_lookDist is greater
//!< than zero.
float m_yaw{0.0};
float m_pitch{0.0};
float m_lookDist{0.0}; //!< Zero gives first person free look, otherwise orbit about m_lookAt
//! View camera transform (v in MVP).
AZ::Transform View() const;
//! World camera transform.
AZ::Transform Transform() const;
//! World rotation.
AZ::Matrix3x3 Rotation() const;
//! World translation.
AZ::Vector3 Translation() const;
};
inline AZ::Transform Camera::View() const
{
return Transform().GetInverse();
}
inline AZ::Transform Camera::Transform() const
{
return AZ::Transform::CreateTranslation(m_lookAt) * AZ::Transform::CreateRotationX(m_pitch) *
AZ::Transform::CreateRotationZ(m_yaw) * AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisZ(m_lookDist));
}
inline AZ::Matrix3x3 Camera::Rotation() const
{
return AZ::Matrix3x3::CreateFromQuaternion(Transform().GetRotation());
}
inline AZ::Vector3 Camera::Translation() const
{
return Transform().GetTranslation();
}
struct CursorMotionEvent
{
ScreenPoint m_position;
};
struct ScrollEvent
{
float m_delta;
};
struct DiscreteInputEvent
{
InputChannelId m_channelId; //!< Channel type. (e.g. Keyboard key, mouse button or other device input).
InputChannel::State m_state; //!< Channel state. (e.g. Begin/update/end event).
};
using InputEvent = AZStd::variant<AZStd::monostate, CursorMotionEvent, ScrollEvent, DiscreteInputEvent>;
class CameraInput
{
public:
enum class Activation
{
Idle,
Begin,
Active,
End
};
virtual ~CameraInput() = default;
bool Beginning() const
{
return m_activation == Activation::Begin;
}
bool Ending() const
{
return m_activation == Activation::End;
}
bool Idle() const
{
return m_activation == Activation::Idle;
}
bool Active() const
{
return m_activation == Activation::Active;
}
void BeginActivation()
{
m_activation = Activation::Begin;
}
void EndActivation()
{
m_activation = Activation::End;
}
void ContinueActivation()
{
m_activation = Activation::Active;
}
void ClearActivation()
{
m_activation = Activation::Idle;
}
void Reset()
{
ClearActivation();
ResetImpl();
}
virtual void HandleEvents(const InputEvent& event) = 0;
virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0;
virtual bool Exclusive() const
{
return false;
}
protected:
virtual void ResetImpl()
{
}
private:
Activation m_activation = Activation::Idle;
};
struct SmoothProps
{
float m_lookSmoothness = 5.0f;
float m_moveSmoothness = 5.0f;
};
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, float deltaTime);
class Cameras
{
public:
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
void HandleEvents(const InputEvent& event);
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime);
void Reset();
private:
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_idleCameraInputs;
};
class CameraSystem
{
public:
void HandleEvents(const InputEvent& event);
Camera StepCamera(const Camera& targetCamera, float deltaTime);
Cameras m_cameras;
private:
float m_scrollDelta = 0.0f;
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
};
class RotateCameraInput : public CameraInput
{
public:
explicit RotateCameraInput(const InputChannelId channelId)
: m_channelId(channelId)
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
InputChannelId m_channelId;
struct Props
{
float m_rotateSpeed = 0.005f;
} m_props;
};
struct PanAxes
{
AZ::Vector3 m_horizontalAxis;
AZ::Vector3 m_verticalAxis;
};
using PanAxesFn = AZStd::function<PanAxes(const Camera& camera)>;
inline PanAxes LookPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
return {orientation.GetBasisX(), orientation.GetBasisZ()};
}
inline PanAxes OrbitPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = [&orientation] {
const auto forward = orientation.GetBasisY();
return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized();
}();
return {basisX, basisY};
}
class PanCameraInput : public CameraInput
{
public:
explicit PanCameraInput(PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_panSpeed = 0.01f;
bool m_panInvertX = true;
bool m_panInvertY = true;
} m_props;
private:
PanAxesFn m_panAxesFn;
};
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
inline AZ::Matrix3x3 LookTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = orientation.GetBasisY();
const auto basisZ = AZ::Vector3::CreateAxisZ();
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = [&orientation] {
const auto forward = orientation.GetBasisY();
return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized();
}();
const auto basisZ = AZ::Vector3::CreateAxisZ();
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
class TranslateCameraInput : public CameraInput
{
public:
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
struct Props
{
float m_translateSpeed = 10.0f;
float m_boostMultiplier = 3.0f;
} m_props;
private:
enum class TranslationType
{
// clang-format off
Nil = 0,
Forward = 1 << 0,
Backward = 1 << 1,
Left = 1 << 2,
Right = 1 << 3,
Up = 1 << 4,
Down = 1 << 5,
// clang-format on
};
friend TranslationType operator|(const TranslationType lhs, const TranslationType rhs)
{
return static_cast<TranslationType>(
static_cast<std::underlying_type_t<TranslationType>>(lhs) | static_cast<std::underlying_type_t<TranslationType>>(rhs));
}
friend TranslationType& operator|=(TranslationType& lhs, const TranslationType rhs)
{
lhs = lhs | rhs;
return lhs;
}
friend TranslationType operator^(const TranslationType lhs, const TranslationType rhs)
{
return static_cast<TranslationType>(
static_cast<std::underlying_type_t<TranslationType>>(lhs) ^ static_cast<std::underlying_type_t<TranslationType>>(rhs));
}
friend TranslationType& operator^=(TranslationType& lhs, const TranslationType rhs)
{
lhs = lhs ^ rhs;
return lhs;
}
friend TranslationType operator&(const TranslationType lhs, const TranslationType rhs)
{
return static_cast<TranslationType>(
static_cast<std::underlying_type_t<TranslationType>>(lhs) & static_cast<std::underlying_type_t<TranslationType>>(rhs));
}
friend TranslationType& operator&=(TranslationType& lhs, const TranslationType rhs)
{
lhs = lhs & rhs;
return lhs;
}
static TranslationType translationFromKey(InputChannelId channelId);
TranslationType m_translation = TranslationType::Nil;
TranslationAxesFn m_translationAxesFn;
bool m_boost = false;
};
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.2f;
} m_props;
};
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.1f;
} m_props;
};
class ScrollTranslationCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_translateSpeed = 0.2f;
} m_props;
};
class OrbitCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override
{
return true;
}
Cameras m_orbitCameras;
struct Props
{
float m_defaultOrbitDistance = 15.0f;
float m_maxOrbitDistance = 100.0f;
} m_props;
};
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize);
} // namespace AzFramework
@@ -13,6 +13,7 @@
#pragma once
#include <AzFramework/Viewport/ViewportId.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
@@ -60,17 +61,18 @@ namespace AzFramework
{
//! The viewport ID this event was dispatched to.
ViewportId m_viewportId;
//! The native window handle for the application.
NativeWindowHandle m_windowHandle;
//! The input channel data for this event.
const AzFramework::InputChannel& m_inputChannel;
//! The priority this event was dispatched at.
ViewportControllerPriority m_priority;
ViewportControllerInputEvent(
ViewportId viewportId,
const AzFramework::InputChannel& inputChannel,
ViewportControllerPriority priority = ViewportControllerPriority::DispatchToAllPriorities
)
ViewportId viewportId, NativeWindowHandle windowHandle, const AzFramework::InputChannel& inputChannel,
ViewportControllerPriority priority = ViewportControllerPriority::DispatchToAllPriorities)
: m_viewportId(viewportId)
, m_windowHandle(windowHandle)
, m_inputChannel(inputChannel)
, m_priority(priority)
{
@@ -21,7 +21,6 @@ namespace AzFramework
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<BoundsRequestBus>("BoundsRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Event("GetWorldBounds", &BoundsRequestBus::Events::GetWorldBounds)
->Event("GetLocalBounds", &BoundsRequestBus::Events::GetLocalBounds);
}
@@ -84,7 +84,7 @@ namespace AzFramework
{
if (IVisibilitySystem* visibilitySystem = AZ::Interface<IVisibilitySystem>::Get())
{
visibilitySystem->RemoveEntry(instance_it->second.m_visibilityEntry);
visibilitySystem->GetDefaultVisibilityScene()->RemoveEntry(instance_it->second.m_visibilityEntry);
m_entityVisibilityBoundsUnionInstanceMapping.erase(instance_it);
}
}
@@ -104,7 +104,7 @@ namespace AzFramework
if (visibilitySystem && !worldEntityBoundsUnion.IsClose(instance.m_visibilityEntry.m_boundingVolume))
{
instance.m_visibilityEntry.m_boundingVolume = worldEntityBoundsUnion;
visibilitySystem->InsertOrUpdateEntry(instance.m_visibilityEntry);
visibilitySystem->GetDefaultVisibilityScene()->InsertOrUpdateEntry(instance.m_visibilityEntry);
}
}
}
@@ -56,10 +56,10 @@ namespace AzFramework
m_octreeDebug.Clear();
m_visibleEntityIds.clear();
visSystem->Enumerate(
visSystem->GetDefaultVisibilityScene()->Enumerate(
viewFrustum,
[&viewFrustum, &visibleEntityIdsOut = m_visibleEntityIds,
&octreeDebug = m_octreeDebug](const AzFramework::IVisibilitySystem::NodeData& nodeData)
&octreeDebug = m_octreeDebug](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
if (ed_visibility_showDebug)
{
@@ -13,11 +13,13 @@
#pragma once
#include <AzCore/base.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Sphere.h>
#include <AzCore/Math/Frustum.h>
#include <AzCore/Name/Name.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/std/containers/vector.h>
@@ -35,7 +37,6 @@ namespace AzFramework
{
TYPE_None = 0,
TYPE_Entity = 1 << 0, // All entities
TYPE_NetEntity = 1 << 1, // NetBound entities
TYPE_RPI_Cullable = 1 << 2 // Cullable by the render system
};
@@ -46,12 +47,15 @@ namespace AzFramework
TypeFlags m_typeFlags = TYPE_None;
};
//! @class IVisibilitySystem
//! @brief This is an AZ::Interface<> useful for extremely fast, CPU only, proximity and visibility queries.
class IVisibilitySystem
//! @class IVisibilityScene
//! @brief This is the interface for managing objects and visibility queries for a given scene.
class IVisibilityScene
{
public:
AZ_RTTI(IVisibilitySystem, "{7C6C710F-ACDB-44CD-867D-A2C4B912ECF5}");
AZ_RTTI(IVisibilityScene, "{822BC414-3CE3-40B4-A9A2-A42EA5B9499F}");
IVisibilityScene() = default;
virtual ~IVisibilityScene() = default;
struct NodeData
{
@@ -60,8 +64,8 @@ namespace AzFramework
};
using EnumerateCallback = AZStd::function<void(const NodeData&)>;
IVisibilitySystem() = default;
virtual ~IVisibilitySystem() = default;
//! Get the unique scene name, used to look up the scene in the IVisibilitySystem. Duplicate names will assert on creation.
virtual const AZ::Name& GetName() const = 0;
//! Insert or update an entry within the visibility system.
//! This encompasses the following three scenarios:
@@ -69,11 +73,11 @@ namespace AzFramework
// 2. A previously added entry moves to a new position within its current node in the spatial hash.
// 3. A previously added entry moves to a new node in the spatial hash.
// (causing it to be removed from its original node and added to its new node)
//! @param visibilityEntry data for the object being added to the visibility system
//! @param visibilityEntry data for the object being added/updated
virtual void InsertOrUpdateEntry(VisibilityEntry& visibilityEntry) = 0;
//! Removes an entry from the visibility system.
//! @param visibilityEntry data for the object being added to the visibility system
//! @param visibilityEntry data for the object being removed
virtual void RemoveEntry(VisibilityEntry& visibilityEntry) = 0;
//! Intersects an axis aligned bounding box against the visibility system.
@@ -100,6 +104,34 @@ namespace AzFramework
//! Return the number of VisibilityEntries that have been added to the system
virtual uint32_t GetEntryCount() const = 0;
};
//! @class IVisibilitySystem
//! @brief This is an AZ::Interface<> useful for extremely fast, CPU only, proximity and visibility queries.
class IVisibilitySystem
{
public:
AZ_RTTI(IVisibilitySystem, "{7C6C710F-ACDB-44CD-867D-A2C4B912ECF5}");
IVisibilitySystem() = default;
virtual ~IVisibilitySystem() = default;
//! Return the default IVisibilityScene for entities.
virtual IVisibilityScene* GetDefaultVisibilityScene() = 0;
//! Create a new IVisibilityScene that is uniquely identified by the scene name.
virtual IVisibilityScene* CreateVisibilityScene(const AZ::Name& sceneName) = 0;
//! Destroy the visibility scene.
//! This does not destroy the entities that are a part of the scene, only the visibility scene.
//! This will set the visScene to nullptr
virtual void DestroyVisibilityScene(IVisibilityScene* visScene) = 0;
//! Find the IVisibilityScene that is identified by sceneName.
virtual IVisibilityScene* FindVisibilityScene(const AZ::Name& sceneName) = 0;
//! Logs stats about the visibility system to the console.
virtual void DumpStats(const AZ::ConsoleCommandContainer& arguments) = 0;
AZ_DISABLE_COPY_MOVE(IVisibilitySystem);
};
@@ -113,6 +145,4 @@ namespace AzFramework
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
};
using IVisibilitySystemRequestBus = AZ::EBus<IVisibilitySystem, IVisibilitySystemRequests>;
}
@@ -15,7 +15,7 @@
namespace AzFramework
{
AZ_CVAR(bool, bg_octreeUseQuadtree, false, nullptr, AZ::ConsoleFunctorFlags::ReadOnly, "If set to true, the octreeSystemComponent will degenerate to a quadtree split along the X/Y plane");
AZ_CVAR(bool, bg_octreeUseQuadtree, false, nullptr, AZ::ConsoleFunctorFlags::ReadOnly, "If set to true, the visibility octrees will degenerate to a quadtree split along the X/Y plane");
AZ_CVAR(float, bg_octreeMaxWorldExtents, 16384.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum supported world size by the world octreeSystemComponent");
AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split");
AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation");
@@ -67,9 +67,9 @@ namespace AzFramework
}
void OctreeNode::Insert(OctreeSystemComponent& octreeSystemComponent, VisibilityEntry* entry)
void OctreeNode::Insert(OctreeScene& octreeScene, VisibilityEntry* entry)
{
AZ_Assert(entry->m_internalNode == nullptr, "Double-insertion: Insert invoked for an entry already bound to the OctreeSystemComponent");
AZ_Assert(entry->m_internalNode == nullptr, "Double-insertion: Insert invoked for an entry already bound to the OctreeScene");
// If this is not a leaf node, try to insert into the child nodes
if (m_children != nullptr)
@@ -80,7 +80,7 @@ namespace AzFramework
{
if (AZ::ShapeIntersection::Contains(m_children[child].m_bounds, boundingVolume))
{
return m_children[child].Insert(octreeSystemComponent, entry);
return m_children[child].Insert(octreeScene, entry);
}
}
}
@@ -90,8 +90,8 @@ namespace AzFramework
if ((m_children == nullptr) && (m_entries.size() >= bg_octreeNodeMaxEntries))
{
// If we're not already split, and our entry list gets too large, split this node
Split(octreeSystemComponent);
Insert(octreeSystemComponent, entry);
Split(octreeScene);
Insert(octreeScene, entry);
}
else
{
@@ -102,7 +102,7 @@ namespace AzFramework
}
void OctreeNode::Update(OctreeSystemComponent& octreeSystemComponent, VisibilityEntry* entry)
void OctreeNode::Update(OctreeScene& octreeScene, VisibilityEntry* entry)
{
AZ_Assert(entry->m_internalNode == this, "Update invoked for an entry bound to a different OctreeNode");
@@ -116,7 +116,7 @@ namespace AzFramework
}
// Remove the entry from our current node, since it is no longer contained
Remove(octreeSystemComponent, entry);
Remove(octreeScene, entry);
// Traverse up our ancestor nodes to find the first node that fully contains the entry
// This strategy assumes an entry will typically move a small distance relative to the total world
@@ -125,14 +125,14 @@ namespace AzFramework
{
if (AZ::ShapeIntersection::Contains(insertCheck->m_bounds, boundingVolume))
{
return insertCheck->Insert(octreeSystemComponent, entry);
return insertCheck->Insert(octreeScene, entry);
}
insertCheck = insertCheck->m_parent;
}
}
void OctreeNode::Remove(OctreeSystemComponent& octreeSystemComponent, VisibilityEntry* entry)
void OctreeNode::Remove(OctreeScene& octreeScene, VisibilityEntry* entry)
{
AZ_Assert(entry->m_internalNode == this, "Remove invoked for an entry bound to a different OctreeNode");
AZ_Assert(m_entries[entry->m_internalNodeIndex] == entry, "Visibility entry data is corrupt");
@@ -150,29 +150,30 @@ namespace AzFramework
if (m_parent != nullptr)
{
m_parent->TryMerge(octreeSystemComponent);
m_parent->TryMerge(octreeScene);
}
}
void OctreeNode::Enumerate(const AZ::Aabb& aabb, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeNode::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const
{
EnumerateHelper(aabb, callback);
}
void OctreeNode::Enumerate(const AZ::Sphere& sphere, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeNode::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const
{
EnumerateHelper(sphere, callback);
}
void OctreeNode::Enumerate(const AZ::Frustum& frustum, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeNode::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const
{
EnumerateHelper(frustum, callback);
}
void OctreeNode::EnumerateNoCull(const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeNode::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const
{
// Invoke the callback for the current node
if (!m_entries.empty())
@@ -191,6 +192,7 @@ namespace AzFramework
}
}
const AZStd::vector<VisibilityEntry*>& OctreeNode::GetEntries() const
{
return m_entries;
@@ -209,7 +211,7 @@ namespace AzFramework
}
void OctreeNode::TryMerge(OctreeSystemComponent& octreeSystemComponent)
void OctreeNode::TryMerge(OctreeScene& octreeScene)
{
if (IsLeaf())
{
@@ -222,7 +224,7 @@ namespace AzFramework
const uint32_t childCount = GetChildNodeCount();
for (uint32_t child = 0; child < childCount; ++child)
{
m_children[child].TryMerge(octreeSystemComponent);
m_children[child].TryMerge(octreeScene);
if (!m_children[child].IsLeaf())
{
return;
@@ -232,13 +234,13 @@ namespace AzFramework
if (potentialNodeCount <= bg_octreeNodeMinEntries)
{
Merge(octreeSystemComponent);
Merge(octreeScene);
}
}
template <typename T>
void OctreeNode::EnumerateHelper(const T& boundingVolume, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeNode::EnumerateHelper(const T& boundingVolume, const IVisibilityScene::EnumerateCallback& callback) const
{
AZ_Assert(AZ::ShapeIntersection::Overlaps(boundingVolume, m_bounds), "EnumerateHelper invoked on an octreeSystemComponent node that is not within the bounding volume");
@@ -263,11 +265,11 @@ namespace AzFramework
}
void OctreeNode::Split(OctreeSystemComponent& octreeSystemComponent)
void OctreeNode::Split(OctreeScene& octreeScene)
{
AZ_Assert(m_children == nullptr, "Split invoked on an octreeSystemComponent node that has already been split");
m_childNodeIndex = octreeSystemComponent.AllocateChildNodes();
m_children = octreeSystemComponent.GetChildNodesAtIndex(m_childNodeIndex);
AZ_Assert(m_children == nullptr, "Split invoked on an octreeScene node that has already been split");
m_childNodeIndex = octreeScene.AllocateChildNodes();
m_children = octreeScene.GetChildNodesAtIndex(m_childNodeIndex);
// Set child split planes and bounding volumes
{
@@ -308,14 +310,14 @@ namespace AzFramework
{
entry->m_internalNode = nullptr;
entry->m_internalNodeIndex = 0;
Insert(octreeSystemComponent, entry);
Insert(octreeScene, entry);
}
}
void OctreeNode::Merge(OctreeSystemComponent& octreeSystemComponent)
void OctreeNode::Merge(OctreeScene& octreeScene)
{
AZ_Assert(m_children != nullptr, "Merge invoked on an octreeSystemComponent node that does not have children");
AZ_Assert(m_children != nullptr, "Merge invoked on an octreeScene node that does not have children");
// Move all child entries to our own entry set
const uint32_t childCount = GetChildNodeCount();
@@ -330,46 +332,20 @@ namespace AzFramework
m_children[child].m_entries.clear();
}
octreeSystemComponent.ReleaseChildNodes(m_childNodeIndex);
octreeScene.ReleaseChildNodes(m_childNodeIndex);
m_childNodeIndex = InvalidChildNodeIndex;
m_children = nullptr;
}
void OctreeSystemComponent::Reflect(AZ::ReflectContext* context)
OctreeScene::OctreeScene(const AZ::Name& sceneName)
: m_sceneName(sceneName)
, m_root(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-bg_octreeMaxWorldExtents), AZ::Vector3(bg_octreeMaxWorldExtents)))
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<OctreeSystemComponent, AZ::Component>()
->Version(1);
}
AZ_Assert(!sceneName.IsEmpty(), "sceneName must be a valid string");
}
void OctreeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
OctreeScene::~OctreeScene()
{
provided.push_back(AZ_CRC_CE("VisibilityService"));
}
void OctreeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("VisibilityService"));
}
OctreeSystemComponent::OctreeSystemComponent()
: m_root(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-bg_octreeMaxWorldExtents), AZ::Vector3(bg_octreeMaxWorldExtents)))
{
AZ::Interface<IVisibilitySystem>::Register(this);
IVisibilitySystemRequestBus::Handler::BusConnect();
}
OctreeSystemComponent::~OctreeSystemComponent()
{
IVisibilitySystemRequestBus::Handler::BusDisconnect();
AZ::Interface<IVisibilitySystem>::Unregister(this);
for (auto page : m_nodeCache)
{
delete page;
@@ -378,21 +354,14 @@ namespace AzFramework
m_nodeCache.shrink_to_fit();
}
void OctreeSystemComponent::Activate()
const AZ::Name& OctreeScene::GetName() const
{
;
return m_sceneName;
}
void OctreeSystemComponent::Deactivate()
{
;
}
void OctreeSystemComponent::InsertOrUpdateEntry(VisibilityEntry& entry)
void OctreeScene::InsertOrUpdateEntry(VisibilityEntry& entry)
{
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sharedMutex);
if (entry.m_internalNode != nullptr)
{
static_cast<OctreeNode*>(entry.m_internalNode)->Update(*this, &entry);
@@ -405,8 +374,9 @@ namespace AzFramework
}
void OctreeSystemComponent::RemoveEntry(VisibilityEntry& entry)
void OctreeScene::RemoveEntry(VisibilityEntry& entry)
{
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sharedMutex);
if (entry.m_internalNode)
{
static_cast<OctreeNode*>(entry.m_internalNode)->Remove(*this, &entry);
@@ -415,70 +385,71 @@ namespace AzFramework
}
void OctreeSystemComponent::Enumerate(const AZ::Aabb& aabb, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeScene::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.Enumerate(aabb, callback);
}
void OctreeSystemComponent::Enumerate(const AZ::Sphere& sphere, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeScene::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.Enumerate(sphere, callback);
}
void OctreeSystemComponent::Enumerate(const AZ::Frustum& frustum, const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeScene::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.Enumerate(frustum, callback);
}
void OctreeSystemComponent::EnumerateNoCull(const IVisibilitySystem::EnumerateCallback& callback) const
void OctreeScene::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const
{
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.EnumerateNoCull(callback);
}
uint32_t OctreeSystemComponent::GetEntryCount() const
uint32_t OctreeScene::GetEntryCount() const
{
return m_entryCount;
}
OctreeNode& OctreeSystemComponent::GetRoot()
{
return m_root;
}
uint32_t OctreeSystemComponent::GetNodeCount() const
uint32_t OctreeScene::GetNodeCount() const
{
return m_nodeCount;
}
uint32_t OctreeSystemComponent::GetFreeNodeCount() const
uint32_t OctreeScene::GetFreeNodeCount() const
{
// Each entry represents GetChildNodeCount() nodes
return aznumeric_cast<uint32_t>(m_freeOctreeNodes.size() * GetChildNodeCount());
}
uint32_t OctreeSystemComponent::GetPageCount() const
uint32_t OctreeScene::GetPageCount() const
{
return aznumeric_cast<uint32_t>(m_nodeCache.size());
}
uint32_t OctreeSystemComponent::GetChildNodeCount() const
uint32_t OctreeScene::GetChildNodeCount() const
{
return AzFramework::GetChildNodeCount();
}
void OctreeSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
void OctreeScene::DumpStats()
{
AZ_TracePrintf("Console", "OctreeNode::EntryCount = %u", GetEntryCount());
AZ_TracePrintf("Console", "OctreeNode::NodeCount = %u", GetNodeCount());
AZ_TracePrintf("Console", "OctreeNode::FreeNodeCount = %u", GetFreeNodeCount());
AZ_TracePrintf("Console", "OctreeNode::PageCount = %u", GetPageCount());
AZ_TracePrintf("Console", "OctreeNode::ChildNodeCount = %u", GetChildNodeCount());
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::EntryCount = %u", GetName().GetCStr(), GetEntryCount());
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::NodeCount = %u", GetName().GetCStr(), GetNodeCount());
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::FreeNodeCount = %u", GetName().GetCStr(), GetFreeNodeCount());
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::PageCount = %u", GetName().GetCStr(), GetPageCount());
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::ChildNodeCount = %u", GetName().GetCStr(), GetChildNodeCount());
}
@@ -496,7 +467,7 @@ namespace AzFramework
}
uint32_t OctreeSystemComponent::AllocateChildNodes()
uint32_t OctreeScene::AllocateChildNodes()
{
const uint32_t childCount = GetChildNodeCount();
m_nodeCount += childCount;
@@ -540,18 +511,124 @@ namespace AzFramework
}
void OctreeSystemComponent::ReleaseChildNodes(uint32_t nodeIndex)
void OctreeScene::ReleaseChildNodes(uint32_t nodeIndex)
{
m_nodeCount -= GetChildNodeCount();
m_freeOctreeNodes.push(nodeIndex);
}
OctreeNode* OctreeSystemComponent::GetChildNodesAtIndex(uint32_t nodeIndex) const
OctreeNode* OctreeScene::GetChildNodesAtIndex(uint32_t nodeIndex) const
{
uint32_t childPage;
uint32_t childOffset;
ExtractPageAndOffsetFromIndex(nodeIndex, childPage, childOffset);
return &(*m_nodeCache[childPage])[childOffset];
}
void OctreeSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<OctreeSystemComponent, AZ::Component>()
->Version(1);
}
}
void OctreeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("OctreeService"));
}
void OctreeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("OctreeService"));
}
OctreeSystemComponent::OctreeSystemComponent()
{
AZ::Interface<IVisibilitySystem>::Register(this);
IVisibilitySystemRequestBus::Handler::BusConnect();
m_defaultScene = aznew OctreeScene(AZ::Name("DefaultVisibilityScene"));
}
OctreeSystemComponent::~OctreeSystemComponent()
{
AZ_Assert(m_scenes.empty(), "All IVisibilityScenes must be destroyed before shutdown");
delete m_defaultScene;
IVisibilitySystemRequestBus::Handler::BusDisconnect();
AZ::Interface<IVisibilitySystem>::Unregister(this);
}
void OctreeSystemComponent::Activate()
{
;
}
void OctreeSystemComponent::Deactivate()
{
;
}
IVisibilityScene* OctreeSystemComponent::GetDefaultVisibilityScene()
{
return m_defaultScene;
}
IVisibilityScene* OctreeSystemComponent::CreateVisibilityScene(const AZ::Name& sceneName)
{
AZ_Assert(FindVisibilityScene(sceneName) == nullptr, "Scene with same name already created!");
OctreeScene* newScene = aznew OctreeScene(sceneName);
m_scenes.push_back(newScene);
return newScene;
}
void OctreeSystemComponent::DestroyVisibilityScene(IVisibilityScene* visScene)
{
for (auto iter = m_scenes.begin(); iter != m_scenes.end(); ++iter)
{
if (*iter == visScene)
{
delete visScene;
m_scenes.erase(iter);
return;
}
}
AZ_Assert(false, "visScene[\"%s\"] not found in the OctreeSystemComponent", visScene->GetName().GetCStr());
}
IVisibilityScene* OctreeSystemComponent::FindVisibilityScene(const AZ::Name& sceneName)
{
for (OctreeScene* scene : m_scenes)
{
if(scene->GetName() == sceneName)
{
return scene;
}
}
return nullptr;
}
void OctreeSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
for (OctreeScene* scene : m_scenes)
{
AZ_TracePrintf("Console", "============================================");
scene->DumpStats();
}
AZ_TracePrintf("Console", "============================================");
}
}
@@ -15,14 +15,15 @@
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzCore/Math/Plane.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/std/containers/stack.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/parallel/shared_mutex.h>
namespace AzFramework
{
class OctreeSystemComponent;
class OctreeScene;
//! An internal node within the tree.
//! It contains all objects that are *fully contained* by the node, if an object spans multiple child nodes that object will be stored in the parent.
@@ -40,25 +41,25 @@ namespace AzFramework
OctreeNode& operator=(OctreeNode&& rhs);
//! Inserts a VisibilityEntry into this OctreeNode, potentially triggering a split.
void Insert(OctreeSystemComponent& octreeSystemComponent, VisibilityEntry* entry);
void Insert(OctreeScene& octreeScene, VisibilityEntry* entry);
//! Updates a VisibilityEntry that is currently bound to this OctreeNode.
//! The provided entry must be bound to this node, but may no longer be bound to this node upon function exit.
void Update(OctreeSystemComponent& octreeSystemComponent, VisibilityEntry* entry);
void Update(OctreeScene& octreeScene, VisibilityEntry* entry);
//! Removes a VisibilityEntry from this OctreeNode.
//! The provided entry must be bound to this node.
void Remove(OctreeSystemComponent& octreeSystemComponent, VisibilityEntry* entry);
void Remove(OctreeScene& octreeScene, VisibilityEntry* entry);
//! Recursively enumerates any OctreeNodes and their children that intersect the provided bounding volume.
//! @{
void Enumerate(const AZ::Aabb& aabb, const IVisibilitySystem::EnumerateCallback& callback) const;
void Enumerate(const AZ::Sphere& sphere, const IVisibilitySystem::EnumerateCallback& callback) const;
void Enumerate(const AZ::Frustum& frustum, const IVisibilitySystem::EnumerateCallback& callback) const;
void Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const;
void Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const;
void Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const;
//! @}
//! Recursively enumerate *all* OctreeNodes that have any entries in them (without any culling).
void EnumerateNoCull(const IVisibilitySystem::EnumerateCallback& callback) const;
void EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const;
//! Returns the set of entries bound to this node.
const AZStd::vector<VisibilityEntry*>& GetEntries() const;
@@ -71,13 +72,13 @@ namespace AzFramework
private:
void TryMerge(OctreeSystemComponent& octreeSystemComponent);
void TryMerge(OctreeScene& octreeScene);
template <typename T>
void EnumerateHelper(const T& boundingVolume, const IVisibilitySystem::EnumerateCallback& callback) const;
void EnumerateHelper(const T& boundingVolume, const IVisibilityScene::EnumerateCallback& callback) const;
void Split(OctreeSystemComponent& octreeSystemComponent);
void Merge(OctreeSystemComponent& octreeSystemComponent);
void Split(OctreeScene& octreeScene);
void Merge(OctreeScene& octreeScene);
// The page is stored in the upper 16-bits of the child node index, the offset into the page is the lower 16-bits
// This gives us a maximum of 65,536 pages and 65,536 nodes per page, for a total of 2^32 - 1 total pages (-1 reserved for the invalid index)
@@ -90,60 +91,47 @@ namespace AzFramework
};
//! Implementation of the visibility system interface.
//! This uses a simple adaptive octreeSystemComponent to support partitioning an object set and efficiently running gathers and visibility queries.
class OctreeSystemComponent
: public AZ::Component
, public IVisibilitySystemRequestBus::Handler
//! This uses a simple adaptive octree to support partitioning an object set for a specific scene and efficiently running gathers and visibility queries.
class OctreeScene
: public IVisibilityScene
{
public:
AZ_RTTI(OctreeScene, "{A88E4D86-11F1-4E3F-A91A-66DE99502B93}");
AZ_CLASS_ALLOCATOR(OctreeScene, AZ::SystemAllocator, 0);
AZ_DISABLE_COPY_MOVE(OctreeScene);
AZ_COMPONENT(OctreeSystemComponent, "{CD4FF1C5-BAF4-421D-951B-1E05DAEEF67B}");
explicit OctreeScene(const AZ::Name& sceneName);
virtual ~OctreeScene();
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
OctreeSystemComponent();
virtual ~OctreeSystemComponent();
//! AZ::Component overrides.
//! @{
void Activate() override;
void Deactivate() override;
//! @}
//! IVisibilitySystem overrides.
//! IVisibilityScene overrides.
//! @{
const AZ::Name& GetName() const override;
void InsertOrUpdateEntry(VisibilityEntry& entry) override;
void RemoveEntry(VisibilityEntry& entry) override;
void Enumerate(const AZ::Aabb& aabb, const IVisibilitySystem::EnumerateCallback& callback) const override;
void Enumerate(const AZ::Sphere& sphere, const IVisibilitySystem::EnumerateCallback& callback) const override;
void Enumerate(const AZ::Frustum& frustum, const IVisibilitySystem::EnumerateCallback& callback) const override;
void EnumerateNoCull(const IVisibilitySystem::EnumerateCallback& callback) const override;
void Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const override;
void Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const override;
void Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const override;
void EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const override;
uint32_t GetEntryCount() const override;
//! @}
//! Returns the OctreeSystemComponent's root node.
OctreeNode& GetRoot();
//! OctreeSystemComponent stats
//! Stats
//! @{
uint32_t GetNodeCount() const;
uint32_t GetFreeNodeCount() const;
uint32_t GetPageCount() const;
uint32_t GetChildNodeCount() const;
void DumpStats(const AZ::ConsoleCommandContainer& arguments);
void DumpStats();
//! @}
private:
uint32_t AllocateChildNodes();
void ReleaseChildNodes(uint32_t nodeIndex);
OctreeNode* GetChildNodesAtIndex(uint32_t nodeIndex) const;
// Bind the DumpStats member function to the console as 'OctreeSystemComponent.DumpStats'
AZ_CONSOLEFUNC(OctreeSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dump octreeSystemComponent stats to the console window");
mutable AZStd::shared_mutex m_sharedMutex;
AZ::Name m_sceneName; //< The uniquely identifying name for the visibility scene.
OctreeNode m_root; //< The root node for the octreeSystemComponent.
uint32_t m_entryCount = 0; //< Metric tracking the number of entries inserted into the octreeSystemComponent.
@@ -158,4 +146,47 @@ namespace AzFramework
friend class OctreeNode; // For access to the node allocator methods
};
//! Implementation of the visibility system interface.
//! This manages creating, destroying, and finding the underlying octrees that are associated with specific scenes
class OctreeSystemComponent
: public AZ::Component
, public IVisibilitySystemRequestBus::Handler
{
public:
AZ_COMPONENT(OctreeSystemComponent, "{CD4FF1C5-BAF4-421D-951B-1E05DAEEF67B}");
// Bind the DumpStats member function to the console as 'OctreeSystemComponent.DumpStats'
AZ_CONSOLEFUNC(OctreeSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dump octreeSystemComponent stats to the console window");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
OctreeSystemComponent();
virtual ~OctreeSystemComponent();
//! AZ::Component overrides.
//! @{
void Activate() override;
void Deactivate() override;
//! @}
//! IVisibilitySystem overrides
//! @{
IVisibilityScene* GetDefaultVisibilityScene() override;
IVisibilityScene* CreateVisibilityScene(const AZ::Name& sceneName) override;
void DestroyVisibilityScene(IVisibilityScene* visScene) override;
IVisibilityScene* FindVisibilityScene(const AZ::Name& sceneName) override;
void DumpStats(const AZ::ConsoleCommandContainer& arguments) override;
//! @}
private:
//! The default scene used for most entities (e.g. gameplay, networking)
OctreeScene* m_defaultScene = nullptr;
//! Other scenes (e.g. each rendering scene) are stored here and looked up by name.
AZStd::vector<OctreeScene*> m_scenes; //using a vector<> here because we'll generally have a small number of scenes
};
}
@@ -102,6 +102,8 @@ set(FILES
Viewport/ScreenGeometry.cpp
Viewport/CameraState.h
Viewport/CameraState.cpp
Viewport/CameraInput.h
Viewport/CameraInput.cpp
Viewport/DisplayContextRequestBus.h
Entity/BehaviorEntity.cpp
Entity/BehaviorEntity.h
@@ -426,4 +428,7 @@ set(FILES
Visibility/EntityVisibilityBoundsUnionSystem.cpp
Visibility/EntityVisibilityQuery.h
Visibility/EntityVisibilityQuery.cpp
Dependency/Dependency.h
Dependency/Dependency.inl
Dependency/Version.h
)
@@ -16,13 +16,14 @@
#include <AzCore/Android/APKFileHandler.h>
#include <AzCore/Android/Utils.h>
#include <AzCore/IO/IOUtils.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/functional.h>
#include <android/api-level.h>
#if __ANDROID_API__ == 19
// The following were apparently introduced in API 21, however in earlier versions of the
// The following were apparently introduced in API 21, however in earlier versions of the
// platform specific headers they were defines. In the move to unified headers, the following
// defines were removed from stat.h
#ifndef stat64
@@ -52,7 +53,7 @@ namespace AZ
if (AZ::Android::Utils::IsApkPath(resolvedPath))
{
return AZ::Android::APKFileHandler::IsDirectory(AZ::Android::Utils::StripApkPrefix(resolvedPath));
return AZ::Android::APKFileHandler::IsDirectory(AZ::Android::Utils::StripApkPrefix(resolvedPath).c_str());
}
struct stat result;
@@ -108,7 +109,7 @@ namespace AZ
if (isInAPK)
{
AZ::OSString strippedPath = AZ::Android::Utils::StripApkPrefix(pathWithoutSlash.c_str());
AZ::IO::FixedMaxPath strippedPath = AZ::Android::Utils::StripApkPrefix(pathWithoutSlash.c_str());
char tempBuffer[AZ_MAX_PATH_LEN] = {0};
@@ -290,6 +290,7 @@ namespace AzFramework
UpdateSystemCursorVisibility();
const bool shouldBeDisabled = (m_systemCursorState == SystemCursorState::ConstrainedAndHidden) ||
(m_systemCursorState == SystemCursorState::ConstrainedAndVisible);
if (!shouldBeDisabled)
{
DestroyDisabledSystemCursorEventTap();
@@ -21,7 +21,7 @@ namespace AzFramework
{
AZStd::string GetPersistentName()
{
AZStd::string persistentName = "Lumberyard";
AZStd::string persistentName = "Open 3D Engine";
char procPath[AZ_MAX_PATH_LEN];
AZ::Utils::GetExecutablePathReturnType ret = AZ::Utils::GetExecutablePath(procPath, AZ_MAX_PATH_LEN);
@@ -56,7 +56,7 @@ namespace AzFramework
bool m_isInBorderlessWindowFullScreenState = false; //!< Was a borderless window used to enter full screen state?
};
const char* NativeWindowImpl_Win32::s_defaultClassName = "LumberyardWin32Class";
const char* NativeWindowImpl_Win32::s_defaultClassName = "O3DEWin32Class";
NativeWindow::Implementation* NativeWindow::Implementation::Create()
{
@@ -85,7 +85,7 @@ namespace AzFramework
//!
//! return another vector relative to the specified display orientation, and such that the
//! +y axis points out the back of the screen and z+ axis points out the top of the device.
//! This flipping of axes is to match Lumberyard's z-up and left-handed coordinate system.
//! This flipping of axes is to match Open 3D Engine's z-up and left-handed coordinate system.
//!
//! \param[in] x The x component of the vector to be aligned
//! \param[in] y The y component of the vector to be aligned