Integrating latest 47acbe8
This commit is contained in:
@@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user