Integrating latest 47acbe8

This commit is contained in:
alexpete
2021-03-25 13:57:57 -07:00
parent 448c549698
commit 75dc720198
10312 changed files with 2711566 additions and 671451 deletions
@@ -61,6 +61,7 @@
#include <AzFramework/Archive/ArchiveFileIO.h>
#include <AzFramework/Script/ScriptRemoteDebugging.h>
#include <AzFramework/Script/ScriptComponent.h>
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
#include <AzFramework/StreamingInstall/StreamingInstall.h>
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzFramework/Viewport/CameraState.h>
@@ -407,6 +408,7 @@ namespace AzFramework
azrtti_typeid<AzFramework::InputSystemComponent>(),
azrtti_typeid<AzFramework::DrillerNetworkAgentComponent>(),
azrtti_typeid<AzFramework::StreamingInstall::StreamingInstallSystemComponent>(),
azrtti_typeid<AzFramework::SpawnableSystemComponent>(),
AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}"), // ScriptDebugAgent
});
@@ -28,6 +28,7 @@
#include <AzFramework/Scene/SceneSystemComponent.h>
#include <AzFramework/Script/ScriptComponent.h>
#include <AzFramework/Script/ScriptRemoteDebugging.h>
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
#include <AzFramework/StreamingInstall/StreamingInstall.h>
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzFramework/Visibility/OctreeSystemComponent.h>
@@ -63,6 +64,7 @@ namespace AzFramework
AzFramework::AzFrameworkConfigurationSystemComponent::CreateDescriptor(),
AzFramework::OctreeSystemComponent::CreateDescriptor(),
AzFramework::SpawnableSystemComponent::CreateDescriptor(),
});
}
@@ -57,7 +57,7 @@ namespace AzFramework
{
Scene* scene = createSceneOutcome.GetValue();
bool success = false;
EntityContextId gameEntityContextId;
EntityContextId gameEntityContextId = EntityContextId::CreateNull();
GameEntityContextRequestBus::BroadcastResult(gameEntityContextId, &GameEntityContextRequests::GetGameEntityContextId);
if (!gameEntityContextId.IsNull())
@@ -12,6 +12,8 @@
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/ToString.h>
#include <AzCore/Component/Entity.h>
namespace AzFramework
{
@@ -33,7 +35,6 @@ namespace AzFramework
void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("SkyCloudService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
@@ -49,8 +50,6 @@ namespace AzFramework
incompatible.push_back(AZ_CRC_CE("PhysXShapeColliderService"));
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
incompatible.push_back(AZ_CRC_CE("TouchBendingPhysicsService"));
incompatible.push_back(AZ_CRC_CE("WaterVolumeService"));
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
incompatible.push_back(AZ_CRC_CE("GeometryService"));
@@ -58,12 +57,9 @@ namespace AzFramework
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
incompatible.push_back(AZ_CRC_CE("FixedVertexContainerService"));
incompatible.push_back(AZ_CRC_CE("PolygonPrismShapeService"));
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
incompatible.push_back(AZ_CRC_CE("SplineService"));
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
incompatible.push_back(AZ_CRC_CE("VariableVertexContainerService"));
}
void NonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
@@ -88,7 +84,17 @@ namespace AzFramework
void NonUniformScaleComponent::SetScale(const AZ::Vector3& scale)
{
m_scale = scale;
if (scale.GetMinElement() >= AZ::MinNonUniformScale)
{
m_scale = scale;
}
else
{
AZ::Vector3 clampedScale = scale.GetMax(AZ::Vector3(AZ::MinNonUniformScale));
AZ_Warning("Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s",
AZ::ToString(scale).c_str(), AZ::ToString(clampedScale).c_str(), GetEntity()->GetName().c_str());
m_scale = clampedScale;
}
m_scaleChangedEvent.Signal(m_scale);
}
@@ -17,25 +17,23 @@
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Quaternion.h>
#include <AzFramework/Network/NetBindingHandlerBus.h>
#include <AzFramework/Network/NetBindingSystemBus.h>
#include <AzFramework/Network/NetworkContext.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Serialize/CompressionMarshal.h>
namespace AZ
{
class BehaviorTransformNotificationBusHandler : public TransformNotificationBus::Handler, public AZ::BehaviorEBusHandler
class BehaviorTransformNotificationBusHandler
: public TransformNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorTransformNotificationBusHandler, "{9CEF4DAB-F359-4A3E-9856-7780281E0DAA}", AZ::SystemAllocator
, OnTransformChanged
, OnParentChanged
, OnChildAdded
, OnChildRemoved
AZ_EBUS_BEHAVIOR_BINDER
(
BehaviorTransformNotificationBusHandler,
"{9CEF4DAB-F359-4A3E-9856-7780281E0DAA}",
AZ::SystemAllocator,
OnTransformChanged,
OnParentChanged,
OnChildAdded,
OnChildRemoved
);
void OnTransformChanged(const Transform& localTM, const Transform& worldTM) override
@@ -80,109 +78,10 @@ namespace AZ
new(self) TransformConfig();
}
}
} // namespace AZ
namespace AzFramework
{
//=========================================================================
// TransformReplicaChunk
// [3/9/2016]
//=========================================================================
class TransformReplicaChunk
: public GridMate::ReplicaChunkBase
{
public:
AZ_CLASS_ALLOCATOR(TransformReplicaChunk, AZ::SystemAllocator, 0);
static const char* GetChunkName() { return "TransformReplicaChunk"; }
TransformReplicaChunk()
: m_parentId("ParentId")
, m_localTranslation("LocalTranslationData")
, m_localRotation("LocalRotationData")
, m_localScale("LocalScaleData")
{
m_localTranslation.GetThrottler().SetThreshold(AZ::Vector3(0.005f, 0.005f, 0.005f));
m_localScale.GetThrottler().SetThreshold(AZ::Vector3(0.001f, 0.001f, 0.001f));
}
bool IsReplicaMigratable() override
{
return true;
}
void SetInitialTM(const AZ::Transform& t)
{
m_initialWorldTM = t;
}
void SetLocalTM(const AZ::Transform& t)
{
m_localScale.Set(t.GetScale());
m_localTranslation.Set(t.GetTranslation());
m_localRotation.Set(t.GetRotation());
}
AZ::Transform GetLocalTransform() const
{
AZ::Transform newXform;
newXform.SetTranslation(m_localTranslation.Get());
newXform.SetRotation(m_localRotation.Get());
newXform.SetScale(m_localScale.Get());
return newXform;
}
unsigned int GetLocalTime()
{
return GetReplicaManager()->GetTime().m_localTime;
}
// parentId (can have no parent)
DataSet<AZ::u64>::BindInterface<TransformComponent, &TransformComponent::OnNewNetParentData> m_parentId;
// transform
DataSet<AZ::Vector3, GridMate::Marshaler<AZ::Vector3>, GridMate::EpsilonThrottle<AZ::Vector3>>::BindInterface<TransformComponent, &TransformComponent::OnNewPositionData> m_localTranslation;
DataSet<AZ::Quaternion, GridMate::Marshaler<AZ::Quaternion>, GridMate::BasicThrottle<AZ::Quaternion>>::BindInterface<TransformComponent, &TransformComponent::OnNewRotationData> m_localRotation;
DataSet<AZ::Vector3, GridMate::Marshaler<AZ::Vector3>, GridMate::EpsilonThrottle<AZ::Vector3>>::BindInterface<TransformComponent, &TransformComponent::OnNewScaleData> m_localScale;
AZ::Transform m_initialWorldTM;
class Descriptor
: public ExternalChunkDescriptor<TransformReplicaChunk>
{
public:
ReplicaChunkBase* CreateFromStream(UnmarshalContext& context) override
{
// Pre/Post construct allow DataSets and RPCs to bind to the chunk.
TransformReplicaChunk* transformChunk = aznew TransformReplicaChunk;
context.m_iBuf->Read(transformChunk->m_initialWorldTM);
return transformChunk;
}
void DiscardCtorStream(UnmarshalContext& context) override
{
AZ::Transform discard;
context.m_iBuf->Read(discard);
}
void MarshalCtorData(ReplicaChunkBase* chunk, WriteBuffer& wb) override
{
TransformReplicaChunk* transformChunk = static_cast<TransformReplicaChunk*>(chunk);
TransformComponent* transformComponent = static_cast<TransformComponent*>(transformChunk->GetHandler());
if (transformComponent)
{
wb.Write(transformComponent->GetWorldTM());
}
else
{
wb.Write(transformChunk->m_initialWorldTM);
}
}
};
};
//=========================================================================
bool TransformComponentVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() < 3)
@@ -204,28 +103,10 @@ namespace AzFramework
// future re-additions of it won't remove it (as long as they bump the version number.)
classElement.RemoveElementByName(AZ_CRC("InterpolateScale", 0x9d00b831));
}
return true;
}
//=========================================================================
// TransformComponent
// [8/9/2013]
//=========================================================================
TransformComponent::TransformComponent()
: m_parentTM(nullptr)
, m_parentActive(false)
, m_onNewParentKeepWorldTM(true)
, m_parentActivationTransformMode(ParentActivationTransformMode::MaintainOriginalRelativeTransform)
, m_isStatic(false)
, m_interpolatePosition(AZ::InterpolationMode::NoInterpolation)
, m_interpolateRotation(AZ::InterpolationMode::NoInterpolation)
{
m_localTM = AZ::Transform::CreateIdentity();
m_worldTM = AZ::Transform::CreateIdentity();
}
TransformComponent::TransformComponent(const TransformComponent& copy)
: m_localTM(copy.m_localTM)
, m_worldTM(copy.m_worldTM)
@@ -235,89 +116,9 @@ namespace AzFramework
, m_notificationBus(nullptr)
, m_onNewParentKeepWorldTM(copy.m_onNewParentKeepWorldTM)
, m_parentActivationTransformMode(copy.m_parentActivationTransformMode)
, m_replicaChunk(nullptr)
, m_isStatic(copy.m_isStatic)
, m_interpolatePosition(copy.m_interpolatePosition)
, m_interpolateRotation(copy.m_interpolateRotation)
, m_netTargetTranslation()
, m_netTargetRotation()
, m_netTargetScale(copy.m_netTargetScale)
{
CreateSamples();
if (copy.m_netTargetTranslation)
{
m_netTargetTranslation->SetNewTarget(copy.m_netTargetTranslation->GetTargetValue(), copy.m_netTargetTranslation->GetTargetTimestamp());
}
if (copy.m_netTargetRotation)
{
m_netTargetRotation->SetNewTarget(copy.m_netTargetRotation->GetTargetValue(), copy.m_netTargetRotation->GetTargetTimestamp());
}
SetSyncEnabled(copy.m_isSyncEnabled);
}
void TransformComponent::CreateTranslationSample()
{
switch(m_interpolatePosition)
{
case AZ::InterpolationMode::LinearInterpolation:
m_netTargetTranslation = AZStd::make_unique<AZ::LinearlyInterpolatedSample<AZ::Vector3>>();
break;
case AZ::InterpolationMode::NoInterpolation:
default:
m_netTargetTranslation = AZStd::make_unique<AZ::UninterpolatedSample<AZ::Vector3>>();
break;
}
}
void TransformComponent::CreateRotationSample()
{
switch (m_interpolateRotation)
{
case AZ::InterpolationMode::LinearInterpolation:
m_netTargetRotation = AZStd::make_unique<AZ::LinearlyInterpolatedSample<AZ::Quaternion>>();
break;
case AZ::InterpolationMode::NoInterpolation:
default:
m_netTargetRotation = AZStd::make_unique<AZ::UninterpolatedSample<AZ::Quaternion>>();
break;
}
}
void TransformComponent::CreateSamples()
{
if (m_netTargetTranslation)
{
auto target = m_netTargetTranslation->GetTargetValue();
auto timeStamp = m_netTargetTranslation->GetTargetTimestamp();
CreateTranslationSample();
m_netTargetTranslation->SetNewTarget(target, timeStamp);
}
else
{
CreateTranslationSample();
}
if (m_netTargetRotation)
{
auto target = m_netTargetRotation->GetTargetValue();
auto timeStamp = m_netTargetRotation->GetTargetTimestamp();
CreateRotationSample();
m_netTargetRotation->SetNewTarget(target, timeStamp);
}
else
{
CreateRotationSample();
}
}
TransformComponent::~TransformComponent()
{
;
}
bool TransformComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
@@ -328,9 +129,6 @@ namespace AzFramework
m_worldTM = config->m_worldTransform;
m_parentId = config->m_parentId;
m_parentActivationTransformMode = config->m_parentActivationTransformMode;
SetSyncEnabled(config->m_netSyncEnabled);
m_interpolatePosition = config->m_interpolatePosition;
m_interpolateRotation = config->m_interpolateRotation;
m_isStatic = config->m_isStatic;
return true;
}
@@ -345,9 +143,6 @@ namespace AzFramework
config->m_worldTransform = m_worldTM;
config->m_parentId = m_parentId;
config->m_parentActivationTransformMode = m_parentActivationTransformMode;
config->m_netSyncEnabled = IsSyncEnabled();
config->m_interpolatePosition = m_interpolatePosition;
config->m_interpolateRotation = m_interpolateRotation;
config->m_isStatic = m_isStatic;
return true;
}
@@ -366,8 +161,11 @@ namespace AzFramework
void TransformComponent::Deactivate()
{
EBUS_EVENT_ID(m_parentId, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId());
UnbindFromNetwork();
auto parentTransform = AZ::TransformBus::FindFirstHandler(m_parentId);
if (parentTransform)
{
parentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Removed, GetEntityId());
}
m_notificationBus = nullptr;
if (m_parentId.IsValid())
@@ -379,12 +177,31 @@ namespace AzFramework
AZ::TransformBus::Handler::BusDisconnect();
}
void TransformComponent::BindTransformChangedEventHandler(AZ::TransformChangedEvent::Handler& handler)
{
handler.Connect(m_transformChangedEvent);
}
void TransformComponent::BindParentChangedEventHandler(AZ::ParentChangedEvent::Handler& handler)
{
handler.Connect(m_parentChangedEvent);
}
void TransformComponent::BindChildChangedEventHandler(AZ::ChildChangedEvent::Handler& handler)
{
handler.Connect(m_childChangedEvent);
}
void TransformComponent::NotifyChildChangedEvent(AZ::ChildChangeType changeType, AZ::EntityId entityId)
{
m_childChangedEvent.Signal(changeType, entityId);
}
void TransformComponent::SetLocalTM(const AZ::Transform& tm)
{
if (AreMoveRequestsAllowed())
{
SetLocalTMImpl(tm);
UpdateReplicaChunk();
}
}
@@ -393,28 +210,17 @@ namespace AzFramework
if (AreMoveRequestsAllowed())
{
SetWorldTMImpl(tm);
UpdateReplicaChunk();
}
}
void TransformComponent::SetParent(AZ::EntityId id)
{
if (!IsNetworkControlled())
{
SetParentImpl(id, true);
UpdateReplicaChunk();
}
SetParentImpl(id, true);
}
void TransformComponent::SetParentRelative(AZ::EntityId id)
{
if (!IsNetworkControlled())
{
SetParentImpl(id, m_isStatic);
UpdateReplicaChunk();
}
SetParentImpl(id, m_isStatic);
}
void TransformComponent::SetWorldTranslation(const AZ::Vector3& newPosition)
@@ -860,267 +666,6 @@ namespace AzFramework
}
void TransformComponent::OnEntityActivated(const AZ::EntityId& parentEntityId)
{
OnEntityActivatedImpl(parentEntityId);
UpdateReplicaChunk();
}
void TransformComponent::OnEntityDeactivated(const AZ::EntityId& parentEntityId)
{
if (!IsNetworkControlled())
{
OnEntityDeactivateImpl(parentEntityId);
UpdateReplicaChunk();
}
else
{
// If this transform is network controlled, then the localTM is updated by the network,
// so update m_parentTM and compute worldTM instead.
AZ_Assert(parentEntityId == m_parentId, "We expect to receive notifications only from the current parent!");
m_parentTM = nullptr;
m_parentActive = false;
ComputeWorldTM();
}
}
GridMate::ReplicaChunkPtr TransformComponent::GetNetworkBinding()
{
TransformReplicaChunk* replicaChunk = GridMate::CreateReplicaChunk<TransformReplicaChunk>();
replicaChunk->SetHandler(this);
m_replicaChunk = replicaChunk;
UpdateReplicaChunk();
return m_replicaChunk;
}
void TransformComponent::SetNetworkBinding(GridMate::ReplicaChunkPtr replicaChunk)
{
AZ_Assert(m_replicaChunk == nullptr, "Being bound to two ReplicaChunks");
bool isTransformChunk = replicaChunk != nullptr;
AZ_Assert(isTransformChunk, "Being bound to invalid chunk type");
if (isTransformChunk)
{
replicaChunk->SetHandler(this);
m_replicaChunk = replicaChunk;
TransformReplicaChunk* transformReplicaChunk = static_cast<TransformReplicaChunk*>(m_replicaChunk.get());
m_parentId = AZ::EntityId(transformReplicaChunk->m_parentId.Get());
m_worldTM = transformReplicaChunk->m_initialWorldTM;
m_localTM = transformReplicaChunk->GetLocalTransform();
CreateSamples();
m_netTargetTranslation->SetNewTarget(
transformReplicaChunk->m_localTranslation.Get(),
transformReplicaChunk->m_localTranslation.GetLastUpdateTime());
m_netTargetRotation->SetNewTarget(
transformReplicaChunk->m_localRotation.Get(),
transformReplicaChunk->m_localRotation.GetLastUpdateTime());
m_netTargetScale = transformReplicaChunk->m_localScale.Get();
if (HasAnyInterpolation())
{
// only connect if interpolation was selected for either position or rotation
AZ::TickBus::Handler::BusConnect();
}
}
m_onNewParentKeepWorldTM = false;
}
void TransformComponent::UnbindFromNetwork()
{
if (HasAnyInterpolation())
{
AZ::TickBus::Handler::BusDisconnect();
}
if (m_replicaChunk)
{
m_replicaChunk->SetHandler(nullptr);
m_replicaChunk = nullptr;
}
}
void TransformComponent::OnNewNetTransformData(const AZ::Transform& transform, const GridMate::TimeContext& /*tc*/)
{
SetLocalTMImpl(transform);
}
void TransformComponent::OnNewNetParentData(const AZ::u64& parentId, const GridMate::TimeContext& /*tc*/)
{
SetParentImpl(AZ::EntityId(parentId), false);
}
bool TransformComponent::IsNetworkControlled() const
{
return m_replicaChunk && m_replicaChunk->GetReplica() && !m_replicaChunk->IsMaster();
}
bool TransformComponent::IsPositionInterpolated()
{
return m_interpolatePosition != AZ::InterpolationMode::NoInterpolation;
}
bool TransformComponent::IsRotationInterpolated()
{
return m_interpolateRotation != AZ::InterpolationMode::NoInterpolation;
}
bool TransformComponent::HasAnyInterpolation()
{
return IsPositionInterpolated() || IsRotationInterpolated();
}
void TransformComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*currentTime*/)
{
if (GetEntity() && GetEntity()->GetState() == AZ::Entity::State::Active)
{
if (m_replicaChunk && m_replicaChunk->IsProxy())
{
const unsigned int localTime = m_replicaChunk->GetReplicaManager()->GetTime().m_localTime;
const AZ::Transform newXform = GetInterpolatedTransform(localTime);
SetLocalTMImpl(newXform);
}
}
}
AZ::Transform TransformComponent::GetInterpolatedTransform(unsigned localTime)
{
const AZ::Vector3 newTranslation = m_netTargetTranslation->GetInterpolatedValue(localTime);
const AZ::Quaternion newRotation = m_netTargetRotation->GetInterpolatedValue(localTime);
AZ::Transform newXform = AZ::Transform::CreateFromQuaternionAndTranslation(newRotation, newTranslation);
newXform.MultiplyByScale(m_netTargetScale);
return newXform;
}
void TransformComponent::OnNewPositionData(const AZ::Vector3& translation, const GridMate::TimeContext& tc)
{
m_netTargetTranslation->SetNewTarget(translation, tc.m_realTime);
if (!HasAnyInterpolation())
{
unsigned int localTime = m_replicaChunk->GetReplicaManager()->GetTime().m_localTime;
AZ::Transform newXform = GetInterpolatedTransform(localTime);
SetLocalTMImpl(newXform);
}
};
void TransformComponent::OnNewRotationData(const AZ::Quaternion& rotation, const GridMate::TimeContext& tc)
{
m_netTargetRotation->SetNewTarget(rotation, tc.m_realTime);
if (!HasAnyInterpolation())
{
unsigned int localTime = m_replicaChunk->GetReplicaManager()->GetTime().m_localTime;
AZ::Transform newXform = GetInterpolatedTransform(localTime);
SetLocalTMImpl(newXform);
}
}
void TransformComponent::OnNewScaleData(const AZ::Vector3& scale, const GridMate::TimeContext& /*tc*/)
{
// no interpolation of scale by design, very unlikely somebody needs it
m_netTargetScale = scale;
}
void TransformComponent::UpdateReplicaChunk()
{
if (!IsNetworkControlled() && m_replicaChunk)
{
TransformReplicaChunk* transformReplicaChunk = static_cast<TransformReplicaChunk*>(m_replicaChunk.get());
transformReplicaChunk->SetLocalTM(GetLocalTM());
transformReplicaChunk->m_parentId.Set(static_cast<AZ::u64>(GetParentId()));
}
}
void TransformComponent::SetParentImpl(AZ::EntityId parentId, bool isKeepWorldTM)
{
if (parentId == GetEntityId())
{
AZ_Warning("TransformComponent", false, "An entity can not be set as its own parent.");
return;
}
AZ::EntityId oldParent = m_parentId;
if (m_parentId.IsValid())
{
AZ::TransformNotificationBus::Handler::BusDisconnect();
AZ::TransformHierarchyInformationBus::Handler::BusDisconnect();
AZ::EntityBus::Handler::BusDisconnect();
m_parentActive = false;
}
m_parentId = parentId;
if (m_parentId.IsValid())
{
AZ::Entity* parentEntity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_parentId);
m_parentActive = parentEntity && (parentEntity->GetState() == AZ::Entity::State::Active);
m_onNewParentKeepWorldTM = isKeepWorldTM;
AZ::TransformNotificationBus::Handler::BusConnect(m_parentId);
AZ::TransformHierarchyInformationBus::Handler::BusConnect(m_parentId);
AZ::EntityBus::Handler::BusConnect(m_parentId);
}
else
{
m_parentTM = nullptr;
if (isKeepWorldTM)
{
SetWorldTM(m_worldTM);
}
else
{
SetLocalTM(m_localTM);
}
if (oldParent.IsValid())
{
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
}
}
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnParentChanged, oldParent, parentId);
if (oldParent != parentId) // Don't send removal notification while activating.
{
EBUS_EVENT_ID(oldParent, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId());
}
EBUS_EVENT_ID(parentId, AZ::TransformNotificationBus, OnChildAdded, GetEntityId());
}
void TransformComponent::SetLocalTMImpl(const AZ::Transform& tm)
{
m_localTM = tm;
ComputeWorldTM(); // We can user dirty flags and compute it later on demand
}
void TransformComponent::SetWorldTMImpl(const AZ::Transform& tm)
{
m_worldTM = tm;
ComputeLocalTM(); // We can user dirty flags and compute it later on demand
}
void TransformComponent::OnTransformChangedImpl(const AZ::Transform& /*parentLocalTM*/, const AZ::Transform& parentWorldTM)
{
// Called when our parent transform changes
// Ignore the event until we've already derived our local transform.
if (m_parentTM)
{
m_worldTM = parentWorldTM * m_localTM;
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
}
}
void TransformComponent::OnEntityActivatedImpl(const AZ::EntityId& parentEntityId)
{
AZ_Assert(parentEntityId == m_parentId, "We expect to receive notifications only from the current parent!");
@@ -1171,15 +716,109 @@ namespace AzFramework
}
}
void TransformComponent::OnEntityDeactivateImpl(const AZ::EntityId& parentEntityId)
void TransformComponent::OnEntityDeactivated([[maybe_unused]] const AZ::EntityId& parentEntityId)
{
(void)parentEntityId;
AZ_Assert(parentEntityId == m_parentId, "We expect to receive notifications only from the current parent!");
m_parentTM = nullptr;
m_parentActive = false;
ComputeLocalTM();
}
void TransformComponent::SetParentImpl(AZ::EntityId parentId, bool isKeepWorldTM)
{
if (parentId == GetEntityId())
{
AZ_Warning("TransformComponent", false, "An entity can not be set as its own parent.");
return;
}
AZ::EntityId oldParent = m_parentId;
if (m_parentId.IsValid())
{
AZ::TransformNotificationBus::Handler::BusDisconnect();
AZ::TransformHierarchyInformationBus::Handler::BusDisconnect();
AZ::EntityBus::Handler::BusDisconnect();
m_parentActive = false;
}
m_parentId = parentId;
if (m_parentId.IsValid())
{
AZ::Entity* parentEntity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_parentId);
m_parentActive = parentEntity && (parentEntity->GetState() == AZ::Entity::State::Active);
m_onNewParentKeepWorldTM = isKeepWorldTM;
AZ::TransformNotificationBus::Handler::BusConnect(m_parentId);
AZ::TransformHierarchyInformationBus::Handler::BusConnect(m_parentId);
AZ::EntityBus::Handler::BusConnect(m_parentId);
}
else
{
m_parentTM = nullptr;
if (isKeepWorldTM)
{
SetWorldTM(m_worldTM);
}
else
{
SetLocalTM(m_localTM);
}
if (oldParent.IsValid())
{
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
}
}
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnParentChanged, oldParent, parentId);
m_parentChangedEvent.Signal(oldParent, parentId);
if (oldParent != parentId) // Don't send removal notification while activating.
{
EBUS_EVENT_ID(oldParent, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId());
auto oldParentTransform = AZ::TransformBus::FindFirstHandler(oldParent);
if (oldParentTransform)
{
oldParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Removed, GetEntityId());
}
}
EBUS_EVENT_ID(parentId, AZ::TransformNotificationBus, OnChildAdded, GetEntityId());
auto newParentTransform = AZ::TransformBus::FindFirstHandler(parentId);
if (newParentTransform)
{
newParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Added, GetEntityId());
}
}
void TransformComponent::SetLocalTMImpl(const AZ::Transform& tm)
{
m_localTM = tm;
ComputeWorldTM(); // We can user dirty flags and compute it later on demand
}
void TransformComponent::SetWorldTMImpl(const AZ::Transform& tm)
{
m_worldTM = tm;
ComputeLocalTM(); // We can user dirty flags and compute it later on demand
}
void TransformComponent::OnTransformChangedImpl(const AZ::Transform& /*parentLocalTM*/, const AZ::Transform& parentWorldTM)
{
// Called when our parent transform changes
// Ignore the event until we've already derived our local transform.
if (m_parentTM)
{
m_worldTM = parentWorldTM * m_localTM;
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
}
}
void TransformComponent::ComputeLocalTM()
{
if (m_parentTM)
@@ -1192,6 +831,7 @@ namespace AzFramework
}
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
}
void TransformComponent::ComputeWorldTM()
@@ -1206,15 +846,11 @@ namespace AzFramework
}
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
}
bool TransformComponent::AreMoveRequestsAllowed() const
{
if (IsNetworkControlled())
{
return false;
}
// Don't allow static transform to be moved while entity is activated.
// But do allow a static transform to be moved when the entity is deactivated.
if (m_isStatic && m_entity && (m_entity->GetState() > AZ::Entity::State::Init))
@@ -1248,7 +884,7 @@ namespace AzFramework
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection);
if(behaviorContext)
if (behaviorContext)
{
behaviorContext->EBus<AZ::TransformNotificationBus>("TransformNotificationBus")->
Handler<AZ::BehaviorTransformNotificationBusHandler>();
@@ -1396,26 +1032,8 @@ namespace AzFramework
->Property("parentActivationTransformMode",
[](AZ::TransformConfig* config) { return (int&)(config->m_parentActivationTransformMode); },
[](AZ::TransformConfig* config, const int& i) { config->m_parentActivationTransformMode = (AZ::TransformConfig::ParentActivationTransformMode)i; })
->Property("netSyncEnabled", BehaviorValueProperty(&AZ::TransformConfig::m_netSyncEnabled))
->Property("interpolatePosition",
[](AZ::TransformConfig* config) { return (int&)(config->m_interpolatePosition); },
[](AZ::TransformConfig* config, const int& i) { config->m_interpolatePosition = (AZ::InterpolationMode)i; })
->Property("interpolateRotation",
[](AZ::TransformConfig* config) { return (int&)(config->m_interpolateRotation); },
[](AZ::TransformConfig* config, const int& i) { config->m_interpolateRotation = (AZ::InterpolationMode)i; })
->Property("isStatic", BehaviorValueProperty(&AZ::TransformConfig::m_isStatic))
;
}
NetworkContext* netContext = azrtti_cast<NetworkContext*>(reflection);
if (netContext)
{
netContext->Class<TransformComponent>()
->Chunk<TransformReplicaChunk, TransformReplicaChunk::Descriptor>()
->Field("ParentId", &TransformReplicaChunk::m_parentId)
->Field("LocalTranslationData", &TransformReplicaChunk::m_localTranslation)
->Field("LocalRotationData", &TransformReplicaChunk::m_localRotation)
->Field("LocalScaleData", &TransformReplicaChunk::m_localScale);
}
}
} // namespace AZ
@@ -29,26 +29,20 @@ namespace AzToolsFramework
namespace AzFramework
{
class TransformReplicaChunk;
class GameEntityContextComponent;
/// @deprecated Use AZ::TransformConfig
using TransformComponentConfiguration = AZ::TransformConfig;
//! Fundamental component that describes the entity in 3D space.
//! It is net-bindable. Only local transform is synchronized, so when
//! parented, the parent must properly synchronize its transform as well.
class TransformComponent
: public AZ::Component
, public AZ::EntityBus::Handler
, public AZ::TransformBus::Handler
, public AZ::TransformNotificationBus::Handler
, public AZ::EntityBus::Handler
, public AZ::TickBus::Handler
, private AZ::TransformHierarchyInformationBus::Handler
, public NetBindable
{
friend class TransformReplicaChunk;
public:
AZ_COMPONENT(TransformComponent, AZ::TransformComponentTypeId, NetBindable, AZ::TransformInterface);
@@ -56,11 +50,15 @@ namespace AzFramework
using ParentActivationTransformMode = AZ::TransformConfig::ParentActivationTransformMode;
TransformComponent();
TransformComponent() = default;
TransformComponent(const TransformComponent& copy);
virtual ~TransformComponent();
~TransformComponent() override = default;
// TransformBus events (publicly accessible)
void BindTransformChangedEventHandler(AZ::TransformChangedEvent::Handler& handler) override;
void BindParentChangedEventHandler(AZ::ParentChangedEvent::Handler& handler) override;
void BindChildChangedEventHandler(AZ::ChildChangedEvent::Handler& handler) override;
void NotifyChildChangedEvent(AZ::ChildChangeType changeType, AZ::EntityId entityId) override;
//! Returns true if the tm was set to the local transform.
const AZ::Transform& GetLocalTM() override { return m_localTM; }
//! Returns true if the tm was set to the world transform.
@@ -115,8 +113,6 @@ namespace AzFramework
float GetLocalY() override;
float GetLocalZ() override;
bool IsPositionInterpolated() override;
// Rotation modifiers
void SetRotation(const AZ::Vector3& eulerAnglesRadian) override;
void SetRotationQuaternion(const AZ::Quaternion& quaternion) override;
@@ -148,8 +144,6 @@ namespace AzFramework
AZ::Vector3 GetLocalRotation() override;
AZ::Quaternion GetLocalRotationQuaternion() override;
bool IsRotationInterpolated() override;
// Scale Modifiers
void SetScale(const AZ::Vector3& scale) override;
void SetScaleX(float scaleX) override;
@@ -187,28 +181,6 @@ namespace AzFramework
void OnEntityDeactivated(const AZ::EntityId& parentEntityId) override;
//! @}
//! Methods implementing NetBindable.
//! @{
GridMate::ReplicaChunkPtr GetNetworkBinding() override;
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk) override;
void UnbindFromNetwork() override;
//! Called by the net chunk when new transform data arrives from the network.
void OnNewNetTransformData(const AZ::Transform& transform, const GridMate::TimeContext& tc);
//! Called by the net chunk when new parent id arrives from the network.
void OnNewNetParentData(const AZ::u64& parentId, const GridMate::TimeContext& tc);
//! Returns true if this instance is non-authoritative.
bool IsNetworkControlled() const;
//! Triggers an update of the chunk data. Should only be called on the authoritative instance.
void UpdateReplicaChunk();
//! @}
// AZ::TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//////////////////////////////////////////////////////////////////////////
// Actual Implementation Functions
// They are protected so we can gate them when network-controlled
@@ -216,8 +188,6 @@ namespace AzFramework
void SetLocalTMImpl(const AZ::Transform& tm);
void SetWorldTMImpl(const AZ::Transform& tm);
void OnTransformChangedImpl(const AZ::Transform& parentLocalTM, const AZ::Transform& parentWorldTM);
void OnEntityActivatedImpl(const AZ::EntityId& parentEntityId);
void OnEntityDeactivateImpl(const AZ::EntityId& parentEntityId);
void ComputeLocalTM();
void ComputeWorldTM();
//////////////////////////////////////////////////////////////////////////
@@ -228,44 +198,31 @@ namespace AzFramework
// TransformHierarchyInformationBus
void GatherChildren(AZStd::vector<AZ::EntityId>& children) override;
//! Feedback from corresponding replica chunk.
//! @{
void OnNewPositionData(const AZ::Vector3&, const GridMate::TimeContext&);
void OnNewRotationData(const AZ::Quaternion&, const GridMate::TimeContext&);
void OnNewScaleData(const AZ::Vector3&, const GridMate::TimeContext&);
//! @}
/// \ref ComponentDescriptor::GetProvidedServices
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
/// \ref ComponentDescriptor::Reflect
static void Reflect(AZ::ReflectContext* reflection);
AZ::Transform m_localTM; ///< Local transform relative to parent transform (same as worldTM if no parent).
AZ::Transform m_worldTM; ///< World transform including parent transform (same as localTM if no parent).
AZ::EntityId m_parentId; ///< If valid, this transform is parented to m_parentId.
AZ::TransformInterface* m_parentTM; ///< Cached - pointer to parent transform, to avoid extra calls. Valid only when if it's present.
bool m_parentActive; ///< Keeps track of the state of the parent entity.
AZ::TransformNotificationBus::BusPtr m_notificationBus; ///< Cached bus pointer to the notification bus.
bool m_onNewParentKeepWorldTM; ///< If set, recompute localTM instead of worldTM when parent becomes active.
ParentActivationTransformMode m_parentActivationTransformMode;
GridMate::ReplicaChunkPtr m_replicaChunk;
bool m_isStatic; ///< If true, the transform is static and doesn't move while entity is active.
AZ::InterpolationMode m_interpolatePosition; ///< Interpolation mode for net-synced position updates.
AZ::InterpolationMode m_interpolateRotation; ///< Interpolation mode for net-synced rotation updates.
AZ::TransformChangedEvent m_transformChangedEvent; ///< Event used to signal when a transform changes.
AZ::ParentChangedEvent m_parentChangedEvent; ///< Event used to signal when a transforms parent changes.
AZ::ChildChangedEvent m_childChangedEvent; ///< Event used to signal when a transform has a child entity added or removed.
private:
AZ::Transform m_localTM = AZ::Transform::CreateIdentity(); ///< Local transform relative to parent transform (same as worldTM if no parent).
AZ::Transform m_worldTM = AZ::Transform::CreateIdentity(); ///< World transform including parent transform (same as localTM if no parent).
bool HasAnyInterpolation();
AZ::EntityId m_parentId; ///< If valid, this transform is parented to m_parentId.
AZ::TransformInterface* m_parentTM = nullptr; ///< Cached - pointer to parent transform, to avoid extra calls. Valid only when if it's present.
AZ::TransformNotificationBus::BusPtr m_notificationBus; ///< Cached bus pointer to the notification bus.
ParentActivationTransformMode m_parentActivationTransformMode = ParentActivationTransformMode::MaintainOriginalRelativeTransform;
bool m_parentActive = false; ///< Keeps track of the state of the parent entity.
bool m_onNewParentKeepWorldTM = true; ///< If set, recompute localTM instead of worldTM when parent becomes active.
bool m_isStatic = false; ///< If true, the transform is static and doesn't move while entity is active.
void CreateSamples();
void CreateTranslationSample();
void CreateRotationSample();
AZ::Transform GetInterpolatedTransform(unsigned int localTime);
AZStd::unique_ptr<AZ::Sample<AZ::Vector3>> m_netTargetTranslation;
AZStd::unique_ptr<AZ::Sample<AZ::Quaternion>> m_netTargetRotation;
AZ::Vector3 m_netTargetScale;
//! @deprecated
//! @{
AZ::InterpolationMode m_interpolatePosition = AZ::InterpolationMode::NoInterpolation;
AZ::InterpolationMode m_interpolateRotation = AZ::InterpolationMode::NoInterpolation;
//! @}
};
} // namespace AZ
@@ -0,0 +1,47 @@
/*
* 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 <AzFramework/Engine/Engine.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Utils/Utils.h>
namespace AzFramework
{
namespace Engine
{
AZ::IO::FixedMaxPath FindEngineRoot(AZStd::string_view searchPath)
{
// File to locate
const char engineRootMarker[] = "engine.json";
AZ::IO::FixedMaxPath currentSearchPath{searchPath};
if (currentSearchPath.empty())
{
char executablePath[AZ_MAX_PATH_LEN];
AZ::Utils::GetExecutablePathReturnType result = AZ::Utils::GetExecutablePath(executablePath, AZ_MAX_PATH_LEN);
currentSearchPath = executablePath;
}
do
{
currentSearchPath = currentSearchPath.ParentPath();
if (AZ::IO::SystemFile::Exists((currentSearchPath / engineRootMarker).c_str()))
{
return currentSearchPath;
}
} while (currentSearchPath.ParentPath() != currentSearchPath);
return {};
}
}
} // AzFramework
@@ -0,0 +1,25 @@
/*
* 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/IO/Path/Path.h>
#include <AzCore/std/string/string_view.h>
namespace AzFramework
{
namespace Engine
{
// Helper to attempt to locate the engine root by searching up the directory tree. If no search path is
// provided the current executable path is used
AZ::IO::FixedMaxPath FindEngineRoot(AZStd::string_view searchPath = {});
} // Engine
} // AzFramework
@@ -93,6 +93,8 @@ namespace AzFramework
virtual void SetEntitiesRemovedCallback(OnEntitiesRemovedCallback onEntitiesRemovedCallback) = 0;
virtual void SetValidateEntitiesCallback(ValidateEntitiesCallback validateEntitiesCallback) = 0;
bool m_shouldAssertForLegacySlicesUsage = false;
protected:
OnEntitiesAddedCallback m_entitiesAddedCallback;
OnEntitiesRemovedCallback m_entitiesRemovedCallback;
@@ -19,6 +19,19 @@ namespace AzFramework
using EntityContextId = AZ::Uuid;
using EntityList = AZStd::vector<AZ::Entity*>;
class EntityOwnershipService;
class EntityOwnershipServiceInterface
{
public:
AZ_RTTI(EntityOwnershipServiceInterface, "{6490E958-5DF5-45CF-9A25-D857DB0C67DB}");
EntityOwnershipServiceInterface() = default;
virtual ~EntityOwnershipServiceInterface() = default;
virtual AZStd::unique_ptr<EntityOwnershipService> CreateEntityOwnershipService() = 0;
};
class EntityOwnershipServiceNotifications
: public AZ::EBusTraits
{
@@ -1,288 +0,0 @@
/*
* 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/EBus/EBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Quaternion.h>
namespace Physics
{
constexpr const char * const AZ_TOUCH_BENDING_WINDOW = "AzTouchBending";
// Bone orientation
//
// _ TOP Point Z+ up
// | | ^
// | | |
// |*| (0,0,0) X- <------|--------> X+
// | | |
// |_| BOTTOM Point |
// Z-
/// SpinePoint contains properties of mass, thickness, damping and stiffness
/// for the bone that will be made. The SpinePoint is the BOTTOM point
/// of a Bone.
struct SpinePoint
{
///mass in Kg.
float m_mass;
///If you imagine the Bone to be a cylinder, this is its radius in meters.
float m_thickness;
///A value from 0.0 to 1.0. 0.0 means no damping, lots of back and forth movement around its original pose.
///1.0 means maximum damping, the segment will quickly converge back to its original pose.
float m_damping;
///A value from 0.0 to 1.0. 0.0 means no stiffness, the segment will look like a sad willow,
///It would never return to its original pose.
float m_stiffness;
///Position is in Model Space.
AZ::Vector3 m_position;
};
struct Spine
{
///Index of parent spine. -1 if no parent.
int m_parentSpineIndex;
///Index of the point within the parent spine array of segments.
///-1 if no parent.
int m_parentPointIndex;
///Array of segments.
AZStd::vector<SpinePoint> m_points;
};
typedef void* SpineTreeIDType;
///SpineTree is an archetype. This is basically the AzFramework version
///of CStatObj.SSpine.
struct SpineTree
{
///Unique Identifier Of this SpineTree.
SpineTreeIDType m_spineTreeId;
///A SpineTree ALWAYS contains at least one spine.
AZStd::vector<Spine> m_spines;
///Helper method.
size_t CalculateTotalNumberOfBones() const
{
size_t numberOfBones = 0;
for (const Spine& spine : m_spines)
{
numberOfBones += spine.m_points.size() - 1;
}
return numberOfBones;
}
};
///The Engine side of Touch bending uses this as an opaque handle.
///Only the TouchBending Gem knows what's inside.
///This handle corresponds one-to-one with a unique Vegetation Render Node instance.
struct TouchBendingTriggerHandle;
///The Engine side of Touch bending uses this as an opaque handle.
///Only the TouchBending Gem knows what's inside.
///This handle corresponds one-to-one with a unique CStatObjFoliage instance.
struct TouchBendingSkeletonHandle;
///Used by TouchBending Gem to talk back with the Engine.
class ITouchBendingCallback
{
public:
ITouchBendingCallback() = default;
virtual ~ITouchBendingCallback() = default;
/** @brief Checks if a render node is within e_CullVegActivation radius from the camera
*
* @param privateData Pointer to the Render Node inside the Engine that represents
* the touch bendable entity. From the point of view of the TouchBending Gem this
* is an opaque pointer, but from the point of view of the engine this is a
* CVegetation render node.
* @returns Returns a non-zero SpineTreeIDType if the Render Node is within
* e_CullVegActivation radius from the center of the main camera.
* Otherwise returns zero.
*/
virtual SpineTreeIDType CheckDistanceToCamera(const void* privateData) = 0;
/** @brief Builds a SpineTree archetype object using its SpineTreeIDType.
*
* \p privateData is a CVegetation*
* \p spineTreeId is a CStatObj*
*
* @param privateData Pointer to the Render Node inside the Engine that represents
* the touch bendable entity.
* @param spineTreeId Spine Tree Archetype Identifier as given previously by the Engine.
* @param spineTreeOut Output SpineTree archetype object.
* @returns TRUE if such \p spineTreeId is valid and a SpineTree archetype was successfully built.
* Otherwise returns FALSE.
*/
virtual bool BuildSpineTree(const void* privateData, SpineTreeIDType spineTreeId, SpineTree& spineTreeOut) = 0;
/** TouchBending Gem calls this to notify the Engine that a unique PhysicalizedSkeleton instance was built
* on behalf of \p privateData.
*
* The Engine uses this event to build a CStatObjFoliage to keep track of active touch bendable objects.
* The Engine will keep CStatObjFoliage alive as long as it is touched or for a specific lifetime in seconds
* defined by the CVar e_FoliageBranchesTimeout.
*
* @param privateData Pointer to the Render Node inside the Engine that represents
* the touch bendable entity.
* @param skeletonHandle Opaque pointer that the CStatObjFoliage must keep a copy to. The engine
* should should use it later when calling *Skeleton*() named methods of the TouchBendingBus.
* @returns true if the CStatObjFoliage was created successfully. It may return false only for cases where the CStatObj
* was removed and CStatObjFoliage can only be created if CStatObj is not null.
*/
virtual bool OnPhysicalizedTouchBendingSkeleton(const void* privateData, TouchBendingSkeletonHandle* skeletonHandle) = 0;
}; //class ITouchBendingCallback
//Exact same memory format as QuatTS
//CStatObjFoliage::ComputeSkinningTransformations() uses:
//QuatTS.q[x,y,z] as TOP joint position.
//QuatTS.t[x,y,z] as BOTTOM joint position.
//QuatTS.s CStatObjFoliage::GetSkinningData() reads this value for the first bone of each spine
// as marker for valid data, if less than zero, the spine is skipped by the Skinning code.
// A bone has two joints, TOP and BOTTOM:
//
// _ TOP Z+ up
// | | ^
// | | |
// |*| (0,0,0) X- <------|--------> X+
// | | |
// |_| BOTTOM |
// Z-
struct JointPositions
{
float m_TopJointLocation[3]; //Equivalent to QuatTS.q.xyz (ijk)
float m_qw; //Equivalent to QuatTS.q.w
float m_BottomJointLocation[3]; //Equivalent to QuatTS.t
float m_hasNewData; //Equivalent to QuatTS.s (See description above about CStatObjFoliage::GetSkinningData()).
};
/**
* Replacement of CryPhysics Touch Bending simulation.
*/
class TouchBendingRequest
: public AZ::EBusTraits
{
public:
AZ_RTTI(TouchBendingRequest, "{4E9DE1BE-F0C7-47E7-B315-9302F62D044C}");
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
virtual ~TouchBendingRequest() = default;
/// If the EBUS implementation (aka TouchBending Gem) returns TRUE
/// all of the Physics simulation for Touch Bendable CVegetation is done
/// by the TouchBending Gem with PhysX. If it returns FALSE the engine
/// will default to CryPhysics.
virtual bool IsTouchBendingEnabled() const = 0;
/** @brief Creates a TouchBending Trigger with a simple trigger box.
*
* Initially a TouchBending Trigger is nothing more than a trigger volume. There's
* no skeleton, etc. It will serve as the trigger, that when touched, builds a unique physicalized
* skeleton with the same amount of bones as a SpineTree. Recall that SpineTree is an archetype.
* The TouchBending Gem Builds a TouchBendingSkeletonHandle based on a SpineTree when TouchBendingTriggerHandle is touched.
*
* When the User adds an object via the Vegetation panel of the "Terrain Tool" UI
* this method will be called by the engine.
*
* If the User has enabled the Dynamic Vegetation Gem this method can be called
* at runtime as CVegetation nodes appear within the Camera Frustum.
*
* @param worldTransform This transform includes the scale factor. It is the position of the root
* of the CVegetation node.
* @param worldAabb Axis Aligned Bounding Box in world coordinates of the CVegetation node.
* @param callback The engine gives this callback to the TouchBending Gem for further communication.
* @param callbackPrivateData The Engine gives this opaque handle to TouchBending Gem so the Gem it can properly address it
* address the right Render Node instance when using the \p callback.
* @returns An opaque handle of a TouchBending Trigger Instance created by the TouchBending Gem.
*/
virtual TouchBendingTriggerHandle* CreateTouchBendingTrigger(const AZ::Transform& worldTransform,
const AZ::Aabb&worldAabb, ITouchBendingCallback* callback, const void * callbackPrivateData) = 0;
/** @brief Used by the engine to notify TouchBending Gem about the visibility status of the physicalized skeleton.
*
* @param skeletonHandle Opaque pointer to the physicalized skeleton created by TouchBending Gem.
* @param isVisible if TRUE the engine finds out that the skeleton is visible. If FALSE the engine calculated
* that the skeleton is either totally outside of the Camera Frustum or its distance
* from the camera exceeds the CVAR e_CullVegActivation.
* @param skeletonBoneCountOut It is the responsibility of the TouchBending Gem to fill this out
* with the number of bones available for skinning.
* @param triggerTouchCountOut It is the responsibility of the TouchBending Gem to fill this out
* with the number of objects that are touching the touch bending trigger.
* @returns void
*/
virtual void SetTouchBendingSkeletonVisibility(Physics::TouchBendingSkeletonHandle* skeletonHandle,
bool isVisible, AZ::u32& skeletonBoneCountOut, AZ::u32& triggerTouchCountOut) = 0;
/** @brief The engine calls this when it is deleting the Render Node.
*
* When the User deletes an object via the Vegetation panel of the Rollup Bar (Legacy) UI
* this method will be called by the engine.
*
* If the User has enabled the Dynamic Vegetation Gem this method can be called
* at runtime as CVegetation nodes disappear from the Camera Frustum.
*
* @param handle Opaque handle of the TouchBending trigger instance as created by the
* TouchBending Gem.
* @returns void
*/
virtual void DeleteTouchBendingTrigger(TouchBendingTriggerHandle* handle) = 0;
/** @brief The engine calls this to destroy a physicalized skeleton.
*
* The touch bending trigger remains active.
* This means that in the future something may touch the trigger
* and the skeleton is created again.
*
* @param skeletonHandle Opaque handle of the TouchBending Skeleton as created by the
* TouchBending Gem. The skeleton will be removed from the Physics World.
* @returns
*/
virtual void DephysicalizeTouchBendingSkeleton(TouchBendingSkeletonHandle* skeletonHandle) = 0;
/** Reads the current position of the pair-of-joints per bone of the Skeleton into the \p jointPositions
* buffer.
*
* @param skeletonHandle Opaque handle of the physicalized skeleton instance as created by the
* TouchBending Gem.
* @param jointPositions Buffer where the Top and Bottom Joint positions for each bone
* is written to. Please read the documentation of "struct JointPositions" for clarification.
* @returns void
*/
virtual void ReadJointPositionsOfSkeleton(TouchBendingSkeletonHandle* skeletonHandle, JointPositions* jointPositions) = 0;
};
using TouchBendingBus = AZ::EBus<TouchBendingRequest>;
/// A helper method to test if there's a Gem implementing the TouchBendingBus.
AZ_INLINE bool IsTouchBendingEnabled()
{
bool isEnabled = false;
TouchBendingBus::BroadcastResult(isEnabled, &TouchBendingBus::Events::IsTouchBendingEnabled);
return isEnabled;
}
}
@@ -17,7 +17,7 @@
namespace AzFramework
{
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformXenia, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
const char* PlatformIdToPalFolder(AzFramework::PlatformId platform)
{
@@ -35,8 +35,6 @@ namespace AzFramework
return "iOS";
case AzFramework::OSX:
return "Mac";
case AzFramework::XENIA:
return "Xenia";
case AzFramework::PROVO:
return "Provo";
case AzFramework::SALEM:
@@ -84,10 +82,6 @@ namespace AzFramework
{
return PlatformSalem;
}
else if (osPlatform == PlatformCodeNameXenia)
{
return PlatformCodeNameXenia;
}
else if (osPlatform == PlatformCodeNameJasper)
{
return PlatformJasper;
@@ -220,9 +214,6 @@ namespace AzFramework
case PlatformId::OSX:
platformCodes.emplace_back(PlatformCodeNameMac);
break;
case PlatformId::XENIA:
platformCodes.emplace_back(PlatformCodeNameXenia);
break;
case PlatformId::PROVO:
platformCodes.emplace_back(PlatformCodeNameProvo);
break;
@@ -28,7 +28,6 @@ namespace AzFramework
constexpr char PlatformES3[] = "es3";
constexpr char PlatformIOS[] = "ios";
constexpr char PlatformOSX[] = "osx_gl";
constexpr char PlatformXenia[] = "xenia";
constexpr char PlatformProvo[] = "provo";
constexpr char PlatformSalem[] = "salem";
constexpr char PlatformJasper[] = "jasper";
@@ -39,7 +38,6 @@ namespace AzFramework
constexpr char PlatformCodeNameAndroid[] = "Android";
constexpr char PlatformCodeNameiOS[] = "iOS";
constexpr char PlatformCodeNameMac[] = "Mac";
constexpr char PlatformCodeNameXenia[] = "Xenia";
constexpr char PlatformCodeNameProvo[] = "Provo";
constexpr char PlatformCodeNameSalem[] = "Salem";
constexpr char PlatformCodeNameJasper[] = "Jasper";
@@ -57,7 +55,6 @@ namespace AzFramework
ES3,
IOS,
OSX,
XENIA,
PROVO,
SALEM,
JASPER,
@@ -68,7 +65,7 @@ namespace AzFramework
// Add new platforms above this
NumPlatformIds
);
constexpr int NumClientPlatforms = 8;
constexpr int NumClientPlatforms = 7;
constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently
enum class PlatformFlags : AZ::u32
{
@@ -77,7 +74,6 @@ namespace AzFramework
Platform_ES3 = 1 << PlatformId::ES3,
Platform_IOS = 1 << PlatformId::IOS,
Platform_OSX = 1 << PlatformId::OSX,
Platform_XENIA = 1 << PlatformId::XENIA,
Platform_PROVO = 1 << PlatformId::PROVO,
Platform_SALEM = 1 << PlatformId::SALEM,
Platform_JASPER = 1 << PlatformId::JASPER,
@@ -89,7 +85,7 @@ namespace AzFramework
// A special platform that will always correspond to all non-server platforms, even if new ones are added
Platform_ALL_CLIENT = 1ULL << 31,
AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_XENIA | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags);
@@ -0,0 +1,224 @@
/*
* 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 <AzFramework/ProjectManager/ProjectManager.h>
#include <AzFramework/Engine/Engine.h>
#include <AzCore/IO/SystemFile.h>
namespace AzFramework
{
namespace ProjectManager
{
// Check if any project path appears to have been provided on the command line
bool HasCommandLineProjectName(const int argc, char* argv[])
{
constexpr int numOptionPrefixes = 3;
static const char* optionPrefixes[numOptionPrefixes] = { "/", "--", "-" };
constexpr int numOptionNames = 2;
static const char* optionNames[numOptionNames] = { "projectpath", R"(regset="/Amazon/AzCore/Bootstrap/sys_game_folder)" };
for (int i = 1; i < argc; ++i)
{
int thisPrefix = 0;
for (; thisPrefix < numOptionPrefixes; ++thisPrefix)
{
if (strncmp(argv[i], optionPrefixes[thisPrefix], strlen(optionPrefixes[thisPrefix])) == 0)
{
break;
}
}
// If the argument doesn't start with any of our switch start parameters, this isn't an argument giving us a project
if (thisPrefix == numOptionPrefixes)
{
continue;
}
// We compare the portion of the string after our prefix
int startIndex = strlen(optionPrefixes[thisPrefix]);
// If the whole argument was just one of the prefixes, this also isn't what we were looking for
if (startIndex == strlen(argv[i]))
{
continue;
}
int switchNum = 0;
for (; switchNum < numOptionNames; ++switchNum)
{
// Start the string comparison at startIndex for each string - after the option indicator
if (azstrnicmp(&argv[i][startIndex], optionNames[switchNum], strlen(optionNames[switchNum])) == 0)
{
int expectedOptionLength = strlen(optionNames[switchNum]) + startIndex;
// The option is what we're looking for if it had a space after it (it was the whole argument) or it has an equals next
if (strlen(argv[i]) == (expectedOptionLength) || ((strlen(argv[i]) > expectedOptionLength ) && argv[i][expectedOptionLength] == '='))
{
// We found one of the acceptable arguments
return true;
}
}
}
}
return false;
}
// Check for a project name, if not found, attempt to launch project manager and shut down
ProjectPathCheckResult CheckProjectPathProvided(const int argc, char* argv[])
{
// If we were able to locate a path to a project, we're done
if (HasProjectName(argc, argv))
{
return ProjectPathCheckResult::ProjectPathFound;
}
if (LaunchProjectManager())
{
AZ_TracePrintf("ProjectManager", "Project Manager launched successfully, requesting exit.");
return ProjectPathCheckResult::ProjectManagerLaunched;
}
AZ_Error("ProjectManager", false, "Project Manager failed to launch and no project selected!");
return ProjectPathCheckResult::ProjectManagerLaunchFailed;
}
} // ProjectManager
bool ProjectManager::HasProjectName(const int argc, char* argv[])
{
return HasCommandLineProjectName(argc, argv) || HasBootstrapProjectName();
}
// This is a transition method until all projects/tests/etc have been converted to expect and use the projectpath parameter
// If bootstrap.cfg exists and has a valid project name we should assume we're launching using it
// After that time it can be removed
bool ProjectManager::HasBootstrapProjectName(AZStd::string_view projectFolder)
{
AZ::IO::FixedMaxPath enginePath = Engine::FindEngineRoot(projectFolder);
if (enginePath.empty())
{
AZ_Warning("ProjectManager", false, "Couldn't find engine root");
return false;
}
auto bootstrapPath = enginePath / "bootstrap.cfg";
if (!AZ::IO::SystemFile::Exists(bootstrapPath.c_str()))
{
AZ_Warning("ProjectManager", false, "No bootstrap file found at %s", bootstrapPath.c_str());
return false;
}
AZStd::fixed_string< MaxBootstrapFileSize> bootstrapString;
auto fileSize = AZ::IO::SystemFile::Length(bootstrapPath.c_str());
if (fileSize >= MaxBootstrapFileSize)
{
AZ_Warning("ProjectManager", false, "Bootstrap read size at %s is %zu", bootstrapPath.c_str(), fileSize);
bootstrapString.resize_no_construct(MaxBootstrapFileSize);
}
else
{
bootstrapString.resize_no_construct(fileSize);
}
AZ::IO::SystemFile::SizeType bytesRead = AZ::IO::SystemFile::Read(bootstrapPath.c_str(), bootstrapString.data(), MaxBootstrapFileSize - 1);
if (bytesRead == (MaxBootstrapFileSize - 1))
{
AZ_Warning("ProjectManager", false, "Bootstrap read size at %s was %zu", bootstrapPath.c_str(), bytesRead);
}
if (!ContentHasProjectName(bootstrapString))
{
AZ_TracePrintf("ProjectManager", "Bootstrap at %s did not contain project name", bootstrapPath.c_str());
return false;
}
return true;
}
// This is a transition method until all projects/tests/etc have been converted to expect and use the projectpath parameter
// If bootstrap.cfg exists and has a valid project name we should assume we're launching using it
// After that time it can be removed
bool ProjectManager::ContentHasProjectName(AZStd::fixed_string< MaxBootstrapFileSize>& bootstrapString)
{
static const char* const projectKey = "sys_game_folder";
size_t searchStart = bootstrapString.find(projectKey);
while (searchStart != bootstrapString.npos)
{
// Once we've found the key we need to search the line forward and backwards. Commented out lines shouldn't count
// and if there's no value after the equals then it's also not set
auto checkPos = searchStart;
// We're at the start already if this is position 0
bool foundLineStart = checkPos == 0;
if (checkPos)
{
--checkPos;
}
while (checkPos > 0 && bootstrapString[checkPos] != '-')
{
if (bootstrapString[checkPos] == '\n')
{
// Looks like a valid key
foundLineStart = true;
break;
}
if (!std::isspace(bootstrapString[checkPos]))
{
// This appears to be some other character appearing before our key, this isn't valid
break;
}
--checkPos;
}
if (!foundLineStart)
{
// Commented line or other content preceding our key, keep searching
searchStart = bootstrapString.find(projectKey, searchStart + 1);
continue;
}
checkPos = searchStart + strlen(projectKey);
bool foundEquals = false;
while (checkPos < bootstrapString.length())
{
if (bootstrapString[checkPos] == '\n')
{
// We've reached the end of the line and didn't find anything that seems to be a value for our key
break;
}
if (std::isspace(bootstrapString[checkPos]))
{
// Whitespace - keep searching back
++checkPos;
continue;
}
if (bootstrapString[checkPos] == '=')
{
foundEquals = true;
++checkPos;
continue;
}
if (foundEquals)
{
auto nameEnd = bootstrapString.find_first_of(" \n", checkPos);
if (nameEnd == bootstrapString.npos)
{
// End of content, this is valid
nameEnd = bootstrapString.length();
}
constexpr size_t nameMax = 100;
if (nameEnd - checkPos > nameMax)
{
AZ_Warning("ProjectManager", false, "Project name exceeded %zu characters (%zu)", nameMax, nameEnd - checkPos);
return false;
}
AZStd::fixed_string<nameMax + 1> projectName(&bootstrapString[checkPos], nameEnd - checkPos);
AZ_TracePrintf("ProjectManager", "Found project name of %s", projectName.c_str());
// This is not a space, we've found our key, and we've found some sort of non space entry, we count this as "it looks like we have a value entered"
return true;
}
// there was some other content on this line after our key before the equals that was not a space, this isn't our key
searchStart = bootstrapString.find(projectKey, searchStart + 1);
break;
}
searchStart = bootstrapString.find(projectKey, searchStart + 1);
}
return false;
}
} // AzFramework
@@ -0,0 +1,43 @@
/*
* 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/IO/SystemFile.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string_view.h>
namespace AzFramework
{
namespace ProjectManager
{
constexpr AZ::IO::SystemFile::SizeType MaxBootstrapFileSize = 1024 * 10;
// Check if any project name can be found anywhere
bool HasProjectName(const int argc, char* argv[]);
// Check if any project name can be found on the command line
bool HasCommandLineProjectName(const int argc, char* argv[]);
// Check if a relative project is being used through bootstrap
bool HasBootstrapProjectName(AZStd::string_view projectFolder = {});
// Search content for project name key
bool ContentHasProjectName(AZStd::fixed_string< MaxBootstrapFileSize>& bootstrapString);
enum class ProjectPathCheckResult
{
ProjectManagerLaunchFailed = -1,
ProjectManagerLaunched = 0,
ProjectPathFound = 1
};
// Check for a project name, if not found, attempts to launch project manager and returns false
ProjectPathCheckResult CheckProjectPathProvided(const int argc, char* argv[]);
// Attempt to Launch the project manager. Requires locating the engine root, project manager script, and python.
bool LaunchProjectManager();
}
} // AzFramework
@@ -0,0 +1,74 @@
/*
* 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 <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Spawnable/Spawnable.h>
namespace AzFramework
{
Spawnable::Spawnable(const AZ::Data::AssetId& id)
: AZ::Data::AssetData(id)
{
}
Spawnable::Spawnable(Spawnable&& other)
: m_entities(AZStd::move(other.m_entities))
{
}
Spawnable& Spawnable::operator=(Spawnable&& other)
{
if (this != &other)
{
m_entities = AZStd::move(other.m_entities);
}
return *this;
}
const Spawnable::EntityList& Spawnable::GetEntities() const
{
return m_entities;
}
Spawnable::EntityList& Spawnable::GetEntities()
{
return m_entities;
}
bool Spawnable::IsEmpty() const
{
return m_entities.empty();
}
SpawnableMetaData& Spawnable::GetMetaData()
{
return m_metaData;
}
const SpawnableMetaData& Spawnable::GetMetaData() const
{
return m_metaData;
}
void Spawnable::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<Spawnable, AZ::Data::AssetData>()->Version(1)
->Field("Meta data", &Spawnable::m_metaData)
->Field("Entities", &Spawnable::m_entities);
}
}
} // namespace AzFramework
@@ -0,0 +1,65 @@
/*
* 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/Asset/AssetCommon.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Spawnable/SpawnableMetaData.h>
namespace AzFramework
{
class ReflectContext;
class Spawnable final
: public AZ::Data::AssetData
{
public:
AZ_CLASS_ALLOCATOR(Spawnable, AZ::SystemAllocator, 0);
AZ_RTTI(AzFramework::Spawnable, "{855E3021-D305-4845-B284-20C3F7FDF16B}", AZ::Data::AssetData);
using EntityList = AZStd::vector<AZStd::unique_ptr<AZ::Entity>>;
inline static constexpr const char* FileExtension = "spawnable";
Spawnable() = default;
explicit Spawnable(const AZ::Data::AssetId& id);
Spawnable(const Spawnable& rhs) = delete;
Spawnable(Spawnable&& other);
~Spawnable() override = default;
Spawnable& operator=(const Spawnable& rhs) = delete;
Spawnable& operator=(Spawnable&& other);
const EntityList& GetEntities() const;
EntityList& GetEntities();
bool IsEmpty() const;
SpawnableMetaData& GetMetaData();
const SpawnableMetaData& GetMetaData() const;
static void Reflect(AZ::ReflectContext* context);
private:
SpawnableMetaData m_metaData;
// Container for keeping all entities of the prefab the Spawnable was created from.
// Includes both direct and nested entities of the prefab.
EntityList m_entities;
};
using SpawnableList = AZStd::vector<Spawnable>;
} // namespace AzFramework
@@ -0,0 +1,91 @@
/*
* 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 <AzCore/Serialization/Utils.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
#include <AzFramework/Spawnable/Spawnable.h>
namespace AzFramework
{
SpawnableAssetHandler::SpawnableAssetHandler()
{
AZ::AssetTypeInfoBus::MultiHandler::BusConnect(AZ::AzTypeInfo<Spawnable>::Uuid());
}
SpawnableAssetHandler::~SpawnableAssetHandler()
{
AZ::AssetTypeInfoBus::MultiHandler::BusDisconnect();
}
AZ::Data::AssetPtr SpawnableAssetHandler::CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type)
{
AZ_Assert(type == AZ::AzTypeInfo<Spawnable>::Uuid(),
"Asset handler for Spawnable was given a type that's not a Spawnable: %s", type.ToString<AZStd::string>().c_str());
return aznew Spawnable(id);
}
void SpawnableAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr)
{
delete ptr;
}
void SpawnableAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes)
{
assetTypes.push_back(AZ::AzTypeInfo<Spawnable>::Uuid());
}
auto SpawnableAssetHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB) -> LoadResult
{
Spawnable* spawnable = asset.GetAs<Spawnable>();
AZ_Assert(spawnable, "Loaded asset data handed to the SpawnableAssetHandler didn't contain a Spawanble.");
AZ::ObjectStream::FilterDescriptor filter(assetLoadFilterCB);
if (AZ::Utils::LoadObjectFromStreamInPlace(*stream, *spawnable, nullptr /*SerializeContext*/, filter))
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
else
{
AZ_Error("Spawnable", false, "Failed to deserialize asset %s.", asset->GetId().ToString<AZStd::string>().c_str());
return AZ::Data::AssetHandler::LoadResult::Error;
}
}
AZ::Data::AssetType SpawnableAssetHandler::GetAssetType() const
{
return AZ::AzTypeInfo<Spawnable>::Uuid();
}
const char* SpawnableAssetHandler::GetAssetTypeDisplayName() const
{
return "Spawnable";
}
const char* SpawnableAssetHandler::GetGroup() const
{
return "Prefab";
}
const char* SpawnableAssetHandler::GetBrowserIcon() const
{
return "Editor/Icons/Components/Viewport/EntityInSlice.png";
}
void SpawnableAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
{
extensions.push_back(Spawnable::FileExtension);
}
} // namespace AzFramework
@@ -0,0 +1,57 @@
/*
* 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/Asset/AssetManager.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace AzFramework
{
class SpawnableAssetHandler final
: public AZ::Data::AssetHandler
, public AZ::AssetTypeInfoBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(SpawnableAssetHandler, AZ::SystemAllocator, 0);
AZ_RTTI(AZ::SpawnableAssetHandler, "{BF6E2D17-87C9-4BB1-A205-3656CF6D551D}", AZ::Data::AssetHandler);
SpawnableAssetHandler();
~SpawnableAssetHandler() override;
//
// AssetHandler
//
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override;
void DestroyAsset(AZ::Data::AssetPtr ptr) override;
void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override;
//
// AssetTypeInfoBus
//
AZ::Data::AssetType GetAssetType() const override;
const char* GetAssetTypeDisplayName() const override;
const char* GetGroup() const override;
const char* GetBrowserIcon() const override;
void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
protected:
LoadResult LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
};
} // namespace AzFramework
@@ -0,0 +1,220 @@
/*
* 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 <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/ObjectStream.h> // Needed for ObjectStreamWriteOverrideCB and its AZ_TYPE_INFO_SPECIALIZE
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/typetraits/typetraits.h>
#include <AzFramework/Spawnable/SpawnableMetaData.h>
namespace AzFramework
{
SpawnableMetaData::SpawnableMetaData(Table table)
{
AZ_Assert(AZStd::is_sorted(table.begin(), table.end(),
[](const auto& lhs, const auto& rhs)
{
return lhs.first < rhs.first;
}), "The key/value table provided to SpawnableMetaData needs to be sorted by key.");
m_keys.reserve(table.size());
m_values.reserve(table.size());
for (auto&& entry : table)
{
m_keys.push_back(entry.first);
m_values.push_back(AZStd::move(entry.second));
}
}
bool SpawnableMetaData::Get(AZStd::string_view key, bool& value) const
{
return GetGeneric(GetKeyHash(key), value);
}
bool SpawnableMetaData::Get(AZStd::string_view key, uint64_t& value) const
{
return GetGeneric(GetKeyHash(key), value);
}
bool SpawnableMetaData::Get(AZStd::string_view key, int64_t& value) const
{
return GetGeneric(GetKeyHash(key), value);
}
bool SpawnableMetaData::Get(AZStd::string_view key, double& value) const
{
return GetGeneric(GetKeyHash(key), value);
}
bool SpawnableMetaData::Get(AZStd::string_view key, AZStd::string_view& value) const
{
return GetGeneric(GetKeyHash(key), value);
}
bool SpawnableMetaData::Get(AZStd::string_view key, SpawnableMetaDataArraySize& value) const
{
return GetGeneric(GetKeyHash(key), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, uint64_t index, bool& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, uint64_t index, uint64_t& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, uint64_t index, int64_t& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, uint64_t index, double& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, uint64_t index, AZStd::string_view& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, bool& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, uint64_t& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, int64_t& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, double& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
bool SpawnableMetaData::Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, AZStd::string_view& value) const
{
return GetGeneric(GetKeyHash(arrayKey, index), value);
}
auto SpawnableMetaData::GetType(AZStd::string_view key) const -> ValueType
{
return GetTypeGeneric(GetKeyHash(key));
}
auto SpawnableMetaData::GetType(AZStd::string_view arrayKey, uint64_t index) const -> ValueType
{
return GetTypeGeneric(GetKeyHash(arrayKey, index));
}
auto SpawnableMetaData::GetType(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index) const -> ValueType
{
return GetTypeGeneric(GetKeyHash(arrayKey, index));
}
void SpawnableMetaData::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<SpawnableMetaData>()->Version(1)
->Field("Keys", &SpawnableMetaData::m_keys)
->Field("Values", &SpawnableMetaData::m_values);
}
}
auto SpawnableMetaData::GetKeyHash(AZStd::string_view key) const -> TableKey
{
return AZ::TypeHash64(reinterpret_cast<const uint8_t*>(key.data()), aznumeric_cast<uint64_t>(key.length()));
}
auto SpawnableMetaData::GetKeyHash(AZStd::string_view arrayKey, uint64_t index) const -> TableKey
{
return AZ::TypeHash64(
reinterpret_cast<const uint8_t*>(arrayKey.data()), aznumeric_cast<uint64_t>(arrayKey.length()),
aznumeric_caster(ArrayKeyRoot + index));
}
auto SpawnableMetaData::GetKeyHash(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index) const -> TableKey
{
return GetKeyHash(arrayKey, aznumeric_cast<uint64_t>(index));
}
template<typename T>
bool SpawnableMetaData::GetGeneric(AZ::HashValue64 key, T& value) const
{
auto it = AZStd::lower_bound(m_keys.begin(), m_keys.end(), key);
if (it != m_keys.end() && *it == key)
{
size_t index = AZStd::distance(m_keys.begin(), it);
if constexpr (AZStd::is_same_v<T, AZStd::string_view>)
{
if (const AZStd::string* storedValue = AZStd::get_if<AZStd::string>(&m_values[index]); storedValue != nullptr)
{
value = *storedValue;
return true;
}
}
else
{
if (const T* storedValue = AZStd::get_if<T>(&m_values[index]); storedValue != nullptr)
{
value = *storedValue;
return true;
}
}
}
return false;
}
auto SpawnableMetaData::GetTypeGeneric(AZ::HashValue64 key) const -> ValueType
{
auto it = AZStd::lower_bound(m_keys.begin(), m_keys.end(), key);
if (it != m_keys.end() && *it == key)
{
size_t index = AZStd::distance(m_keys.begin(), it);
return AZStd::visit([](auto&& args) -> ValueType
{
using Key = AZStd::decay_t<decltype(args)>;
if constexpr (AZStd::is_same_v<Key, bool>) { return ValueType::Boolean; }
else if constexpr (AZStd::is_same_v<Key, uint64_t>) { return ValueType::UnsignedInteger; }
else if constexpr (AZStd::is_same_v<Key, int64_t>) { return ValueType::SignedInteger; }
else if constexpr (AZStd::is_same_v<Key, double>) { return ValueType::FloatingPoint; }
else if constexpr (AZStd::is_same_v<Key, AZStd::string>){ return ValueType::String; }
else if constexpr (AZStd::is_same_v<Key, SpawnableMetaDataArraySize>)
{
return ValueType::ArraySize;
}
else
{
return ValueType::Unavailable;
}
}, m_values[index]);
}
else
{
return ValueType::Unavailable;
}
}
}; // namespace AzFramework
@@ -0,0 +1,102 @@
/*
* 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/RTTI/TypeSafeIntegral.h>
#include <AzCore/Utils/TypeHash.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/utils.h>
namespace AZ
{
class ReflectContext;
}
namespace AzFramework
{
AZ_TYPE_SAFE_INTEGRAL(SpawnableMetaDataArraySize, uint64_t);
using SpawnableMetaDataArrayIndex = SpawnableMetaDataArraySize;
//! Simple meta data that can be stored along with a spawnable.
//! This class is designed to be read-only and is expected to be serialized. AzToolsFramework provides a class for
//! constructing meta data.
class SpawnableMetaData final
{
public:
static inline constexpr uint64_t ArrayKeyRoot = 146223222818353; // Random prime number
using TableKey = AZ::HashValue64;
using TableValue = AZStd::variant<bool, uint64_t, int64_t, double, AZStd::string, SpawnableMetaDataArraySize>;
using TableEntry = AZStd::pair<TableKey, TableValue>;
using Table = AZStd::vector<TableEntry>;
enum class ValueType
{
Unavailable,
Boolean,
UnsignedInteger,
SignedInteger,
FloatingPoint,
String,
ArraySize
};
AZ_TYPE_INFO(AZ::SpawnableMetaData, "{3832FA08-B10B-49AF-A81E-8E2FC1FF98B1}");
SpawnableMetaData() = default;
// This constructor exists only so externally constructed tables can be provided by tools.
explicit SpawnableMetaData(Table table);
bool Get(AZStd::string_view key, bool& value) const;
bool Get(AZStd::string_view key, uint64_t& value) const;
bool Get(AZStd::string_view key, int64_t& value) const;
bool Get(AZStd::string_view key, double& value) const;
bool Get(AZStd::string_view key, AZStd::string_view& value) const;
bool Get(AZStd::string_view key, SpawnableMetaDataArraySize& value) const;
bool Get(AZStd::string_view arrayKey, uint64_t index, bool& value) const;
bool Get(AZStd::string_view arrayKey, uint64_t index, uint64_t& value) const;
bool Get(AZStd::string_view arrayKey, uint64_t index, int64_t& value) const;
bool Get(AZStd::string_view arrayKey, uint64_t index, double& value) const;
bool Get(AZStd::string_view arrayKey, uint64_t index, AZStd::string_view& value) const;
bool Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, bool& value) const;
bool Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, uint64_t& value) const;
bool Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, int64_t& value) const;
bool Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, double& value) const;
bool Get(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index, AZStd::string_view& value) const;
ValueType GetType(AZStd::string_view key) const;
ValueType GetType(AZStd::string_view arrayKey, uint64_t index) const;
ValueType GetType(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index) const;
static void Reflect(AZ::ReflectContext* context);
private:
TableKey GetKeyHash(AZStd::string_view key) const;
TableKey GetKeyHash(AZStd::string_view arrayKey, uint64_t index) const;
TableKey GetKeyHash(AZStd::string_view arrayKey, SpawnableMetaDataArrayIndex index) const;
template<typename T>
bool GetGeneric(AZ::HashValue64 key, T& value) const;
ValueType GetTypeGeneric(AZ::HashValue64 key) const;
AZStd::vector<TableKey> m_keys;
AZStd::vector<TableValue> m_values;
};
} // namespace AzFramework
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(AzFramework::SpawnableMetaDataArraySize);
@@ -0,0 +1,95 @@
/*
* 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 <AzCore/Asset/AssetManager.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Spawnable/SpawnableMetaData.h>
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
namespace AzFramework
{
void SpawnableSystemComponent::Reflect(AZ::ReflectContext* context)
{
Spawnable::Reflect(context);
SpawnableMetaData::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->RegisterGenericType<AZ::Data::Asset<Spawnable>>();
}
}
void SpawnableSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC_CE("SpawnableSystemService"));
}
void SpawnableSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC_CE("SpawnableSystemService"));
}
void SpawnableSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC_CE("AssetDatabaseService"));
services.push_back(AZ_CRC_CE("AssetCatalogService"));
}
void SpawnableSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
if (!m_rootSpawnableInitialized)
{
auto registry = AZ::SettingsRegistry::Get();
AZ_Assert(registry, "Unable to check for root spawnable because the Settings Registry is not available.");
if (registry->GetObject(m_rootSpawnable, RootSpawnableRegistryKey) && m_rootSpawnable.GetId().IsValid())
{
AZ_TracePrintf("Spawnables", "Root spawnable '%s' used.\n", m_rootSpawnable.GetHint().c_str());
if (!m_rootSpawnable.QueueLoad())
{
AZ_Error("Spawnables", false, "Unable to queue root spawnable for loading.\n");
}
}
else
{
AZ_Warning("Spawnables", false, "No root spawnable assigned or root spawanble couldnt' be loaded.\n"
"The root spawnable can be assigned in the Settings Registry under the key '$s'.\n", RootSpawnableRegistryKey);
}
m_rootSpawnableInitialized = true;
}
}
void SpawnableSystemComponent::Activate()
{
// Register with AssetDatabase
AZ_Assert(AZ::Data::AssetManager::IsReady(), "Spawnables can't be registered because the Asset Manager is not ready yet.");
AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, AZ::AzTypeInfo<Spawnable>::Uuid());
// Register with AssetCatalog
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::EnableCatalogForAsset, AZ::AzTypeInfo<Spawnable>::Uuid());
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::AddExtension, Spawnable::FileExtension);
AssetCatalogEventBus::Handler::BusConnect();
}
void SpawnableSystemComponent::Deactivate()
{
AssetCatalogEventBus::Handler::BusDisconnect();
AZ_Assert(AZ::Data::AssetManager::IsReady(),
"Spawnables can't be unregistered because the Asset Manager has been destroyed already or never started.");
AZ::Data::AssetManager::Instance().UnregisterHandler(&m_assetHandler);
}
} // namespace AzFramework
@@ -0,0 +1,57 @@
/*
* 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/Component/Component.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
namespace AzFramework
{
class SpawnableSystemComponent
: public AZ::Component
, public AssetCatalogEventBus::Handler
{
public:
AZ_COMPONENT(SpawnableSystemComponent, "{12D0DA52-BB86-4AC3-8862-9493E0D0E207}");
inline static constexpr const char* RootSpawnableRegistryKey = "/Amazon/AzCore/Bootstrap/RootSpawnable";
SpawnableSystemComponent() = default;
SpawnableSystemComponent(const SpawnableSystemComponent&) = delete;
SpawnableSystemComponent(SpawnableSystemComponent&&) = delete;
SpawnableSystemComponent& operator=(const SpawnableSystemComponent&) = delete;
SpawnableSystemComponent& operator=(SpawnableSystemComponent&&) = delete;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services);
//
// AssetCatalogEventBus
//
void OnCatalogLoaded(const char* catalogFile) override;
protected:
void Activate() override;
void Deactivate() override;
SpawnableAssetHandler m_assetHandler;
AZ::Data::Asset<Spawnable> m_rootSpawnable;
bool m_rootSpawnableInitialized{ false };
};
} // namespace AzFramework
@@ -12,7 +12,7 @@
#pragma once
#include <AzFramework/Viewport/Viewport.h>
#include <AzFramework/Viewport/ViewportControllerInterface.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/containers/unordered_map.h>
@@ -23,7 +23,12 @@ namespace AzFramework
//! Subclasses of MultiViewportController will be provided with one instance of TViewportControllerInstance
//! per registered viewport, where TViewportControllerInstance must implement the interface of
//! MultiViewportControllerInstance and provide a TViewportControllerInstance(ViewportId) constructor.
template <class TViewportControllerInstance>
//! @param TViewportControllerInstance is the instance type of the controller,
//! one shall be instantiated per registered viewport. This child should conform to the MultiViewportControllerInstanceInterface
//! @param Priority is the priority at which this controller should be dispatched events.
//! Input events may not be received if a higher prioririty controller consumes the event.
//! To receive events at all priorities, DispatchToAllPriorities may be specified.
template <class TViewportControllerInstance, ViewportControllerPriority Priority = ViewportControllerPriority::Normal>
class MultiViewportController
: public ViewportControllerInterface
{
@@ -31,10 +36,11 @@ namespace AzFramework
~MultiViewportController() override;
// ViewportControllerInterface ...
bool HandleInputChannelEvent(ViewportId viewport, const AzFramework::InputChannel& inputChannel) override;
void UpdateViewport(ViewportId viewport, FloatSeconds deltaTime, AZ::ScriptTimePoint time) override;
bool HandleInputChannelEvent(const ViewportControllerInputEvent& event) override;
void UpdateViewport(const ViewportControllerUpdateEvent& event) override;
void RegisterViewportContext(ViewportId viewport) override;
void UnregisterViewportContext(ViewportId viewport) override;
ViewportControllerPriority GetPriority() const override;
private:
AZStd::unordered_map<ViewportId, AZStd::unique_ptr<TViewportControllerInstance>> m_instances;
@@ -45,14 +51,14 @@ namespace AzFramework
{
public:
explicit MultiViewportControllerInstanceInterface(ViewportId viewport)
: m_viewport(viewport)
: m_viewportId(viewport)
{
}
ViewportId GetViewportId() const { return m_viewportId; }
virtual bool HandleInputChannelEvent([[maybe_unused]]const AzFramework::InputChannel& inputChannel) { return false; }
virtual void UpdateViewport([[maybe_unused]]FloatSeconds deltaTime, [[maybe_unused]]AZ::ScriptTimePoint time) {}
virtual bool HandleInputChannelEvent([[maybe_unused]]const ViewportControllerInputEvent& event) { return false; }
virtual void UpdateViewport([[maybe_unused]]const ViewportControllerUpdateEvent& event) {}
private:
ViewportId m_viewportId;
@@ -13,8 +13,8 @@
namespace AzFramework
{
template <class TViewportControllerInstance>
MultiViewportController<TViewportControllerInstance>::~MultiViewportController()
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
MultiViewportController<TViewportControllerInstance, Priority>::~MultiViewportController()
{
static_assert(
AZStd::is_constructible<TViewportControllerInstance, ViewportId>::value,
@@ -22,31 +22,37 @@ namespace AzFramework
);
}
template <class TViewportControllerInstance>
bool MultiViewportController<TViewportControllerInstance>::HandleInputChannelEvent(ViewportId viewport, const AzFramework::InputChannel& inputChannel)
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
bool MultiViewportController<TViewportControllerInstance, Priority>::HandleInputChannelEvent(const ViewportControllerInputEvent& event)
{
auto instanceIt = m_instances.find(viewport);
auto instanceIt = m_instances.find(event.m_viewportId);
AZ_Assert(instanceIt != m_instances.end(), "Attempted to call HandleInputChannelEvent on an unregistered viewport");
return instanceIt->second->HandleInputChannelEvent(inputChannel);
return instanceIt->second->HandleInputChannelEvent(event);
}
template <class TViewportControllerInstance>
void MultiViewportController<TViewportControllerInstance>::UpdateViewport(ViewportId viewport, FloatSeconds deltaTime, AZ::ScriptTimePoint time)
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
void MultiViewportController<TViewportControllerInstance, Priority>::UpdateViewport(const ViewportControllerUpdateEvent& event)
{
auto instanceIt = m_instances.find(viewport);
auto instanceIt = m_instances.find(event.m_viewportId);
AZ_Assert(instanceIt != m_instances.end(), "Attempted to call UpdateViewport on an unregistered viewport");
instanceIt->second->UpdateViewport(deltaTime, time);
instanceIt->second->UpdateViewport(event);
}
template <class TViewportControllerInstance>
void MultiViewportController<TViewportControllerInstance>::RegisterViewportContext(ViewportId viewport)
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
void MultiViewportController<TViewportControllerInstance, Priority>::RegisterViewportContext(ViewportId viewport)
{
m_instances[viewport] = AZStd::make_unique<TViewportControllerInstance>(viewport);
}
template <class TViewportControllerInstance>
void MultiViewportController<TViewportControllerInstance>::UnregisterViewportContext(ViewportId viewport)
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
void MultiViewportController<TViewportControllerInstance, Priority>::UnregisterViewportContext(ViewportId viewport)
{
m_instances.erase(viewport);
}
template <class TViewportControllerInstance, ViewportControllerPriority Priority>
ViewportControllerPriority MultiViewportController<TViewportControllerInstance, Priority>::GetPriority() const
{
return Priority;
}
} //namespace AzFramework
@@ -32,6 +32,76 @@ namespace AzFramework
using FloatSeconds = AZStd::chrono::duration<float>;
//! Controller Priority determines when controllers receive input and update events.
//! Controller Priority is provided by the list containing a viewport controller.
//! Input channel events are received from highest to lowest priority order,
//! allowing high priority controllers to consume input events and stop their propagation.
//! Viewport update events are received from lowest to highest priority order,
//! allowing high priority controllers to be the last to update the viewport state.
//!
//! Controller lists may receive DispatchToAllPriorities events, which will in turn
//! dispatch events to all of their children in priority order.
//!
//! @see AzFramework::ViewportControllerList
//! @note Because of this behavior, a ViewportControllerList that belongs to another
//! ViewportControllerList shall not receive a DispatchToAllPriorities event, and instead
//! shall receive multiple events from its parent at all priority levels.
enum class ViewportControllerPriority : uint8_t {
Highest = 0,
High,
Normal,
Low,
Lowest,
DispatchToAllPriorities
};
//! An event dispatched to ViewportControllers when input occurs.
struct ViewportControllerInputEvent
{
//! The viewport ID this event was dispatched to.
ViewportId m_viewportId;
//! 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
)
: m_viewportId(viewportId)
, m_inputChannel(inputChannel)
, m_priority(priority)
{
}
};
//! An event dispatched to ViewportControllers every tick.
struct ViewportControllerUpdateEvent
{
//! The viewport ID this event was dispatched to.
ViewportId m_viewportId;
//! The time since the last update event, in seconds.
FloatSeconds m_deltaTime;
//! The absolute time point of this event.
AZ::ScriptTimePoint m_time;
//! The priority this event was dispatched at.
ViewportControllerPriority m_priority;
ViewportControllerUpdateEvent(
ViewportId viewportId, FloatSeconds deltaTime,
AZ::ScriptTimePoint time,
ViewportControllerPriority priority = ViewportControllerPriority::DispatchToAllPriorities
)
: m_viewportId(viewportId)
, m_deltaTime(deltaTime)
, m_time(time)
, m_priority(priority)
{
}
};
//! The interface for a Viewport Controller which handles input events and periodic updates for one or more registered Viewports.
//! @see SingleViewportController for simple cases involving only one viewport.
//! @see MultiViewportController for cases involving multiple viewports with no shared state.
@@ -43,16 +113,25 @@ namespace AzFramework
//! Handles an input event dispatched to a given viewportContext.
//! @return A "handled" flag. If OnInputChannelEvent returns true, the event is considered handled and all further input handling
//! in the containing ViewportControllerList halts.
virtual bool HandleInputChannelEvent([[maybe_unused]]ViewportId viewport, [[maybe_unused]]const AzFramework::InputChannel& inputChannel) { return false; }
virtual bool HandleInputChannelEvent([[maybe_unused]]const ViewportControllerInputEvent& event) { return false; }
//! Called to notify this controller that its input state should be reset.
//! This is called when input events, such as key up events, may have been missed and it should be assumed that
//! all input channels are in their default (i.e. no buttons pressed or other input provided) state.
virtual void ResetInputChannels(){}
//! Updates the current state of the viewport. This should be used to update e.g. the camera transform and will be called every frame
//! for each registered viewport.
virtual void UpdateViewport([[maybe_unused]]ViewportId viewport, [[maybe_unused]]FloatSeconds deltaTime, [[maybe_unused]]AZ::ScriptTimePoint time) {}
virtual void UpdateViewport([[maybe_unused]]const ViewportControllerUpdateEvent& event) {}
//! Registers a ViewportContext to be handled by this controller.
//! The controller will receive OnInputChannelEvent and OnUpdateViewport notifications for the viewports.
virtual void RegisterViewportContext(ViewportId viewport) = 0;
//! Unregisters a viewport from being handled by this controller.
//! No further events will be received from this viewport after this is called.
virtual void UnregisterViewportContext(ViewportId viewport) = 0;
//! Gets the priority at which this controller will receive input events.
//! If set to DispatchToAllPriorities, the controller will receive events multiple times for each
//! available priority level. This typically is only needed in the case of a list of other viewport
//! controllers, each with their own priority (handled by ViewportControllerList for most cases).
virtual ViewportControllerPriority GetPriority() const { return ViewportControllerPriority::Normal; }
};
} //namespace AzFramework
@@ -16,100 +16,162 @@
namespace AzFramework
{
void ViewportControllerList::Add(ViewportControllerPtr controller, ViewportControllerList::Priority priority)
void ViewportControllerList::Add(ViewportControllerPtr controller)
{
auto controllerIt = AZStd::find_if(
m_controllers.begin(), m_controllers.end(),
[controller](const ViewportControllerData& data)
for (auto &controllerData : m_controllers)
{
return data.controller == controller;
});
if (controllerIt != m_controllers.end())
{
AZ_Assert(false, "Attempted to add a duplicate controller to a ViewportControllerList");
return;
auto& controllerList = controllerData.second;
auto controllerIt = AZStd::find(
controllerList.begin(), controllerList.end(),
controller
);
if (controllerIt != controllerList.end())
{
AZ_Assert(false, "Attempted to add a duplicate controller to a ViewportControllerList");
return;
}
}
m_controllers.push_back({controller, priority});
m_controllers[controller->GetPriority()].push_back(controller);
for (auto viewportId : m_viewports)
{
controller->RegisterViewportContext(viewportId);
}
SortControllers();
}
void ViewportControllerList::Remove(ViewportControllerPtr controller)
{
m_controllers.erase(AZStd::remove_if(
m_controllers.begin(), m_controllers.end(),
[controller](const ViewportControllerData& data)
for (auto &controllerData : m_controllers)
{
return data.controller == controller;
}));
}
void ViewportControllerList::SetPriority(ViewportControllerPtr controller, ViewportControllerList::Priority priority)
{
auto controllerIt = AZStd::find_if(
m_controllers.begin(), m_controllers.end(),
[controller](const ViewportControllerData& data)
{
return data.controller == controller;
});
if (controllerIt != m_controllers.end())
{
controllerIt->priority = priority;
auto& controllerList = controllerData.second;
controllerList.erase(AZStd::remove(controllerList.begin(), controllerList.end(), controller));
}
SortControllers();
}
bool ViewportControllerList::HandleInputChannelEvent(ViewportId viewport, const AzFramework::InputChannel& inputChannel)
bool ViewportControllerList::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
{
// Iterate in forward order, so that the lowest priority values get the first opportunity to consume the event
for (const auto& controllerInfo : m_controllers)
// If our event priority is "custom", we should dispatch at all priority levels in order
using AzFramework::ViewportControllerPriority;
if (event.m_priority == AzFramework::ViewportControllerPriority::DispatchToAllPriorities)
{
if (controllerInfo.controller->HandleInputChannelEvent(viewport, inputChannel))
AzFramework::ViewportControllerInputEvent syntheticEvent = event;
for (const auto priority : {
ViewportControllerPriority::Highest,
ViewportControllerPriority::High,
ViewportControllerPriority::Normal,
ViewportControllerPriority::Low,
ViewportControllerPriority::Lowest })
{
return true;
syntheticEvent.m_priority = priority;
if (DispatchInputChannelEvent(syntheticEvent))
{
return true;
}
}
return false;
}
else
{
// Otherwise, dispatch to controllers at our priority
return DispatchInputChannelEvent(event);
}
}
bool ViewportControllerList::DispatchInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
{
if (auto priorityListIt = m_controllers.find(event.m_priority); priorityListIt != m_controllers.end())
{
for (const auto& controller : priorityListIt->second)
{
if (controller->HandleInputChannelEvent(event))
{
return true;
}
}
}
// Also dispatch to any nested controllers with "Custom" priority
if (auto priorityListIt = m_controllers.find(ViewportControllerPriority::DispatchToAllPriorities);
priorityListIt != m_controllers.end())
{
for (const auto& controller : priorityListIt->second)
{
if (controller->HandleInputChannelEvent(event))
{
return true;
}
}
}
return false;
}
void ViewportControllerList::UpdateViewport(ViewportId viewport, FloatSeconds deltaTime, AZ::ScriptTimePoint time)
void ViewportControllerList::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
{
// Iterate in reverse order, so that the lowest priority values go last
// This lets authoritative state changes in controllers with priority "win"
for (auto controllerIt = m_controllers.rbegin(), end = m_controllers.rend(); controllerIt != end; ++controllerIt)
// If our event priority is "custom", we should dispatch at all priority levels in reverse order
// Reverse order lets high priority controllers get the last say in viewport update operations
using AzFramework::ViewportControllerPriority;
if (event.m_priority == AzFramework::ViewportControllerPriority::DispatchToAllPriorities)
{
controllerIt->controller->UpdateViewport(viewport, deltaTime, time);
AzFramework::ViewportControllerUpdateEvent syntheticEvent = event;
for (const auto priority : {
ViewportControllerPriority::Lowest,
ViewportControllerPriority::Low,
ViewportControllerPriority::Normal,
ViewportControllerPriority::High,
ViewportControllerPriority::Highest })
{
syntheticEvent.m_priority = priority;
DispatchUpdateViewport(syntheticEvent);
}
}
else
{
// Otherwise, dispatch to controllers at our priority
DispatchUpdateViewport(event);
}
}
void ViewportControllerList::DispatchUpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
{
if (auto priorityListIt = m_controllers.find(event.m_priority); priorityListIt != m_controllers.end())
{
for (const auto& controller : priorityListIt->second)
{
controller->UpdateViewport(event);
}
}
// Also dispatch to any nested controllers with "Custom" priority
if (auto priorityListIt = m_controllers.find(ViewportControllerPriority::DispatchToAllPriorities);
priorityListIt != m_controllers.end())
{
for (const auto& controller : priorityListIt->second)
{
controller->UpdateViewport(event);
}
}
}
void ViewportControllerList::RegisterViewportContext(ViewportId viewport)
{
m_viewports.insert(viewport);
for (const auto& controllerInfo : m_controllers)
for (auto& controllerData : m_controllers)
{
controllerInfo.controller->RegisterViewportContext(viewport);
for (auto& controller : controllerData.second)
{
controller->RegisterViewportContext(viewport);
}
}
}
void ViewportControllerList::UnregisterViewportContext(ViewportId viewport)
{
m_viewports.erase(viewport);
for (const auto& controllerInfo : m_controllers)
for (auto& controllerData : m_controllers)
{
controllerInfo.controller->UnregisterViewportContext(viewport);
for (auto& controller : controllerData.second)
{
controller->UnregisterViewportContext(viewport);
}
}
}
void ViewportControllerList::SortControllers()
{
AZStd::sort(
m_controllers.begin(), m_controllers.end(),
[](const ViewportControllerData& d1, const ViewportControllerData& d2)
{
return d1.priority < d2.priority;
});
}
} //namespace AzFramework
@@ -14,62 +14,49 @@
#include <AzFramework/Viewport/ViewportControllerInterface.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
namespace AzFramework
{
//! A list of ViewportControllers that allows priority-ordered dispatch to the registered ViewportControllers.
//! A list of ViewportControllers that allows priority-ordered dispatch to controllers registered to it.
//! ViewportControllerList itself is-a controller, meaning it controllers can be contained in nested lists.
class ViewportControllerList final
: public ViewportControllerInterface
{
public:
//! Controller Priority determines when controllers receive input and update events.
//! Input channel events are received from highest to lowest priority order,
//! allowing high priority controllers to consume input events and stop their propagation.
//! Viewport update events are received from lowest to highest priority order,
//! allowing high priority controllers to be the last to update the viewport state.
enum class Priority : unsigned char {
Highest = 0,
High,
Normal,
Low,
Lowest
};
//! Adds a controller to this list at the specified priority.
//! This controller will be notified of all InputChannelEvents not consumed by a higher priority controller
//! via OnInputChannelEvent.
void Add(ViewportControllerPtr controller, Priority priority = Priority::Normal);
void Add(ViewportControllerPtr controller);
//! Removes a controller from this list.
void Remove(ViewportControllerPtr controller);
//! Updates the priority level for a controller.
void SetPriority(ViewportControllerPtr controller, Priority priority);
// ViewportControllerInterface overrides
//! Dispatches an InputChannelEvent to all controllers registered to this list until
//! either a controller returns true to consume the event in OnInputChannelEvent or the controller list is exhausted.
//! InputChannelEvents are sent to controllers in priority order (from the lowest priority value to the highest).
bool HandleInputChannelEvent(ViewportId viewport, const AzFramework::InputChannel& inputChannel) override;
bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override;
//! Dispatches an update tick to all controllers registered to this list.
//! This occurs in *reverse* priority order (i.e. from the highest priority value to the lowest) so that
//! controllers with the highest registration priority may override the transforms of the controllers with the
//! lowest registration priority.
void UpdateViewport(ViewportId viewport, FloatSeconds deltaTime, AZ::ScriptTimePoint time) override;
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
//! Registers a Viewport to this list.
//! All current and added controllers will be registered with this viewport.
void RegisterViewportContext(ViewportId viewport) override;
//! Unregisters a Viewport from this list and all associated controllers.
void UnregisterViewportContext(ViewportId viewport);
//! All ViewportControllerLists have a priority of Custom to ensure
//! that they receive events at all priorities from any parent controllers.
AzFramework::ViewportControllerPriority GetPriority() const { return ViewportControllerPriority::DispatchToAllPriorities; }
private:
void SortControllers();
bool DispatchInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event);
void DispatchUpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event);
struct ViewportControllerData
{
ViewportControllerPtr controller;
Priority priority = Priority::Normal;
};
AZStd::vector<ViewportControllerData> m_controllers;
AZStd::unordered_map<AzFramework::ViewportControllerPriority, AZStd::vector<ViewportControllerPtr>> m_controllers;
AZStd::unordered_set<ViewportId> m_viewports;
};
} //namespace AzFramework
@@ -34,8 +34,9 @@ namespace AzFramework
enum TypeFlags
{
TYPE_None = 0,
TYPE_Entity = 1 << 0,
TYPE_RPI_Cullable = 1 << 1
TYPE_Entity = 1 << 0, // All entities
TYPE_NetEntity = 1 << 1, // NetBound entities
TYPE_RPI_Cullable = 1 << 2 // Cullable by the render system
};
AZ::Aabb m_boundingVolume = AZ::Aabb::CreateNull();
@@ -82,6 +82,8 @@ set(FILES
CommandLine/CommandLine.h
CommandLine/CommandRegistrationBus.h
Debug/DebugCameraBus.h
Engine/Engine.cpp
Engine/Engine.h
Viewport/ViewportBus.h
Viewport/ViewportBus.cpp
Viewport/ViewportColors.h
@@ -262,10 +264,11 @@ set(FILES
Physics/ClassConverters.cpp
Physics/ClassConverters.h
Physics/MaterialBus.h
Physics/TouchBendingBus.h
Physics/WorldEventhandler.h
Physics/ScriptCanvasPhysicsUtils.h
Physics/ScriptCanvasPhysicsUtils.cpp
ProjectManager/ProjectManager.h
ProjectManager/ProjectManager.cpp
Render/GameIntersectorComponent.h
Render/GameIntersectorComponent.cpp
Render/GeometryIntersectionBus.h
@@ -273,6 +276,14 @@ set(FILES
Render/Intersector.cpp
Render/Intersector.h
Render/IntersectorInterface.h
Spawnable/Spawnable.cpp
Spawnable/Spawnable.h
Spawnable/SpawnableAssetHandler.h
Spawnable/SpawnableAssetHandler.cpp
Spawnable/SpawnableMetaData.cpp
Spawnable/SpawnableMetaData.h
Spawnable/SpawnableSystemComponent.h
Spawnable/SpawnableSystemComponent.cpp
Terrain/TerrainDataRequestBus.h
Terrain/TerrainDataRequestBus.cpp
Platform/PlatformDefaults.h